From 6a52721ea789c60c9877d28358e4b1a51e2ddd4e Mon Sep 17 00:00:00 2001 From: Baptiste Grob <60621355+baptiste-grob@users.noreply.github.com> Date: Wed, 14 Apr 2021 15:05:33 +0200 Subject: [PATCH] feat: delete local backups when signing out --- app/assets/javascripts/app.ts | 4 +- .../components/ConfirmSignoutModal.tsx | 119 ++++++++++++++++++ .../directives/views/accountMenu.ts | 11 +- app/assets/javascripts/services/bridge.ts | 6 +- .../javascripts/services/browserBridge.ts | 11 +- app/assets/javascripts/strings.ts | 3 +- .../ui_models/app_state/account_menu_state.ts | 8 ++ .../javascripts/ui_models/application.ts | 7 +- app/assets/stylesheets/_sn.scss | 4 + .../templates/directives/account-menu.pug | 6 +- package.json | 1 + yarn.lock | 5 + 12 files changed, 168 insertions(+), 17 deletions(-) create mode 100644 app/assets/javascripts/components/ConfirmSignoutModal.tsx diff --git a/app/assets/javascripts/app.ts b/app/assets/javascripts/app.ts index a2bcbd446..bcd28cf2f 100644 --- a/app/assets/javascripts/app.ts +++ b/app/assets/javascripts/app.ts @@ -59,6 +59,7 @@ import { SessionsModalDirective } from './components/SessionsModal'; import { NoAccountWarningDirective } from './components/NoAccountWarning'; import { NoProtectionsdNoteWarningDirective } from './components/NoProtectionsNoteWarning'; import { SearchOptionsDirective } from './components/SearchOptions'; +import { ConfirmSignoutDirective } from './components/ConfirmSignoutModal'; function reloadHiddenFirefoxTab(): boolean { /** @@ -147,7 +148,8 @@ const startApplication: StartApplication = async function startApplication( .directive('sessionsModal', SessionsModalDirective) .directive('noAccountWarning', NoAccountWarningDirective) .directive('protectedNotePanel', NoProtectionsdNoteWarningDirective) - .directive('searchOptions', SearchOptionsDirective); + .directive('searchOptions', SearchOptionsDirective) + .directive('confirmSignout', ConfirmSignoutDirective); // Filters angular.module('app').filter('trusted', ['$sce', trusted]); diff --git a/app/assets/javascripts/components/ConfirmSignoutModal.tsx b/app/assets/javascripts/components/ConfirmSignoutModal.tsx new file mode 100644 index 000000000..6d3cc19f2 --- /dev/null +++ b/app/assets/javascripts/components/ConfirmSignoutModal.tsx @@ -0,0 +1,119 @@ +import { useEffect, useRef, useState } from 'preact/hooks'; +import { + AlertDialog, + AlertDialogDescription, + AlertDialogLabel, +} from '@reach/alert-dialog'; +import { STRING_SIGN_OUT_CONFIRMATION } from '@/strings'; +import { WebApplication } from '@/ui_models/application'; +import { toDirective } from './utils'; +import { AppState } from '@/ui_models/app_state'; +import { observer } from 'mobx-react-lite'; + +type Props = { + application: WebApplication; + appState: AppState; +}; + +const ConfirmSignoutContainer = observer((props: Props) => { + if (!props.appState.accountMenu.signingOut) { + return null; + } + return ; +}); + +const ConfirmSignoutModal = observer(({ application, appState }: Props) => { + const [deleteLocalBackups, setDeleteLocalBackups] = useState( + application.hasAccount() + ); + + const cancelRef = useRef(); + function close() { + appState.accountMenu.setSigningOut(false); + } + + const [localBackupsCount, setLocalBackupsCount] = useState(0); + + useEffect(() => { + application.bridge.localBackupsCount().then(setLocalBackupsCount); + }, [appState.accountMenu.signingOut, application.bridge]); + + return ( + +
+
+
+
+
+ + End your session? + + +

+ {STRING_SIGN_OUT_CONFIRMATION} +

+
+ {localBackupsCount > 0 && ( +
+
+ + +
+ )} +
+ + +
+
+
+
+
+
+
+ ); +}); + +export const ConfirmSignoutDirective = toDirective( + ConfirmSignoutContainer +); diff --git a/app/assets/javascripts/directives/views/accountMenu.ts b/app/assets/javascripts/directives/views/accountMenu.ts index 10c35da52..d8e799bd1 100644 --- a/app/assets/javascripts/directives/views/accountMenu.ts +++ b/app/assets/javascripts/directives/views/accountMenu.ts @@ -385,15 +385,8 @@ class AccountMenuCtrl extends PureViewCtrl { this.appState.openSessionsModal(); } - async destroyLocalData() { - if ( - await confirmDialog({ - text: STRING_SIGN_OUT_CONFIRMATION, - confirmButtonStyle: 'danger', - }) - ) { - this.application.signOut(); - } + signOut() { + this.appState.accountMenu.setSigningOut(true); } showRegister() { diff --git a/app/assets/javascripts/services/bridge.ts b/app/assets/javascripts/services/bridge.ts index 5cf08d83a..6e9c74854 100644 --- a/app/assets/javascripts/services/bridge.ts +++ b/app/assets/javascripts/services/bridge.ts @@ -10,9 +10,13 @@ export interface Bridge { environment: Environment; getKeychainValue(): Promise; - setKeychainValue(value: any): Promise; + setKeychainValue(value: unknown): Promise; clearKeychainValue(): Promise; + localBackupsCount(): Promise; + viewlocalBackups(): void; + deleteLocalBackups(): Promise; + extensionsServerHost?: string; syncComponents(payloads: unknown[]): void; onMajorDataChange(): void; diff --git a/app/assets/javascripts/services/browserBridge.ts b/app/assets/javascripts/services/browserBridge.ts index 29796ec80..174b933bb 100644 --- a/app/assets/javascripts/services/browserBridge.ts +++ b/app/assets/javascripts/services/browserBridge.ts @@ -1,4 +1,4 @@ -import { Bridge } from "./bridge"; +import { Bridge } from './bridge'; import { Environment } from '@standardnotes/snjs'; const KEYCHAIN_STORAGE_KEY = 'keychain'; @@ -14,7 +14,7 @@ export class BrowserBridge implements Bridge { } } - async setKeychainValue(value: any): Promise { + async setKeychainValue(value: unknown): Promise { localStorage.setItem(KEYCHAIN_STORAGE_KEY, JSON.stringify(value)); } @@ -22,9 +22,16 @@ export class BrowserBridge implements Bridge { localStorage.removeItem(KEYCHAIN_STORAGE_KEY); } + async localBackupsCount(): Promise { + /** Browsers cannot save backups, only let you download one */ + return 0; + } + /** No-ops */ /* eslint-disable @typescript-eslint/no-empty-function */ + async deleteLocalBackups(): Promise {} + viewlocalBackups(): void {} syncComponents(): void {} onMajorDataChange(): void {} onInitialDataLoad(): void {} diff --git a/app/assets/javascripts/strings.ts b/app/assets/javascripts/strings.ts index 25232bc9c..f9847de89 100644 --- a/app/assets/javascripts/strings.ts +++ b/app/assets/javascripts/strings.ts @@ -52,8 +52,7 @@ export function StringEmptyTrash(count: number) { /** @account */ export const STRING_ACCOUNT_MENU_UNCHECK_MERGE = 'Unchecking this option means any of the notes you have written while you were signed out will be deleted. Are you sure you want to discard these notes?'; -export const STRING_SIGN_OUT_CONFIRMATION = - 'Are you sure you want to end your session? This will delete all local items and extensions.'; +export const STRING_SIGN_OUT_CONFIRMATION = 'This will delete all local items and extensions.'; export const STRING_ERROR_DECRYPTING_IMPORT = 'There was an error decrypting your items. Make sure the password you entered is correct and try again.'; export const STRING_E2E_ENABLED = 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 bf7489495..2ef9eb719 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 @@ -2,12 +2,16 @@ import { action, makeObservable, observable } from "mobx"; export class AccountMenuState { show = false; + signingOut = false; constructor() { makeObservable(this, { show: observable, + signingOut: observable, + setShow: action, toggleShow: action, + setSigningOut: action, }); } @@ -15,6 +19,10 @@ export class AccountMenuState { this.show = show; } + setSigningOut = (signingOut: boolean): void => { + this.signingOut = signingOut; + } + toggleShow = (): void => { this.show = !this.show; } diff --git a/app/assets/javascripts/ui_models/application.ts b/app/assets/javascripts/ui_models/application.ts index 07256a10e..c60b1d725 100644 --- a/app/assets/javascripts/ui_models/application.ts +++ b/app/assets/javascripts/ui_models/application.ts @@ -53,7 +53,7 @@ export class WebApplication extends SNApplication { private $compile: angular.ICompileService, scope: angular.IScope, defaultSyncServerHost: string, - private bridge: Bridge, + public bridge: Bridge, ) { super( bridge.environment, @@ -169,6 +169,11 @@ export class WebApplication extends SNApplication { return angular.element(document.getElementById(this.identifier)!); } + async signOutAndDeleteLocalBackups(): Promise { + await this.bridge.deleteLocalBackups(); + return this.signOut(); + } + presentPasswordModal(callback: () => void) { const scope = this.scope!.$new(true) as InputModalScope; scope.type = "password"; diff --git a/app/assets/stylesheets/_sn.scss b/app/assets/stylesheets/_sn.scss index 6d74b17bc..dc6968a9e 100644 --- a/app/assets/stylesheets/_sn.scss +++ b/app/assets/stylesheets/_sn.scss @@ -24,6 +24,10 @@ background-clip: padding-box; } +.color-foreground { + color: var(--sn-stylekit-foreground-color); +} + .ring-info { box-shadow: 0 0 0 2px var(--sn-stylekit-info-color); } diff --git a/app/assets/templates/directives/account-menu.pug b/app/assets/templates/directives/account-menu.pug index ff5efdb35..dc2ff1524 100644 --- a/app/assets/templates/directives/account-menu.pug +++ b/app/assets/templates/directives/account-menu.pug @@ -297,6 +297,10 @@ | {{ self.state.errorReportingEnabled ? 'Disable' : 'Enable'}} Error Reporting .sk-panel-row a(ng-click="self.openErrorReportingDialog()").sk-a What data is being sent? + confirm-signout( + app-state='self.appState' + application='self.application' + ) .sk-panel-footer .sk-panel-row .sk-p.left.neutral @@ -311,7 +315,7 @@ ) | Cancel a.sk-a.right.danger.capitalize( - ng-click='self.destroyLocalData()', + ng-click='self.signOut()', ng-if=` !self.state.formData.showLogin && !self.state.formData.showRegister` diff --git a/package.json b/package.json index 16e243e5e..eca86d0c1 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "@standardnotes/sncrypto-web": "^1.2.10", "@standardnotes/snjs": "^2.0.74", "mobx": "^6.1.6", + "mobx-react-lite": "^3.2.0", "preact": "^10.5.12" } } diff --git a/yarn.lock b/yarn.lock index 7fa840f87..696d2be26 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6094,6 +6094,11 @@ mixin-deep@^1.2.0: dependencies: minimist "^1.2.5" +mobx-react-lite@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.2.0.tgz#331d7365a6b053378dfe9c087315b4e41c5df69f" + integrity sha512-q5+UHIqYCOpBoFm/PElDuOhbcatvTllgRp3M1s+Hp5j0Z6XNgDbgqxawJ0ZAUEyKM8X1zs70PCuhAIzX1f4Q/g== + mobx@^6.1.6: version "6.1.6" resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.1.6.tgz#ae75e57ec07d190ed187273864002163fa357224"