feat: delete local backups when signing out

This commit is contained in:
Baptiste Grob
2021-04-14 15:05:33 +02:00
parent d3ff9f2f21
commit 6a52721ea7
12 changed files with 168 additions and 17 deletions

View File

@@ -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]);

View File

@@ -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 <ConfirmSignoutModal {...props} />;
});
const ConfirmSignoutModal = observer(({ application, appState }: Props) => {
const [deleteLocalBackups, setDeleteLocalBackups] = useState(
application.hasAccount()
);
const cancelRef = useRef<HTMLButtonElement>();
function close() {
appState.accountMenu.setSigningOut(false);
}
const [localBackupsCount, setLocalBackupsCount] = useState(0);
useEffect(() => {
application.bridge.localBackupsCount().then(setLocalBackupsCount);
}, [appState.accountMenu.signingOut, application.bridge]);
return (
<AlertDialog onDismiss={close} leastDestructiveRef={cancelRef}>
<div className="sk-modal-content">
<div className="sn-component">
<div className="sk-panel">
<div className="sk-panel-content">
<div className="sk-panel-section">
<AlertDialogLabel className="sk-h3 sk-panel-section-title capitalize">
End your session?
</AlertDialogLabel>
<AlertDialogDescription className="sk-panel-row">
<p className="color-foreground">
{STRING_SIGN_OUT_CONFIRMATION}
</p>
</AlertDialogDescription>
{localBackupsCount > 0 && (
<div className="flex">
<div className="sk-panel-row"></div>
<label className="flex items-center">
<input
type="checkbox"
checked={deleteLocalBackups}
onChange={(event) => {
setDeleteLocalBackups(
(event.target as HTMLInputElement).checked
);
}}
/>
<span className="ml-2">
Delete {localBackupsCount} local backup file
{localBackupsCount > 1 ? 's' : ''}
</span>
</label>
<button
className="capitalize sk-a ml-1.5 p-0 rounded cursor-pointer"
onClick={() => {
application.bridge.viewlocalBackups();
}}
>
View backup files
</button>
</div>
)}
<div className="flex my-1 mt-4">
<button
className="sn-button neutral"
ref={cancelRef}
onClick={close}
>
Cancel
</button>
<button
className="sn-button danger ml-2"
onClick={() => {
if (deleteLocalBackups) {
application.signOutAndDeleteLocalBackups();
} else {
application.signOut();
}
close();
}}
>
{application.hasAccount()
? 'Sign Out'
: 'Clear Session Data'}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</AlertDialog>
);
});
export const ConfirmSignoutDirective = toDirective<Props>(
ConfirmSignoutContainer
);

View File

@@ -385,15 +385,8 @@ class AccountMenuCtrl extends PureViewCtrl<unknown, AccountMenuState> {
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() {

View File

@@ -10,9 +10,13 @@ export interface Bridge {
environment: Environment;
getKeychainValue(): Promise<unknown>;
setKeychainValue(value: any): Promise<void>;
setKeychainValue(value: unknown): Promise<void>;
clearKeychainValue(): Promise<void>;
localBackupsCount(): Promise<number>;
viewlocalBackups(): void;
deleteLocalBackups(): Promise<void>;
extensionsServerHost?: string;
syncComponents(payloads: unknown[]): void;
onMajorDataChange(): void;

View File

@@ -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<void> {
async setKeychainValue(value: unknown): Promise<void> {
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<number> {
/** Browsers cannot save backups, only let you download one */
return 0;
}
/** No-ops */
/* eslint-disable @typescript-eslint/no-empty-function */
async deleteLocalBackups(): Promise<void> {}
viewlocalBackups(): void {}
syncComponents(): void {}
onMajorDataChange(): void {}
onInitialDataLoad(): void {}

View File

@@ -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 =

View File

@@ -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;
}

View File

@@ -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<void> {
await this.bridge.deleteLocalBackups();
return this.signOut();
}
presentPasswordModal(callback: () => void) {
const scope = this.scope!.$new(true) as InputModalScope;
scope.type = "password";

View File

@@ -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);
}

View File

@@ -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`

View File

@@ -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"
}
}

View File

@@ -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"