refactor: migrate account-menu to react - implement functionality

- implement download backup
- implement import backup
- add `useCallback` to function that is used in `useEffect`, so that its reference isn't changed on every render
This commit is contained in:
VardanHakobyan
2021-06-03 20:04:00 +04:00
parent da5f25e809
commit 7a05a448c3

View File

@@ -14,12 +14,16 @@ import {
STRING_ENC_NOT_ENABLED, STRING_ENC_NOT_ENABLED,
STRING_GENERATING_LOGIN_KEYS, STRING_GENERATING_LOGIN_KEYS,
STRING_GENERATING_REGISTER_KEYS, STRING_GENERATING_REGISTER_KEYS,
STRING_IMPORT_SUCCESS,
STRING_INVALID_IMPORT_FILE,
STRING_LOCAL_ENC_ENABLED, STRING_LOCAL_ENC_ENABLED,
STRING_NON_MATCHING_PASSCODES, STRING_NON_MATCHING_PASSCODES,
STRING_NON_MATCHING_PASSWORDS, STRING_NON_MATCHING_PASSWORDS,
STRING_UNSUPPORTED_BACKUP_FILE_VERSION,
StringImportError,
StringUtils StringUtils
} from '@/strings'; } from '@/strings';
import { ContentType } from '@node_modules/@standardnotes/snjs'; import { BackupFile, ContentType } from '@node_modules/@standardnotes/snjs';
import { PasswordWizardType } from '@/types'; import { PasswordWizardType } from '@/types';
import { JSXInternal } from '@node_modules/preact/src/jsx'; import { JSXInternal } from '@node_modules/preact/src/jsx';
import TargetedEvent = JSXInternal.TargetedEvent; import TargetedEvent = JSXInternal.TargetedEvent;
@@ -88,7 +92,7 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
const [url, setUrl] = useState<string | undefined>(undefined); const [url, setUrl] = useState<string | undefined>(undefined);
const [showPasscodeForm, setShowPasscodeForm] = useState(false); const [showPasscodeForm, setShowPasscodeForm] = useState(false);
const [selectedAutoLockInterval, setSelectedAutoLockInterval] = useState<unknown>(null); const [selectedAutoLockInterval, setSelectedAutoLockInterval] = useState<unknown>(null);
const [isLoading, setIsLoading] = useState<unknown>(false); const [isImportDataLoading, setIsImportDataLoading] = useState(false);
const [isErrorReportingEnabled, setIsErrorReportingEnabled] = 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(''); // TODO: Vardan: figure out how to get `appVersion` similar to original code
const [hasPasscode, setHasPasscode] = useState(application.hasPasscode()); const [hasPasscode, setHasPasscode] = useState(application.hasPasscode());
@@ -138,27 +142,27 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
passwordInputRef.current.blur(); passwordInputRef.current.blur();
}; };
/* /*
// TODO: move to top // TODO: move to top
type FormData = { type FormData = {
email: string; email: string;
password: string; password: string;
passwordConfirmation: string; passwordConfirmation: string;
showLogin: boolean; showLogin: boolean;
showRegister: boolean; showRegister: boolean;
showPasscodeForm: boolean; showPasscodeForm: boolean;
isStrictSignin?: boolean; isStrictSignin?: boolean;
isEphemeral: boolean; isEphemeral: boolean;
shouldMergeLocal?: boolean; shouldMergeLocal?: boolean;
url: string; url: string;
isAuthenticating: boolean; isAuthenticating: boolean;
status: string; status: string;
passcode: string; passcode: string;
passcodeConfirmation: string; passcodeConfirmation: string;
};*/ };*/
const login = async() => { const login = async () => {
setStatus(STRING_GENERATING_LOGIN_KEYS); setStatus(STRING_GENERATING_LOGIN_KEYS);
setIsAuthenticating(true); setIsAuthenticating(true);
@@ -287,7 +291,7 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
setProtectionsDisabledUntil(getProtectionsDisabledUntil()); setProtectionsDisabledUntil(getProtectionsDisabledUntil());
}; };
const refreshEncryptionStatus = () => { const refreshEncryptionStatus = useCallback(() => {
const hasUser = application.hasAccount(); const hasUser = application.hasAccount();
const hasPasscode = application.hasPasscode(); const hasPasscode = application.hasPasscode();
@@ -304,7 +308,7 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
setEncryptionStatusString(newEncryptionStatusString); setEncryptionStatusString(newEncryptionStatusString);
setIsEncryptionEnabled(encryptionEnabled); setIsEncryptionEnabled(encryptionEnabled);
setIsBackupEncrypted(encryptionEnabled); setIsBackupEncrypted(encryptionEnabled);
}; }, [application]);
const handleAddPassCode = () => { const handleAddPassCode = () => {
setShowPasscodeForm(true); setShowPasscodeForm(true);
@@ -408,11 +412,74 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
}; };
const downloadDataArchive = () => { const downloadDataArchive = () => {
console.log('downloadDataArchive'); application.getArchiveService().downloadBackup(isBackupEncrypted);
}; };
const importFileSelected = () => {
console.log('importFileSelected'); const readFile = async (file: File): Promise<any> => {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
try {
const data = JSON.parse(e.target!.result as string);
resolve(data);
} catch (e) {
application.alertService.alert(STRING_INVALID_IMPORT_FILE);
}
};
reader.readAsText(file);
});
};
const performImport = async (data: BackupFile) => {
setIsImportDataLoading(true);
const result = await application.importData(data);
setIsImportDataLoading(false);
if (!result) {
return;
} else if ('error' in result) {
void alertDialog({
text: result.error,
});
} else if (result.errorCount) {
void alertDialog({
text: StringImportError(result.errorCount),
});
} else {
void alertDialog({
text: STRING_IMPORT_SUCCESS,
});
}
};
const importFileSelected = async (event: TargetedEvent<HTMLInputElement, Event>) => {
const { files } = (event.target as HTMLInputElement);
if (!files) {
return;
}
const file = files[0];
const data = await readFile(file);
if (!data) {
return;
}
if (data.version || data.auth_params || data.keyParams) {
const version =
data.version || data.keyParams?.version || data.auth_params?.version;
if (
application.protocolService.supportedVersions().includes(version)
) {
await performImport(data);
} else {
setIsImportDataLoading(false);
void alertDialog({ text: STRING_UNSUPPORTED_BACKUP_FILE_VERSION });
}
} else {
await performImport(data);
}
}; };
const toggleErrorReportingEnabled = () => { const toggleErrorReportingEnabled = () => {
@@ -826,31 +893,30 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
</> </>
)} )}
</div> </div>
{!isLoading && ( {!isImportDataLoading && (
<div className='sk-panel-section'> <div className='sk-panel-section'>
<div className='sk-panel-section-title'>Data Backups</div> <div className='sk-panel-section-title'>Data Backups</div>
<div className='sk-p'>Download a backup of all your data.</div> <div className='sk-p'>Download a backup of all your data.</div>
{isEncryptionEnabled && ( {isEncryptionEnabled && (
<form className='sk-panel-form sk-panel-row'> <form className='sk-panel-form sk-panel-row'>
<div className='sk-input-group' /> <div className='sk-input-group'>
<label className='sk-horizontal-group tight'> <label className='sk-horizontal-group tight'>
<input <input
type='radio' type='radio'
onChange={() => setIsBackupEncrypted(true)} onChange={() => setIsBackupEncrypted(true)}
value='true' checked={isBackupEncrypted}
checked={isBackupEncrypted} />
/> <p className='sk-p'>Encrypted</p>
<p className='sk-p'>Encrypted</p> </label>
</label> <label className='sk-horizontal-group tight'>
<label className='sk-horizontal-group tight'> <input
<input type='radio'
type='radio' onChange={() => setIsBackupEncrypted(false)}
onChange={() => setIsBackupEncrypted(false)} checked={!isBackupEncrypted}
value='false' />
checked={!isBackupEncrypted} <p className='sk-p'>Decrypted</p>
/> </label>
<p className='sk-p'>Decrypted</p> </div>
</label>
</form> </form>
)} )}
<div className='sk-panel-row' /> <div className='sk-panel-row' />
@@ -872,7 +938,7 @@ const AccountMenu = observer(({ application, appState, closeAccountMenu }: Props
</p> </p>
)} )}
<div className='sk-panel-row' /> <div className='sk-panel-row' />
{isLoading && ( {isImportDataLoading && (
<div className='sk-spinner small info' /> <div className='sk-spinner small info' />
)} )}
</div> </div>