refactor: migrate account-menu to react - implement functionality

- link the new React component to the app's store
- setup correct initial values
- small fixes
This commit is contained in:
VardanHakobyan
2021-05-28 19:25:44 +04:00
parent e18f410fd2
commit 1194c73c49
3 changed files with 360 additions and 307 deletions

View File

@@ -2,13 +2,13 @@ import { observer } from 'mobx-react-lite';
import { toDirective } from '@/components/utils'; import { toDirective } from '@/components/utils';
import { AppState } from '@/ui_models/app_state'; import { AppState } from '@/ui_models/app_state';
import { WebApplication } from '@/ui_models/application'; import { WebApplication } from '@/ui_models/application';
import { useEffect, useRef, useState } from 'preact/hooks'; import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import { User } from '@standardnotes/snjs/dist/@types/services/api/responses'; import { isDesktopApplication, isSameDay } from '@/utils';
import { isDesktopApplication } from '@/utils';
import { storage, StorageKey } from '@Services/localStorage'; import { storage, StorageKey } from '@Services/localStorage';
import { disableErrorReporting, enableErrorReporting, errorReportingId } from '@Services/errorReporting'; import { disableErrorReporting, enableErrorReporting, errorReportingId } from '@Services/errorReporting';
import { ConfirmSignoutDirective } from '@/components/ConfirmSignoutModal'; import { STRING_E2E_ENABLED, STRING_ENC_NOT_ENABLED, STRING_LOCAL_ENC_ENABLED, StringUtils } from '@/strings';
import { ContentType } from '@node_modules/@standardnotes/snjs';
// eslint-disable-next-line @typescript-eslint/no-empty-interface // eslint-disable-next-line @typescript-eslint/no-empty-interface
// interface Props {} // TODO: Vardan: implement props and remove `eslint-disable` // interface Props {} // TODO: Vardan: implement props and remove `eslint-disable`
@@ -20,11 +20,33 @@ type Props = {
// const HistoryMenu = observer((props: Props) => { // const HistoryMenu = observer((props: Props) => {
// const AccountMenu = observer((props) => { // const AccountMenu = observer((props) => {
const AccountMenu = observer(({ appState, application }: Props) => { const AccountMenu = observer(({ appState, application }: Props) => {
const getProtectionsDisabledUntil = (): string | null => {
const protectionExpiry = application.getProtectionSessionExpiryDate();
const now = new Date();
if (protectionExpiry > now) {
let f: Intl.DateTimeFormat;
if (isSameDay(protectionExpiry, now)) {
f = new Intl.DateTimeFormat(undefined, {
hour: 'numeric',
minute: 'numeric'
});
} else {
f = new Intl.DateTimeFormat(undefined, {
weekday: 'long',
day: 'numeric',
month: 'short',
hour: 'numeric',
minute: 'numeric'
});
}
return f.format(protectionExpiry);
}
return null;
};
const passcodeInput = useRef<HTMLInputElement>(); // TODO: implement what is missing for `passcodeInput`, e.g. - autofocus
const passcodeInput = useRef<HTMLInputElement>();
// const { user, formData } = application;
// const [user, setUser] = useState(null); // TODO: Vardan: set correct type and initial value
const [user, setUser] = useState<User | undefined>(undefined); // TODO: Vardan: set correct type and initial value
// TODO: Vardan `showLogin` and `showRegister` were in `formData` in Angular code, check whether I need to write similarly // TODO: Vardan `showLogin` and `showRegister` were in `formData` in Angular code, check whether I need to write similarly
const [showLogin, setShowLogin] = useState(false); const [showLogin, setShowLogin] = useState(false);
const [showRegister, setShowRegister] = useState(false); const [showRegister, setShowRegister] = useState(false);
@@ -33,32 +55,41 @@ const AccountMenu = observer(({ appState, application }: Props) => {
const [password, setPassword] = useState<string | undefined>(undefined); const [password, setPassword] = useState<string | undefined>(undefined);
const [passwordConfirmation, setPasswordConfirmation] = useState<string | undefined>(undefined); const [passwordConfirmation, setPasswordConfirmation] = useState<string | undefined>(undefined);
const [status, setStatus] = useState(''); const [status, setStatus] = useState('');
const [syncError, setSyncError] = useState(''); const [syncError, setSyncError] = useState<string | undefined>(undefined);
const [server, setServer] = useState('');
const [notesAndTagsCount, setNotesAndTagsCount] = useState(0); const [server, setServer] = useState<string | undefined>(undefined);
const [isEncryptionEnabled, setIsEncryptionEnabled] = useState(false);
const [encryptionStatusString, setEncryptionStatusString] = useState('');
const [hasProtections, setHasProtections] = useState(false);
const [protectionsDisabledUntil, setProtectionsDisabledUntil] = useState<string | null>(null);
const [hasPasscode, setHasPasscode] = useState(false);
const [canAddPasscode, setCanAddPasscode] = useState(false);
const [showPasscodeForm, setShowPasscodeForm] = useState(false); const [showPasscodeForm, setShowPasscodeForm] = useState(false);
const [keyStorageInfo, setKeyStorageInfo] = useState<string | null>(null);
const [passcodeAutoLockOptions, setPasscodeAutoLockOptions] = useState<{value: number; label: string}[]>([]);
const [selectedAutoLockInterval, setSelectedAutoLockInterval] = useState<unknown>(null); const [selectedAutoLockInterval, setSelectedAutoLockInterval] = useState<unknown>(null);
const [isLoading, setIsLoading] = useState<unknown>(false); const [isLoading, setIsLoading] = useState<unknown>(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 [showBetaWarning, setShowBetaWarning] = useState(false);
const user = application.getUser();
const hasUser = application.hasAccount();
const hasPasscode = application.hasPasscode();
const isEncryptionEnabled = hasUser || hasPasscode;
const encryptionStatusString = hasUser
? STRING_E2E_ENABLED : hasPasscode
? STRING_LOCAL_ENC_ENABLED : STRING_ENC_NOT_ENABLED;
// TODO: Vardan: in original code initial value of `backupEncrypted` is `hasUser || hasPasscode` - // TODO: Vardan: in original code initial value of `backupEncrypted` is `hasUser || hasPasscode` -
// once I have those values here, set them as initial value // once I have those values here, set them as initial value
const [isBackupEncrypted, setIsBackupEncrypted] = useState(false); const [isBackupEncrypted, setIsBackupEncrypted] = useState(isEncryptionEnabled);
const [isSyncInProgress, setIsSyncInProgress] = useState(false); const [isSyncInProgress, setIsSyncInProgress] = useState(false);
const reloadAutoLockInterval = useCallback(async () => {
const interval = await application.getAutolockService().getAutoLockInterval();
setSelectedAutoLockInterval(interval);
}, [application]);
const errorReportingIdValue = errorReportingId(); const errorReportingIdValue = errorReportingId();
const protectionsDisabledUntil = getProtectionsDisabledUntil();
const canAddPasscode = !application.isEphemeralSession();
const keyStorageInfo = StringUtils.keyStorageInfo(application);
const passcodeAutoLockOptions = application.getAutolockService().getAutoLockIntervalOptions();
const showBetaWarning = appState.showBetaWarning;
/* /*
const displayRegistrationForm = () => { const displayRegistrationForm = () => {
@@ -110,8 +141,8 @@ const AccountMenu = observer(({ appState, application }: Props) => {
}; };
const getEncryptionStatusForNotes = () => { const getEncryptionStatusForNotes = () => {
console.log('implement `getEncryptionStatusForNotes`'); const length = notesAndTagsCount;
return ''; return `${length}/${length} notes and tags encrypted`;
}; };
const enableProtections = () => { const enableProtections = () => {
@@ -192,23 +223,45 @@ const AccountMenu = observer(({ appState, application }: Props) => {
console.log('close this'); console.log('close this');
}; };
// 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);
}, [appState.sync.errorMessage, appState.sync.inProgress]);
useEffect(() => { useEffect(() => {
// TODO: Vardan: get the real count setIsErrorReportingEnabled(storage.get(StorageKey.DisableErrorReporting) === false);
setNotesAndTagsCount(1);
}, []); }, []);
useEffect(() => { useEffect(() => {
setIsErrorReportingEnabled( storage.get(StorageKey.DisableErrorReporting) === false); // 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]);
useEffect(() => {
reloadAutoLockInterval();
}, [reloadAutoLockInterval]);
useEffect(() => {
setIsErrorReportingEnabled(storage.get(StorageKey.DisableErrorReporting) === false);
}, []); }, []);
/*
useEffect(() => { useEffect(() => {
setAppVersion(`v${((window as any).electronAppVersion || appVersion)}`); const host = application.getHost();
}, [appVersion]); setServer(host);
*/ }, [application]);
/* /*
const { searchOptions } = appState; const { searchOptions } = appState;
@@ -222,7 +275,7 @@ const AccountMenu = observer(({ appState, application }: Props) => {
return ( return (
<div style={{ <div style={{
top: '-250px', top: '-70px',
right: '-450px', right: '-450px',
width: '100px', width: '100px',
height: '100px', height: '100px',
@@ -257,7 +310,7 @@ const AccountMenu = observer(({ appState, application }: Props) => {
</button> </button>
</div> </div>
<div className='sk-panel-row sk-p'> <div className='sk-panel-row sk-p'>
Standard Notes is free on every platform, and comes<br /> Standard Notes is free on every platform, and comes
standard with sync and encryption. standard with sync and encryption.
</div> </div>
</div> </div>
@@ -275,7 +328,8 @@ const AccountMenu = observer(({ appState, application }: Props) => {
type='email' type='email'
placeholder='Email' placeholder='Email'
required required
spellCheck={false} /> spellCheck={false}
/>
<input className='sk-input contrast' <input className='sk-input contrast'
name='password' name='password'
type='password' type='password'
@@ -297,34 +351,37 @@ const AccountMenu = observer(({ appState, application }: Props) => {
onChange={handlePasswordConfirmationChange} onChange={handlePasswordConfirmationChange}
/>} />}
<div className='sk-panel-row' /> <div className='sk-panel-row' />
<a className="sk-panel-row sk-bold" onClick={() => {setShowAdvanced(showAdvanced => !showAdvanced)}}> <a className='sk-panel-row sk-bold' onClick={() => {
setShowAdvanced(showAdvanced => !showAdvanced);
}}>
Advanced Options Advanced Options
</a> </a>
</div> </div>
{showAdvanced && ( {showAdvanced && (
<div className="sk-notification unpadded contrast advanced-options sk-panel-row"> <div className='sk-notification unpadded contrast advanced-options sk-panel-row'>
<div className="sk-panel-column stretch"> <div className='sk-panel-column stretch'>
<div className="sk-notification-title sk-panel-row padded-row"> <div className='sk-notification-title sk-panel-row padded-row'>
Advanced Options Advanced Options
</div> </div>
<div className="bordered-row padded-row"> <div className='bordered-row padded-row'>
<label className="sk-label">Sync Server Domain</label> <label className='sk-label'>Sync Server Domain</label>
<input className="sk-input sk-base" <input className='sk-input sk-base'
name="server" name='server'
placeholder="Server URL" placeholder='Server URL'
onChange={handleHostInputChange} onChange={handleHostInputChange}
value={server}
required required
/> />
</div> </div>
{showLogin && ( {showLogin && (
<label className="sk-label padded-row sk-panel-row justify-left"> <label className='sk-label padded-row sk-panel-row justify-left'>
<div className="sk-horizontal-group tight"> <div className='sk-horizontal-group tight'>
<input className="sk-input" type="checkbox" onChange={handleChangeStrictSignIn} /> <input className='sk-input' type='checkbox' onChange={handleChangeStrictSignIn} />
<p className="sk-p">Use strict sign in</p> <p className='sk-p'>Use strict sign in</p>
<span> <span>
<a className="info" <a className='info'
href="https://standardnotes.org/help/security" rel="noopener" href='https://standardnotes.org/help/security' rel='noopener'
target="_blank" target='_blank'
> >
(Learn more) (Learn more)
</a> </a>
@@ -336,44 +393,45 @@ const AccountMenu = observer(({ appState, application }: Props) => {
</div> </div>
)} )}
{!isAuthenticating && ( {!isAuthenticating && (
<div className="sk-panel-section.form-submit"> <div className='sk-panel-section.form-submit'>
<button className="sn-button info text-base py-3 text-center" type="submit" disabled={isAuthenticating}> <button className='sn-button info text-base py-3 text-center' type='submit'
{showLogin ? "Sign In" : "Register"} disabled={isAuthenticating}>
{showLogin ? 'Sign In' : 'Register'}
</button> </button>
</div> </div>
)} )}
{showRegister && ( {showRegister && (
<div className="sk-notification neutral"> <div className='sk-notification neutral'>
<div className="sk-notification-title">No Password Reset.</div> <div className='sk-notification-title'>No Password Reset.</div>
<div className="sk-notification-text"> <div className='sk-notification-text'>
Because your notes are encrypted using your password,<br /> Because your notes are encrypted using your password,
Standard Notes does not have a password reset option.<br /> Standard Notes does not have a password reset option.
You cannot forget your password. You cannot forget your password.
</div> </div>
</div> </div>
)} )}
{status && ( {status && (
<div className="sk-panel-section no-bottom-pad"> <div className='sk-panel-section no-bottom-pad'>
<div className="sk-horizontal-group"> <div className='sk-horizontal-group'>
<div className="sk-spinner small neutral"> <div className='sk-spinner small neutral'>
<div className="sk-label">{status}</div> <div className='sk-label'>{status}</div>
</div> </div>
</div> </div>
</div> </div>
)} )}
{!isAuthenticating && ( {!isAuthenticating && (
<div className="sk-panel-section no-bottom-pad"> <div className='sk-panel-section no-bottom-pad'>
<label className="sk-panel-row justify-left"> <label className='sk-panel-row justify-left'>
<div className="sk-horizontal-group tight"> <div className='sk-horizontal-group tight'>
<input type="checkbox" onChange={handleChangeEphemeral} /> <input type='checkbox' onChange={handleChangeEphemeral} />
<p className="sk-p">Stay signed in</p> <p className='sk-p'>Stay signed in</p>
</div> </div>
</label> </label>
{notesAndTagsCount > 0 && ( {notesAndTagsCount > 0 && (
<label className="sk-panel-row.justify-left"> <label className='sk-panel-row.justify-left'>
<div className="sk-horizontal-group tight"> <div className='sk-horizontal-group tight'>
<input type="checkbox" onChange={handleMergeLocalData} /> <input type='checkbox' onChange={handleMergeLocalData} />
<p className="sk-p">Merge local data ({notesAndTagsCount}) notes and tags</p> <p className='sk-p'>Merge local data ({notesAndTagsCount}) notes and tags</p>
</div> </div>
</label> </label>
)} )}
@@ -385,18 +443,16 @@ const AccountMenu = observer(({ appState, application }: Props) => {
{!showLogin && !showRegister && ( {!showLogin && !showRegister && (
<div> <div>
{user && ( {user && (
<> <div className='sk-panel-section'>
<div className="sk-panel-section">
{syncError && ( {syncError && (
<> <div className='sk-notification danger'>
<div className="sk-notification danger"> <div className='sk-notification-title'>Sync Unreachable</div>
<div className="sk-notification-title">Sync Unreachable</div> <div className='sk-notification-text'>
<div className="sk-notification-text"> Hmm...we can't seem to sync your account.
Hmm...we can't seem to sync your account.<br />
The reason: {syncError} The reason: {syncError}
</div> </div>
<a <a
className="sk-a info-contrast sk-bold sk-panel-row" className='sk-a info-contrast sk-bold sk-panel-row'
href='https://standardnotes.org/help' href='https://standardnotes.org/help'
rel='noopener' rel='noopener'
target='_blank' target='_blank'
@@ -404,82 +460,82 @@ const AccountMenu = observer(({ appState, application }: Props) => {
Need help? Need help?
</a> </a>
</div> </div>
<div className="sk-panel-row"> )}
<div className="sk-panel-column"> <div className='sk-panel-row'>
<div className="sk-h1 sk-bold wrap"> <div className='sk-panel-column'>
<div className='sk-h1 sk-bold wrap'>
{user.email} {user.email}
</div> </div>
<div className="sk-subtitle neutral"> <div className='sk-subtitle neutral'>
{server} {server}
</div> </div>
</div> </div>
</div> </div>
<div className="sk-panel-row" /> <div className='sk-panel-row' />
<a className="sk-a info sk-panel-row condensed" onClick={openPasswordWizard}> <a className='sk-a info sk-panel-row condensed' onClick={openPasswordWizard}>
Change Password Change Password
(Vardan) reached here
</a> </a>
<a className="sk-a info sk-panel-row condensed" onClick={openSessionsModal}> <a className='sk-a info sk-panel-row condensed' onClick={openSessionsModal}>
Manage Sessions Manage Sessions
</a> </a>
</>
)}
</div> </div>
<div className="sk-panel-section"> )}
<div className="sk-panel-section-title">
<div className='sk-panel-section'>
<div className='sk-panel-section-title'>
Encryption Encryption
</div> </div>
{isEncryptionEnabled && ( {isEncryptionEnabled && (
<div className="sk-panel-section-subtitle info"> <div className='sk-panel-section-subtitle info'>
{getEncryptionStatusForNotes()} {getEncryptionStatusForNotes()}
</div> </div>
)} )}
<p className="sk-p"> <p className='sk-p'>
{encryptionStatusString} {encryptionStatusString}
</p> </p>
</div> </div>
{hasProtections && ( {hasProtections && (
<div className="sk-panel-section"> <div className='sk-panel-section'>
<div className="sk-panel-section-title">Protections</div> <div className='sk-panel-section-title'>Protections</div>
{protectionsDisabledUntil && ( {protectionsDisabledUntil && (
<div className="sk-panel-section-subtitle info"> <div className='sk-panel-section-subtitle info'>
Protections are disabled until {protectionsDisabledUntil} Protections are disabled until {protectionsDisabledUntil}
</div> </div>
)} )}
{!protectionsDisabledUntil && ( {!protectionsDisabledUntil && (
<div className="sk-panel-section-subtitle info"> <div className='sk-panel-section-subtitle info'>
Protections are enabled Protections are enabled
</div> </div>
)} )}
<p className="sk-p"> <p className='sk-p'>
Actions like viewing protected notes, exporting decrypted backups,<br /> Actions like viewing protected notes, exporting decrypted backups,
or revoking an active session, require additional authentication<br /> or revoking an active session, require additional authentication
like entering your account password or application passcode. like entering your account password or application passcode.
</p> </p>
{protectionsDisabledUntil && ( {protectionsDisabledUntil && (
<div className="sk-panel-row"> <div className='sk-panel-row'>
<button className="sn-button small.info" onClick={enableProtections}> <button className='sn-button small.info' onClick={enableProtections}>
Enable protections Enable protections
</button> </button>
</div> </div>
)} )}
</div> </div>
)} )}
<div className="sk-panel-section"> <div className='sk-panel-section'>
<div className="sk-panel-section-title">Passcode Lock</div> <div className='sk-panel-section-title'>Passcode Lock</div>
{!hasPasscode && ( {!hasPasscode && (
<div> <div>
{canAddPasscode && ( {canAddPasscode && (
<> <>
{showPasscodeForm && ( {!showPasscodeForm && (
<div className="sk-panel-row"> <div className='sk-panel-row'>
<button className="sn-button.small.info" onClick={handleAddPassCode}> <button className='sn-button small info' onClick={handleAddPassCode}>
Add Passcode Add Passcode
</button> </button>
</div> </div>
)} )}
<p className="sk-p"> <p className='sk-p'>
Add a passcode to lock the application and<br /> Add a passcode to lock the application and
encrypt on-device key storage. encrypt on-device key storage.
</p> </p>
{keyStorageInfo && ( {keyStorageInfo && (
@@ -488,51 +544,51 @@ const AccountMenu = observer(({ appState, application }: Props) => {
</> </>
)} )}
{!canAddPasscode && ( {!canAddPasscode && (
<p className="sk-p"> <p className='sk-p'>
Adding a passcode is not supported in temporary sessions. Please sign<br /> Adding a passcode is not supported in temporary sessions. Please sign
out, then sign back in with the "Stay signed in" option checked. out, then sign back in with the "Stay signed in" option checked.
</p> </p>
)} )}
</div> </div>
)} )}
{showPasscodeForm && ( {showPasscodeForm && (
<form className="sk-panel-form" onSubmit={submitPasscodeForm}> <form className='sk-panel-form' onSubmit={submitPasscodeForm}>
<div className="sk-panel-row" /> <div className='sk-panel-row' />
{/* TODO: Vardan: there are `should-focus` and `sn-autofocus`, implement them */} {/* TODO: Vardan: there are `should-focus` and `sn-autofocus`, implement them */}
<input <input
className="sk-input contrast" className='sk-input contrast'
type="password" type='password'
ref={passcodeInput} ref={passcodeInput}
onChange={handlePasscodeChange} onChange={handlePasscodeChange}
placeholder="Passcode" placeholder='Passcode'
/> />
<input <input
className="sk-input contrast" className='sk-input contrast'
type="password" type='password'
onChange={handleConfirmPasscodeChange} onChange={handleConfirmPasscodeChange}
placeholder="Confirm Passcode" placeholder='Confirm Passcode'
/> />
<button className="sn-button small info mt-2" onClick={submitPasscodeForm}> <button className='sn-button small info mt-2' onClick={submitPasscodeForm}>
Set Passcode Set Passcode
</button> </button>
<button className="sn-button small outlined ml-2" onClick={() => setShowPasscodeForm(false)}> <button className='sn-button small outlined ml-2' onClick={() => setShowPasscodeForm(false)}>
Cancel Cancel
</button> </button>
</form> </form>
)} )}
{hasPasscode && !showPasscodeForm && ( {hasPasscode && !showPasscodeForm && (
<> <>
<div className="sk-panel-section-subtitle info">Passcode lock is enabled</div> <div className='sk-panel-section-subtitle info'>Passcode lock is enabled</div>
<div className="sk-notification contrast"> <div className='sk-notification contrast'>
<div className="sk-notification-title">Options</div> <div className='sk-notification-title'>Options</div>
<div className="sk-notification-text"> <div className='sk-notification-text'>
<div className="sk-panel-row"> <div className='sk-panel-row'>
<div className="sk-horizontal-group"> <div className='sk-horizontal-group'>
<div className="sk-h4 sk-bold">Autolock</div> <div className='sk-h4 sk-bold'>Autolock</div>
{passcodeAutoLockOptions.map(option => { {passcodeAutoLockOptions.map(option => {
return ( return (
<a <a
className={option.value === selectedAutoLockInterval ? "boxed" : ""} className={option.value === selectedAutoLockInterval ? 'boxed' : ''}
onClick={() => selectAutoLockInterval(option.value)}> onClick={() => selectAutoLockInterval(option.value)}>
{option.label} {option.label}
</a> </a>
@@ -540,12 +596,12 @@ const AccountMenu = observer(({ appState, application }: Props) => {
})} })}
</div> </div>
</div> </div>
<div className="sk-p">The autolock timer begins when the window or tab loses focus.</div> <div className='sk-p'>The autolock timer begins when the window or tab loses focus.</div>
<div className="sk-panel-row" /> <div className='sk-panel-row' />
<a className="sk-a info sk-panel-row condensed" onClick={changePasscodePressed}> <a className='sk-a info sk-panel-row condensed' onClick={changePasscodePressed}>
Change Passcode Change Passcode
</a> </a>
<a className="sk-a danger sk-panel-row condensed" onClick={removePasscodePressed}> <a className='sk-a danger sk-panel-row condensed' onClick={removePasscodePressed}>
Remove Passcode Remove Passcode
</a> </a>
</div> </div>
@@ -554,112 +610,106 @@ const AccountMenu = observer(({ appState, application }: Props) => {
)} )}
</div> </div>
{!isLoading && ( {!isLoading && (
<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" 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)}
value="false" value='false'
checked={!isBackupEncrypted} checked={!isBackupEncrypted}
/> />
<p className="sk-p">Decrypted</p> <p className='sk-p'>Decrypted</p>
</label> </label>
</form> </form>
)} )}
<div className="sk-panel-row" /> <div className='sk-panel-row' />
<div className="flex"> <div className='flex'>
<button className="sn-button small info" onClick={downloadDataArchive}>Download Backup</button> <button className='sn-button small info' onClick={downloadDataArchive}>Download Backup</button>
<label className="sn-button small flex items-center info ml-2"> <label className='sn-button small flex items-center info ml-2'>
<input <input
type="file" type='file'
onChange={importFileSelected} onChange={importFileSelected}
style={{display: "none"}} style={{ display: 'none' }}
/> />
Import Backup Import Backup
</label> </label>
</div> </div>
{isDesktopApplication && ( {isDesktopApplication() && (
<p className="mt-5"> <p className='mt-5'>
Backups are automatically created on desktop and can be managed<br /> Backups are automatically created on desktop and can be managed
via the "Backups" top-level menu. via the "Backups" top-level menu.
</p> </p>
)} )}
<div className="sk-panel-row" /> <div className='sk-panel-row' />
{isLoading && ( {isLoading && (
<div className="sk-spinner small info" /> <div className='sk-spinner small info' />
)} )}
</div> </div>
)} )}
<div className="sk-panel-section"> <div className='sk-panel-section'>
<div className="sk-panel-section-title">Error Reporting</div> <div className='sk-panel-section-title'>Error Reporting</div>
<div className="sk-panel-section-subtitle info"> <div className='sk-panel-section-subtitle info'>
Automatic error reporting is {isErrorReportingEnabled ? 'enabled' : 'disabled'} Automatic error reporting is {isErrorReportingEnabled ? 'enabled' : 'disabled'}
</div> </div>
<p className="sk-p"> <p className='sk-p'>
Help us improve Standard Notes by automatically submitting<br /> Help us improve Standard Notes by automatically submitting
anonymized error reports. anonymized error reports.
</p> </p>
{errorReportingIdValue && ( {errorReportingIdValue && (
<> <>
<p className="sk-p selectable"> <p className='sk-p selectable'>
Your random identifier is<br /> Your random identifier is
strong {errorReportingIdValue} strong {errorReportingIdValue}
</p> </p>
<p className="sk-p"> <p className='sk-p'>
Disabling error reporting will remove that identifier from your<br /> Disabling error reporting will remove that identifier from your
local storage, and a new identifier will be created should you<br /> local storage, and a new identifier will be created should you
decide to enable error reporting again in the future. decide to enable error reporting again in the future.
</p> </p>
</> </>
)} )}
<div className="sk-panel-row"> <div className='sk-panel-row'>
<button className="sn-button small info" onClick={toggleErrorReportingEnabled}> <button className='sn-button small info' onClick={toggleErrorReportingEnabled}>
{isErrorReportingEnabled ? 'Disable' : 'Enable'} Error Reporting {isErrorReportingEnabled ? 'Disable' : 'Enable'} Error Reporting
</button> </button>
</div> </div>
<div className="sk-panel-row"> <div className='sk-panel-row'>
<a className="sk-a" onClick={openErrorReportingDialog}>What data is being sent?</a> <a className='sk-a' onClick={openErrorReportingDialog}>What data is being sent?</a>
</div> </div>
</div> </div>
</>
)}
</div> </div>
)} )}
</div> </div>
{ <div className='sk-panel-footer'>
// TODO: Vardan: check whether this works <div className='sk-panel-row'>
() => ConfirmSignoutDirective <div className='sk-p left neutral'>
}
<div className="sk-panel-footer">
<div className="sk-panel-row">
<div className="sk-p left neutral">
<span>{appVersion}</span> <span>{appVersion}</span>
{showBetaWarning && ( {showBetaWarning && (
<span> <span>
<a className="sk-a" onClick={disableBetaWarning}>Hide beta warning</a> <a className='sk-a' onClick={disableBetaWarning}>Hide beta warning</a>
</span> </span>
)} )}
</div> </div>
{(showLogin || showRegister) && ( {(showLogin || showRegister) && (
<a className="sk-a right" onClick={hidePasswordForm}>Cancel</a> <a className='sk-a right' onClick={hidePasswordForm}>Cancel</a>
)} )}
{!showLogin && !showRegister && ( {!showLogin && !showRegister && (
<a className="sk-a right danger capitalize" onClick={signOut}> <a className='sk-a right danger capitalize' onClick={signOut}>
{user ? "Sign out" : "Clear session data"} {user ? 'Sign out' : 'Clear session data'}
</a> </a>
)} )}
</div> </div>

View File

@@ -18,7 +18,11 @@
ng-if='ctrl.showAccountMenu', ng-if='ctrl.showAccountMenu',
application='ctrl.application' application='ctrl.application'
) )
account-menu2() account-menu2(
ng-click='$event.stopPropagation()',
app-state='ctrl.appState'
application='ctrl.application'
)
.sk-app-bar-item .sk-app-bar-item
a.no-decoration.sk-label.title( a.no-decoration.sk-label.title(
href='https://standardnotes.org/help', href='https://standardnotes.org/help',

View File

@@ -438,7 +438,6 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
} }
accountMenuPressed() { accountMenuPressed() {
console.log('here');
this.appState.accountMenu.toggleShow(); this.appState.accountMenu.toggleShow();
this.appState.accountMenu2.toggleShow(); this.appState.accountMenu2.toggleShow();
this.closeAllRooms(); this.closeAllRooms();