From 96aaff5ff818ffb84d4328b0542809603ad0f7fa Mon Sep 17 00:00:00 2001
From: VardanHakobyan
Date: Wed, 16 Jun 2021 15:16:45 +0400
Subject: [PATCH] refactor: move all applicable parts to mobx instead of
passing from parent to children components
---
.../components/AccountMenu/Authentication.tsx | 49 +++++-----
.../components/AccountMenu/DataBackup.tsx | 22 ++---
.../components/AccountMenu/Encryption.tsx | 19 ++--
.../components/AccountMenu/Footer.tsx | 32 +++---
.../components/AccountMenu/PasscodeLock.tsx | 22 ++---
.../components/AccountMenu/Protections.tsx | 56 ++++++++++-
.../components/AccountMenu/User.tsx | 4 +-
.../components/AccountMenu/index.tsx | 97 ++++---------------
.../ui_models/app_state/account_menu_state.ts | 45 ++++++++-
9 files changed, 186 insertions(+), 160 deletions(-)
diff --git a/app/assets/javascripts/components/AccountMenu/Authentication.tsx b/app/assets/javascripts/components/AccountMenu/Authentication.tsx
index 1e16c3b81..7e99deae1 100644
--- a/app/assets/javascripts/components/AccountMenu/Authentication.tsx
+++ b/app/assets/javascripts/components/AccountMenu/Authentication.tsx
@@ -9,36 +9,27 @@ import { JSXInternal } from 'preact/src/jsx';
import TargetedEvent = JSXInternal.TargetedEvent;
import TargetedKeyboardEvent = JSXInternal.TargetedKeyboardEvent;
import { WebApplication } from '@/ui_models/application';
-import { StateUpdater, useEffect, useRef, useState } from 'preact/hooks';
+import { useEffect, useRef, useState } from 'preact/hooks';
import TargetedMouseEvent = JSXInternal.TargetedMouseEvent;
-import { FunctionalComponent } from 'preact';
import { User } from '@standardnotes/snjs/dist/@types/services/api/responses';
+import { observer } from 'mobx-react-lite';
+import { AppState } from '@/ui_models/app_state';
type Props = {
application: WebApplication;
- server: string | undefined;
- setServer: StateUpdater;
+ appState: AppState;
closeAccountMenu: () => void;
notesAndTagsCount: number;
- showLogin: boolean;
- setShowLogin: StateUpdater;
- showRegister: boolean;
- setShowRegister: StateUpdater;
user: User | undefined;
}
-const Authentication: FunctionalComponent = ({
- application,
- server,
- setServer,
- closeAccountMenu,
- notesAndTagsCount,
- showLogin,
- setShowLogin,
- showRegister,
- setShowRegister,
- user
- }: Props) => {
+const Authentication = observer(({
+ application,
+ appState,
+ closeAccountMenu,
+ notesAndTagsCount,
+ user
+ }: Props) => {
const [showAdvanced, setShowAdvanced] = useState(false);
const [isAuthenticating, setIsAuthenticating] = useState(false);
@@ -52,6 +43,15 @@ const Authentication: FunctionalComponent = ({
const [isStrictSignIn, setIsStrictSignIn] = useState(false);
const [shouldMergeLocal, setShouldMergeLocal] = useState(true);
+ const {
+ server,
+ showLogin,
+ showRegister,
+ setShowLogin,
+ setShowRegister,
+ setServer
+ } = appState.accountMenu;
+
useEffect(() => {
if (isEmailFocused) {
emailInputRef.current.focus();
@@ -108,6 +108,7 @@ const Authentication: FunctionalComponent = ({
if (!error) {
setIsAuthenticating(false);
setPassword('');
+ setShowLogin(false);
closeAccountMenu();
return;
@@ -119,7 +120,6 @@ const Authentication: FunctionalComponent = ({
if (error.message) {
await application.alertService.alert(error.message);
- // await handleAlert(error.message);
}
setIsAuthenticating(false);
@@ -128,7 +128,6 @@ const Authentication: FunctionalComponent = ({
const register = async () => {
if (passwordConfirmation !== password) {
application.alertService.alert(STRING_NON_MATCHING_PASSWORDS);
- // handleAlert(STRING_NON_MATCHING_PASSWORDS);
return;
}
setStatus(STRING_GENERATING_REGISTER_KEYS);
@@ -147,9 +146,9 @@ const Authentication: FunctionalComponent = ({
setIsAuthenticating(false);
application.alertService.alert(error.message);
- // handleAlert(error.message);
} else {
setIsAuthenticating(false);
+ setShowRegister(false);
closeAccountMenu();
}
};
@@ -280,6 +279,7 @@ const Authentication: FunctionalComponent = ({
/>}
);
-};
+});
export default Encryption;
diff --git a/app/assets/javascripts/components/AccountMenu/Footer.tsx b/app/assets/javascripts/components/AccountMenu/Footer.tsx
index b9f86a4cb..a19f07dc6 100644
--- a/app/assets/javascripts/components/AccountMenu/Footer.tsx
+++ b/app/assets/javascripts/components/AccountMenu/Footer.tsx
@@ -1,49 +1,43 @@
import { AppState } from '@/ui_models/app_state';
-import { StateUpdater, useState } from 'preact/hooks';
+import { useState } from 'preact/hooks';
import { WebApplication } from '@/ui_models/application';
import { User } from '@standardnotes/snjs/dist/@types/services/api/responses';
import { observer } from 'mobx-react-lite';
type Props = {
- appState: AppState;
application: WebApplication;
- showLogin: boolean;
- setShowLogin: StateUpdater;
- showRegister: boolean;
- setShowRegister: StateUpdater;
+ appState: AppState;
user: User | undefined;
}
const Footer = observer(({
- appState,
application,
- showLogin,
- setShowLogin,
- showRegister,
- setShowRegister,
+ appState,
user
}: Props) => {
+ const {
+ showLogin,
+ showRegister,
+ setShowLogin,
+ setShowRegister,
+ setSigningOut
+ } = appState.accountMenu;
- const showBetaWarning = appState.showBetaWarning;
-
+ const { showBetaWarning, disableBetaWarning: disableAppStateBetaWarning } = appState;
const [appVersion] = useState(() => `v${((window as any).electronAppVersion || application.bridge.appVersion)}`);
const disableBetaWarning = () => {
- appState.disableBetaWarning();
+ disableAppStateBetaWarning();
};
const signOut = () => {
- appState.accountMenu.setSigningOut(true);
+ setSigningOut(true);
};
const hidePasswordForm = () => {
setShowLogin(false);
setShowRegister(false);
- // TODO: Vardan: this comes from main `index.tsx` and the below commented parts should reset password and confirmation.
- // Check whether it works when I don't call them explicitly.
- // setPassword('');
- // setPasswordConfirmation(undefined);
};
return (
diff --git a/app/assets/javascripts/components/AccountMenu/PasscodeLock.tsx b/app/assets/javascripts/components/AccountMenu/PasscodeLock.tsx
index 9f8d29d77..4050d2358 100644
--- a/app/assets/javascripts/components/AccountMenu/PasscodeLock.tsx
+++ b/app/assets/javascripts/components/AccountMenu/PasscodeLock.tsx
@@ -12,25 +12,23 @@ import { alertDialog } from '@Services/alertService';
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import { ApplicationEvent } from '@standardnotes/snjs';
import TargetedMouseEvent = JSXInternal.TargetedMouseEvent;
-import { StateUpdater } from 'preact/hooks';
-import { FunctionalComponent } from 'preact';
+import { observer } from 'mobx-react-lite';
+import { AppState } from '@/ui_models/app_state';
type Props = {
application: WebApplication;
- setEncryptionStatusString: StateUpdater;
- setIsEncryptionEnabled: StateUpdater;
- setIsBackupEncrypted: StateUpdater;
+ appState: AppState;
};
-const PasscodeLock: FunctionalComponent = ({
- application,
- setEncryptionStatusString,
- setIsEncryptionEnabled,
- setIsBackupEncrypted
- }) => {
+const PasscodeLock = observer(({
+ application,
+ appState,
+ }: Props) => {
const keyStorageInfo = StringUtils.keyStorageInfo(application);
const passcodeAutoLockOptions = application.getAutolockService().getAutoLockIntervalOptions();
+ const { setIsEncryptionEnabled, setIsBackupEncrypted, setEncryptionStatusString } = appState.accountMenu;
+
const passcodeInputRef = useRef();
const [passcode, setPasscode] = useState(undefined);
@@ -263,6 +261,6 @@ const PasscodeLock: FunctionalComponent = ({
)}
);
-};
+});
export default PasscodeLock;
diff --git a/app/assets/javascripts/components/AccountMenu/Protections.tsx b/app/assets/javascripts/components/AccountMenu/Protections.tsx
index f48b02918..2b4049974 100644
--- a/app/assets/javascripts/components/AccountMenu/Protections.tsx
+++ b/app/assets/javascripts/components/AccountMenu/Protections.tsx
@@ -1,19 +1,65 @@
import { WebApplication } from '@/ui_models/application';
import { FunctionalComponent } from 'preact';
+import { useCallback, useState } from '@node_modules/preact/hooks';
+import { useEffect } from 'preact/hooks';
+import { ApplicationEvent } from '@node_modules/@standardnotes/snjs';
+import { isSameDay } from '@/utils';
type Props = {
application: WebApplication;
- protectionsDisabledUntil: string | null;
};
-const Protections: FunctionalComponent = ({
- application,
- protectionsDisabledUntil
- }) => {
+const Protections: FunctionalComponent = ({ application }) => {
const enableProtections = () => {
application.clearProtectionSession();
};
+ const hasProtections = application.hasProtectionSources();
+
+ const getProtectionsDisabledUntil = useCallback((): 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;
+ }, [application]);
+
+ const [protectionsDisabledUntil, setProtectionsDisabledUntil] = useState(getProtectionsDisabledUntil());
+
+ useEffect(() => {
+ const removeProtectionSessionExpiryDateChangedObserver = application.addEventObserver(
+ async () => {
+ setProtectionsDisabledUntil(getProtectionsDisabledUntil());
+ },
+ ApplicationEvent.ProtectionSessionExpiryDateChanged
+ );
+
+ return () => {
+ removeProtectionSessionExpiryDateChangedObserver();
+ };
+ }, [application, getProtectionsDisabledUntil]);
+
+ if (!hasProtections) {
+ return null;
+ }
+
return (
Protections
diff --git a/app/assets/javascripts/components/AccountMenu/User.tsx b/app/assets/javascripts/components/AccountMenu/User.tsx
index f7f172338..52cbbd9f2 100644
--- a/app/assets/javascripts/components/AccountMenu/User.tsx
+++ b/app/assets/javascripts/components/AccountMenu/User.tsx
@@ -5,7 +5,6 @@ import { WebApplication } from '@/ui_models/application';
type Props = {
email: string;
- server: string | undefined;
appState: AppState;
application: WebApplication;
closeAccountMenu: () => void;
@@ -13,11 +12,12 @@ type Props = {
const User = observer(({
email,
- server,
appState,
application,
closeAccountMenu
}: Props) => {
+ const { server } = appState.accountMenu;
+
const openPasswordWizard = () => {
closeAccountMenu();
application.presentPasswordWizard(PasswordWizardType.ChangePassword);
diff --git a/app/assets/javascripts/components/AccountMenu/index.tsx b/app/assets/javascripts/components/AccountMenu/index.tsx
index 694d8cbab..56c6ef6e0 100644
--- a/app/assets/javascripts/components/AccountMenu/index.tsx
+++ b/app/assets/javascripts/components/AccountMenu/index.tsx
@@ -3,7 +3,6 @@ import { toDirective } from '@/components/utils';
import { AppState } from '@/ui_models/app_state';
import { WebApplication } from '@/ui_models/application';
import { useEffect, useState } from 'preact/hooks';
-import { isSameDay } from '@/utils';
import { ApplicationEvent } from '@standardnotes/snjs';
import { ConfirmSignoutContainer } from '@/components/ConfirmSignoutModal';
import Authentication from '@/components/AccountMenu/Authentication';
@@ -14,7 +13,6 @@ import Protections from '@/components/AccountMenu/Protections';
import PasscodeLock from '@/components/AccountMenu/PasscodeLock';
import DataBackup from '@/components/AccountMenu/DataBackup';
import ErrorReporting from '@/components/AccountMenu/ErrorReporting';
-import { useCallback } from 'preact/hooks';
type Props = {
appState: AppState;
@@ -22,45 +20,17 @@ type Props = {
};
const AccountMenu = observer(({ application, appState }: Props) => {
- const getProtectionsDisabledUntil = useCallback((): 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;
- }, [application]);
-
- const [showLogin, setShowLogin] = useState(false);
- const [showRegister, setShowRegister] = useState(false);
- const [encryptionStatusString, setEncryptionStatusString] = useState
(undefined);
- const [isEncryptionEnabled, setIsEncryptionEnabled] = useState(false);
- const [server, setServer] = useState(application.getHost());
- const [isBackupEncrypted, setIsBackupEncrypted] = useState(isEncryptionEnabled);
- const [protectionsDisabledUntil, setProtectionsDisabledUntil] = useState(getProtectionsDisabledUntil());
const [user, setUser] = useState(application.getUser());
- const [hasProtections] = useState(application.hasProtectionSources());
- const { notesAndTagsCount } = appState.accountMenu;
+ const {
+ notesAndTagsCount,
+ showLogin,
+ showRegister,
+ closeAccountMenu: closeAppStateAccountMenu
+ } = appState.accountMenu;
const closeAccountMenu = () => {
- appState.accountMenu.closeAccountMenu();
+ closeAppStateAccountMenu();
};
// Add the required event observers
@@ -72,18 +42,10 @@ const AccountMenu = observer(({ application, appState }: Props) => {
ApplicationEvent.KeyStatusChanged
);
- const removeProtectionSessionExpiryDateChangedObserver = application.addEventObserver(
- async () => {
- setProtectionsDisabledUntil(getProtectionsDisabledUntil());
- },
- ApplicationEvent.ProtectionSessionExpiryDateChanged
- );
-
return () => {
removeKeyStatusChangedObserver();
- removeProtectionSessionExpiryDateChangedObserver();
};
- }, [application, getProtectionsDisabledUntil]);
+ }, [application]);
return (
@@ -95,62 +57,45 @@ const AccountMenu = observer(({ application, appState }: Props) => {
{!showLogin && !showRegister && (
{user && (
)}
- {hasProtections && (
-
- )}
+
)}
-
-
+
diff --git a/app/assets/javascripts/ui_models/app_state/account_menu_state.ts b/app/assets/javascripts/ui_models/app_state/account_menu_state.ts
index 0e492a09b..c3e58baa4 100644
--- a/app/assets/javascripts/ui_models/app_state/account_menu_state.ts
+++ b/app/assets/javascripts/ui_models/app_state/account_menu_state.ts
@@ -6,7 +6,13 @@ import { SNItem } from '@standardnotes/snjs/dist/@types/models/core/item';
export class AccountMenuState {
show = false;
signingOut = false;
+ server: string | undefined = undefined;
notesAndTags: SNItem[] = [];
+ isEncryptionEnabled = false;
+ encryptionStatusString = '';
+ isBackupEncrypted = false;
+ showLogin = false;
+ showRegister = false;
constructor(
private application: WebApplication,
@@ -15,21 +21,34 @@ export class AccountMenuState {
makeObservable(this, {
show: observable,
signingOut: observable,
+ server: observable,
notesAndTags: observable,
+ isEncryptionEnabled: observable,
+ encryptionStatusString: observable,
+ isBackupEncrypted: observable,
+ showLogin: observable,
+ showRegister: observable,
setShow: action,
toggleShow: action,
setSigningOut: action,
+ setIsEncryptionEnabled: action,
+ setEncryptionStatusString: action,
+ setIsBackupEncrypted: action,
notesAndTagsCount: computed
});
appEventListeners.push(
this.application.streamItems(
- [ContentType.Note, ContentType.Tag],
+ [
+ ContentType.Note, ContentType.Tag,
+ ContentType.Component // TODO: is this correct for streaming `server`?
+ ],
() => {
runInAction(() => {
this.notesAndTags = this.application.getItems([ContentType.Note, ContentType.Tag]);
+ this.setServer(this.application.getHost());
});
}
)
@@ -48,6 +67,30 @@ export class AccountMenuState {
this.signingOut = signingOut;
};
+ setServer = (server: string | undefined): void => {
+ this.server = server;
+ };
+
+ setIsEncryptionEnabled = (isEncryptionEnabled: boolean): void => {
+ this.isEncryptionEnabled = isEncryptionEnabled;
+ };
+
+ setEncryptionStatusString = (encryptionStatusString: string): void => {
+ this.encryptionStatusString = encryptionStatusString;
+ };
+
+ setIsBackupEncrypted = (isBackupEncrypted: boolean): void => {
+ this.isBackupEncrypted = isBackupEncrypted;
+ };
+
+ setShowLogin = (showLogin: boolean): void => {
+ this.showLogin = showLogin;
+ };
+
+ setShowRegister = (showRegister: boolean): void => {
+ this.showRegister = showRegister;
+ };
+
toggleShow = (): void => {
this.show = !this.show;
};