refactor: migrate account-menu to react - implement functionality

- implement "merge local data"
- show "error reporting dialog"
- fix TS errors
- cleanup TODOs
This commit is contained in:
VardanHakobyan
2021-06-04 20:11:19 +04:00
parent 5349ec8550
commit f7262dc948

View File

@@ -8,6 +8,7 @@ import { isDesktopApplication, isSameDay, preventRefreshing } from '@/utils';
import { storage, StorageKey } from '@Services/localStorage';
import { disableErrorReporting, enableErrorReporting, errorReportingId } from '@Services/errorReporting';
import {
STRING_ACCOUNT_MENU_UNCHECK_MERGE,
STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_CHANGE,
STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_REMOVAL,
STRING_E2E_ENABLED,
@@ -27,8 +28,9 @@ import { BackupFile, ContentType } from '@node_modules/@standardnotes/snjs';
import { PasswordWizardType } from '@/types';
import { JSXInternal } from '@node_modules/preact/src/jsx';
import TargetedEvent = JSXInternal.TargetedEvent;
import { alertDialog } from '@Services/alertService';
import TargetedKeyboardEvent = JSXInternal.TargetedKeyboardEvent;
import TargetedMouseEvent = JSXInternal.TargetedMouseEvent;
import { alertDialog, confirmDialog } from '@Services/alertService';
import { RefObject } from 'react';
type Props = {
@@ -67,7 +69,6 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
const emailInputRef = useRef<HTMLInputElement>();
const passwordInputRef = useRef<HTMLInputElement>();
// TODO: Vardan `showLogin` and `showRegister` were in `formData` in Angular code, check whether I need to write similarly
const [showLogin, setShowLogin] = useState(false);
const [showRegister, setShowRegister] = useState(false);
const [showAdvanced, setShowAdvanced] = useState(false);
@@ -94,31 +95,24 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
const [selectedAutoLockInterval, setSelectedAutoLockInterval] = useState<unknown>(null);
const [isImportDataLoading, setIsImportDataLoading] = useState(false);
const [isErrorReportingEnabled, setIsErrorReportingEnabled] = useState(false);
const [appVersion, setAppVersion] = useState(''); // TODO: Vardan: figure out how to get `appVersion` similar to original code
const [appVersion, setAppVersion] = useState('');
const [hasPasscode, setHasPasscode] = useState(application.hasPasscode());
const [isBackupEncrypted, setIsBackupEncrypted] = useState(isEncryptionEnabled);
const [isSyncInProgress, setIsSyncInProgress] = useState(false);
const [protectionsDisabledUntil, setProtectionsDisabledUntil] = useState(getProtectionsDisabledUntil());
const user = application.getUser();
const reloadAutoLockInterval = useCallback(async () => {
const interval = await application.getAutolockService().getAutoLockInterval();
setSelectedAutoLockInterval(interval);
}, [application]);
const user = application.getUser();
const errorReportingIdValue = errorReportingId();
const canAddPasscode = !application.isEphemeralSession();
const keyStorageInfo = StringUtils.keyStorageInfo(application);
const passcodeAutoLockOptions = application.getAutolockService().getAutoLockIntervalOptions();
const showBetaWarning = appState.showBetaWarning;
/*
const displayRegistrationForm = () => {
console.log('display registration form!');
};
*/
const focusWithTimeout = (inputElementRef: RefObject<HTMLInputElement>) => {
// In case the ref element is not yet available at this moment,
// we call `focus()` after timeout.
@@ -142,26 +136,6 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
passwordInputRef.current.blur();
};
/*
// TODO: move to top
type FormData = {
email: string;
password: string;
passwordConfirmation: string;
showLogin: boolean;
showRegister: boolean;
showPasscodeForm: boolean;
isStrictSignin?: boolean;
isEphemeral: boolean;
shouldMergeLocal?: boolean;
url: string;
isAuthenticating: boolean;
status: string;
passcode: string;
passcodeConfirmation: string;
};*/
const login = async () => {
setStatus(STRING_GENERATING_LOGIN_KEYS);
setIsAuthenticating(true);
@@ -220,8 +194,11 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
}
};
const handleAuthFormSubmit = (event: TargetedEvent<HTMLFormElement> | TargetedMouseEvent<HTMLButtonElement>) => {
// TODO: If I don't need `submit` form at all, get rid of `onSubmit` and thus there will be no need to `preventDefault`
const handleAuthFormSubmit = (event:
TargetedEvent<HTMLFormElement> |
TargetedMouseEvent<HTMLButtonElement> |
TargetedKeyboardEvent<HTMLButtonElement>
) => {
event.preventDefault();
if (!email || !password) {
@@ -239,15 +216,13 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
const handleHostInputChange = (event: TargetedEvent<HTMLInputElement>) => {
const { value } = event.target as HTMLInputElement;
setServer(value);
setUrl(value);
application.setHost(value);
};
// const handleKeyPressKeyDown = (event: KeyboardEvent) => {
const handleKeyPressKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Enter') {
// TODO: fix TS error for `event`
handleAuthFormSubmit(event);
handleAuthFormSubmit(event as TargetedKeyboardEvent<HTMLButtonElement>);
}
};
@@ -266,8 +241,18 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
setPasswordConfirmation(value);
};
const handleMergeLocalData = () => {
console.log('handleMergeLocalData');
const handleMergeLocalData = async (event: TargetedEvent<HTMLInputElement>) => {
const { checked } = event.target as HTMLInputElement;
if (!checked) {
setShouldMergeLocal(checked);
const confirmResult = await confirmDialog({
text: STRING_ACCOUNT_MENU_UNCHECK_MERGE,
confirmButtonStyle: 'danger'
});
setShouldMergeLocal(!confirmResult);
}
};
const openPasswordWizard = () => {
@@ -320,7 +305,6 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
};
const submitPasscodeForm = async (event: TargetedEvent<HTMLFormElement> | TargetedMouseEvent<HTMLButtonElement>) => {
// TODO: If I don't need `submit` form at all, get rid of `onSubmit` and thus there will be no need to `preventDefault`
event.preventDefault();
if (passcode !== passcodeConfirmation) {
@@ -353,7 +337,6 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
refreshEncryptionStatus();
};
// TODO: Vardan: check whether this (and `handleConfirmPasscodeChange`) method is required in the end
const handlePasscodeChange = (event: TargetedEvent<HTMLInputElement>) => {
const { value } = event.target as HTMLInputElement;
setPasscode(value);
@@ -387,14 +370,10 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
appState.accountMenuReact.setSigningOut(true);
};
// TODO: Vardan: the name `changePasscodePressed` comes from original code; it is very similar to my `handlePasscodeChange`.
// Check if `handlePasscodeChange` is not required, remove it and rename `changePasscodePressed` to `handlePasscodeChange`
const changePasscodePressed = () => {
handleAddPassCode();
};
// TODO: Vardan: the name `removePasscodePressed` comes from original code;
// Check if I rename`changePasscodePressed` to `handlePasscodeChange`, also rename `removePasscodePressed` to `handleRemovePasscode`
const removePasscodePressed = async () => {
await preventRefreshing(
STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_REMOVAL,
@@ -494,17 +473,27 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
};
const openErrorReportingDialog = () => {
console.log('openErrorReportingDialog');
alertDialog({
title: 'Data sent during automatic error reporting',
text: `
We use <a target="_blank" rel="noreferrer" href="https://www.bugsnag.com/">Bugsnag</a>
to automatically report errors that occur while the app is running. See
<a target="_blank" rel="noreferrer" href="https://docs.bugsnag.com/platforms/javascript/#sending-diagnostic-data">
this article, paragraph 'Browser' under 'Sending diagnostic data',
</a>
to see what data is included in error reports.
<br><br>
Error reports never include IP addresses and are fully
anonymized. We use error reports to be alerted when something in our
code is causing unexpected errors and crashes in your application
experience.
`
});
};
// TODO: check whether this works fine (e.g. remove all tags and notes and then add one and check whether UI behaves appropriately)
const notesAndTagsCount = application.getItems([ContentType.Note, ContentType.Tag]).length;
const hasProtections = application.hasProtectionSources();
// TODO: Vardan: this is as per `this.autorun` from `$onInit`, check whether it works
// I'm mostly concerned about having dependency, since I think it is running only once in original code
// (I suppose it runs here only once, too. But need to recheck)
useEffect(() => {
setSyncError(appState.sync.errorMessage);
setIsSyncInProgress(appState.sync.inProgress);
@@ -515,11 +504,6 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
}, []);
useEffect(() => {
// TODO: in original `AccountMenu`, the `appVersion` is available in constructor (the `window.electronAppVersion` is `undefined`).
// But I can't find where `appVersion` is passed to AccountMenu's constructor... The only place I found is `app.ts`, where
// it sets constant `appVersion` from `bridge.appVersion` - maybe constructor takes that value from there?
// Ask someone to explain that part.
// Here I just take the version from `application.bridge.appVersion`, as it is done in `app.ts`.
setAppVersion(`v${((window as any).electronAppVersion || application.bridge.appVersion)}`);
}, [appVersion, application.bridge.appVersion]);
@@ -534,7 +518,7 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
useEffect(() => {
const host = application.getHost();
setServer(host);
setUrl(host); // TODO: Vardan: maybe `url` is not needed at all, recheck
setUrl(host);
}, [application]);
useEffect(() => {
@@ -590,7 +574,6 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
</div>
<form className='sk-panel-form' onSubmit={handleAuthFormSubmit} noValidate>
<div className='sk-panel-section'>
{/* TODO: Vardan: there are `should-focus` and `sn-autofocus`, implement them */}
<input className='sk-input contrast'
name='email'
type='email'
@@ -707,12 +690,12 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
</div>
</label>
{notesAndTagsCount > 0 && (
<label className='sk-panel-row.justify-left'>
<label className='sk-panel-row justify-left'>
<div className='sk-horizontal-group tight'>
<input
type='checkbox'
onChange={handleMergeLocalData}
checked={shouldMergeLocal}
onChange={handleMergeLocalData}
/>
<p className='sk-p'>Merge local data ({notesAndTagsCount}) notes and tags</p>
</div>