diff --git a/app/assets/javascripts/components/AccountMenu2.tsx b/app/assets/javascripts/components/AccountMenu2.tsx index 6b15c38e4..6f6dd0299 100644 --- a/app/assets/javascripts/components/AccountMenu2.tsx +++ b/app/assets/javascripts/components/AccountMenu2.tsx @@ -2,13 +2,13 @@ import { observer } from 'mobx-react-lite'; import { toDirective } from '@/components/utils'; import { AppState } from '@/ui_models/app_state'; 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 } from '@/utils'; +import { isDesktopApplication, isSameDay } from '@/utils'; import { storage, StorageKey } from '@Services/localStorage'; 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 // interface Props {} // TODO: Vardan: implement props and remove `eslint-disable` @@ -20,11 +20,33 @@ type Props = { // const HistoryMenu = observer((props: Props) => { // const AccountMenu = observer((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(); // TODO: implement what is missing for `passcodeInput`, e.g. - autofocus - const passcodeInput = useRef(); - // const { user, formData } = application; - // const [user, setUser] = useState(null); // TODO: Vardan: set correct type and initial value - const [user, setUser] = useState(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 const [showLogin, setShowLogin] = useState(false); const [showRegister, setShowRegister] = useState(false); @@ -33,32 +55,41 @@ const AccountMenu = observer(({ appState, application }: Props) => { const [password, setPassword] = useState(undefined); const [passwordConfirmation, setPasswordConfirmation] = useState(undefined); const [status, setStatus] = useState(''); - const [syncError, setSyncError] = useState(''); - const [server, setServer] = useState(''); - const [notesAndTagsCount, setNotesAndTagsCount] = useState(0); - const [isEncryptionEnabled, setIsEncryptionEnabled] = useState(false); - const [encryptionStatusString, setEncryptionStatusString] = useState(''); - const [hasProtections, setHasProtections] = useState(false); - const [protectionsDisabledUntil, setProtectionsDisabledUntil] = useState(null); - const [hasPasscode, setHasPasscode] = useState(false); - const [canAddPasscode, setCanAddPasscode] = useState(false); + const [syncError, setSyncError] = useState(undefined); + + const [server, setServer] = useState(undefined); const [showPasscodeForm, setShowPasscodeForm] = useState(false); - const [keyStorageInfo, setKeyStorageInfo] = useState(null); - const [passcodeAutoLockOptions, setPasscodeAutoLockOptions] = useState<{value: number; label: string}[]>([]); const [selectedAutoLockInterval, setSelectedAutoLockInterval] = useState(null); const [isLoading, setIsLoading] = useState(false); const [isErrorReportingEnabled, setIsErrorReportingEnabled] = useState(false); 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` - // 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 reloadAutoLockInterval = useCallback(async () => { + const interval = await application.getAutolockService().getAutoLockInterval(); + setSelectedAutoLockInterval(interval); + }, [application]); + 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 = () => { @@ -110,8 +141,8 @@ const AccountMenu = observer(({ appState, application }: Props) => { }; const getEncryptionStatusForNotes = () => { - console.log('implement `getEncryptionStatusForNotes`'); - return ''; + const length = notesAndTagsCount; + return `${length}/${length} notes and tags encrypted`; }; const enableProtections = () => { @@ -192,23 +223,45 @@ const AccountMenu = observer(({ appState, application }: Props) => { 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(() => { - // TODO: Vardan: get the real count - setNotesAndTagsCount(1); + setIsErrorReportingEnabled(storage.get(StorageKey.DisableErrorReporting) === false); }, []); 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(() => { - setAppVersion(`v${((window as any).electronAppVersion || appVersion)}`); - }, [appVersion]); - */ + const host = application.getHost(); + setServer(host); + }, [application]); + /* const { searchOptions } = appState; @@ -222,7 +275,7 @@ const AccountMenu = observer(({ appState, application }: Props) => { return (
{
- Standard Notes is free on every platform, and comes
+ Standard Notes is free on every platform, and comes standard with sync and encryption.
@@ -275,7 +328,8 @@ const AccountMenu = observer(({ appState, application }: Props) => { type='email' placeholder='Email' required - spellCheck={false} /> + spellCheck={false} + /> { onChange={handlePasswordConfirmationChange} />} {showAdvanced && ( -
-
-
+
+
+
Advanced Options
-
- - + +
{showLogin && ( -