feat: delete local backups when signing out
This commit is contained in:
@@ -59,6 +59,7 @@ import { SessionsModalDirective } from './components/SessionsModal';
|
|||||||
import { NoAccountWarningDirective } from './components/NoAccountWarning';
|
import { NoAccountWarningDirective } from './components/NoAccountWarning';
|
||||||
import { NoProtectionsdNoteWarningDirective } from './components/NoProtectionsNoteWarning';
|
import { NoProtectionsdNoteWarningDirective } from './components/NoProtectionsNoteWarning';
|
||||||
import { SearchOptionsDirective } from './components/SearchOptions';
|
import { SearchOptionsDirective } from './components/SearchOptions';
|
||||||
|
import { ConfirmSignoutDirective } from './components/ConfirmSignoutModal';
|
||||||
|
|
||||||
function reloadHiddenFirefoxTab(): boolean {
|
function reloadHiddenFirefoxTab(): boolean {
|
||||||
/**
|
/**
|
||||||
@@ -147,7 +148,8 @@ const startApplication: StartApplication = async function startApplication(
|
|||||||
.directive('sessionsModal', SessionsModalDirective)
|
.directive('sessionsModal', SessionsModalDirective)
|
||||||
.directive('noAccountWarning', NoAccountWarningDirective)
|
.directive('noAccountWarning', NoAccountWarningDirective)
|
||||||
.directive('protectedNotePanel', NoProtectionsdNoteWarningDirective)
|
.directive('protectedNotePanel', NoProtectionsdNoteWarningDirective)
|
||||||
.directive('searchOptions', SearchOptionsDirective);
|
.directive('searchOptions', SearchOptionsDirective)
|
||||||
|
.directive('confirmSignout', ConfirmSignoutDirective);
|
||||||
|
|
||||||
// Filters
|
// Filters
|
||||||
angular.module('app').filter('trusted', ['$sce', trusted]);
|
angular.module('app').filter('trusted', ['$sce', trusted]);
|
||||||
|
|||||||
119
app/assets/javascripts/components/ConfirmSignoutModal.tsx
Normal file
119
app/assets/javascripts/components/ConfirmSignoutModal.tsx
Normal 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
|
||||||
|
);
|
||||||
@@ -385,15 +385,8 @@ class AccountMenuCtrl extends PureViewCtrl<unknown, AccountMenuState> {
|
|||||||
this.appState.openSessionsModal();
|
this.appState.openSessionsModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
async destroyLocalData() {
|
signOut() {
|
||||||
if (
|
this.appState.accountMenu.setSigningOut(true);
|
||||||
await confirmDialog({
|
|
||||||
text: STRING_SIGN_OUT_CONFIRMATION,
|
|
||||||
confirmButtonStyle: 'danger',
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
this.application.signOut();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showRegister() {
|
showRegister() {
|
||||||
|
|||||||
@@ -10,9 +10,13 @@ export interface Bridge {
|
|||||||
environment: Environment;
|
environment: Environment;
|
||||||
|
|
||||||
getKeychainValue(): Promise<unknown>;
|
getKeychainValue(): Promise<unknown>;
|
||||||
setKeychainValue(value: any): Promise<void>;
|
setKeychainValue(value: unknown): Promise<void>;
|
||||||
clearKeychainValue(): Promise<void>;
|
clearKeychainValue(): Promise<void>;
|
||||||
|
|
||||||
|
localBackupsCount(): Promise<number>;
|
||||||
|
viewlocalBackups(): void;
|
||||||
|
deleteLocalBackups(): Promise<void>;
|
||||||
|
|
||||||
extensionsServerHost?: string;
|
extensionsServerHost?: string;
|
||||||
syncComponents(payloads: unknown[]): void;
|
syncComponents(payloads: unknown[]): void;
|
||||||
onMajorDataChange(): void;
|
onMajorDataChange(): void;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Bridge } from "./bridge";
|
import { Bridge } from './bridge';
|
||||||
import { Environment } from '@standardnotes/snjs';
|
import { Environment } from '@standardnotes/snjs';
|
||||||
|
|
||||||
const KEYCHAIN_STORAGE_KEY = 'keychain';
|
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));
|
localStorage.setItem(KEYCHAIN_STORAGE_KEY, JSON.stringify(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,9 +22,16 @@ export class BrowserBridge implements Bridge {
|
|||||||
localStorage.removeItem(KEYCHAIN_STORAGE_KEY);
|
localStorage.removeItem(KEYCHAIN_STORAGE_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async localBackupsCount(): Promise<number> {
|
||||||
|
/** Browsers cannot save backups, only let you download one */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** No-ops */
|
/** No-ops */
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
|
async deleteLocalBackups(): Promise<void> {}
|
||||||
|
viewlocalBackups(): void {}
|
||||||
syncComponents(): void {}
|
syncComponents(): void {}
|
||||||
onMajorDataChange(): void {}
|
onMajorDataChange(): void {}
|
||||||
onInitialDataLoad(): void {}
|
onInitialDataLoad(): void {}
|
||||||
|
|||||||
@@ -52,8 +52,7 @@ export function StringEmptyTrash(count: number) {
|
|||||||
/** @account */
|
/** @account */
|
||||||
export const STRING_ACCOUNT_MENU_UNCHECK_MERGE =
|
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?';
|
'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 =
|
export const STRING_SIGN_OUT_CONFIRMATION = 'This will delete all local items and extensions.';
|
||||||
'Are you sure you want to end your session? This will delete all local items and extensions.';
|
|
||||||
export const STRING_ERROR_DECRYPTING_IMPORT =
|
export const STRING_ERROR_DECRYPTING_IMPORT =
|
||||||
'There was an error decrypting your items. Make sure the password you entered is correct and try again.';
|
'There was an error decrypting your items. Make sure the password you entered is correct and try again.';
|
||||||
export const STRING_E2E_ENABLED =
|
export const STRING_E2E_ENABLED =
|
||||||
|
|||||||
@@ -2,12 +2,16 @@ import { action, makeObservable, observable } from "mobx";
|
|||||||
|
|
||||||
export class AccountMenuState {
|
export class AccountMenuState {
|
||||||
show = false;
|
show = false;
|
||||||
|
signingOut = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
show: observable,
|
show: observable,
|
||||||
|
signingOut: observable,
|
||||||
|
|
||||||
setShow: action,
|
setShow: action,
|
||||||
toggleShow: action,
|
toggleShow: action,
|
||||||
|
setSigningOut: action,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,6 +19,10 @@ export class AccountMenuState {
|
|||||||
this.show = show;
|
this.show = show;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setSigningOut = (signingOut: boolean): void => {
|
||||||
|
this.signingOut = signingOut;
|
||||||
|
}
|
||||||
|
|
||||||
toggleShow = (): void => {
|
toggleShow = (): void => {
|
||||||
this.show = !this.show;
|
this.show = !this.show;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export class WebApplication extends SNApplication {
|
|||||||
private $compile: angular.ICompileService,
|
private $compile: angular.ICompileService,
|
||||||
scope: angular.IScope,
|
scope: angular.IScope,
|
||||||
defaultSyncServerHost: string,
|
defaultSyncServerHost: string,
|
||||||
private bridge: Bridge,
|
public bridge: Bridge,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
bridge.environment,
|
bridge.environment,
|
||||||
@@ -169,6 +169,11 @@ export class WebApplication extends SNApplication {
|
|||||||
return angular.element(document.getElementById(this.identifier)!);
|
return angular.element(document.getElementById(this.identifier)!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async signOutAndDeleteLocalBackups(): Promise<void> {
|
||||||
|
await this.bridge.deleteLocalBackups();
|
||||||
|
return this.signOut();
|
||||||
|
}
|
||||||
|
|
||||||
presentPasswordModal(callback: () => void) {
|
presentPasswordModal(callback: () => void) {
|
||||||
const scope = this.scope!.$new(true) as InputModalScope;
|
const scope = this.scope!.$new(true) as InputModalScope;
|
||||||
scope.type = "password";
|
scope.type = "password";
|
||||||
|
|||||||
@@ -24,6 +24,10 @@
|
|||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.color-foreground {
|
||||||
|
color: var(--sn-stylekit-foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
.ring-info {
|
.ring-info {
|
||||||
box-shadow: 0 0 0 2px var(--sn-stylekit-info-color);
|
box-shadow: 0 0 0 2px var(--sn-stylekit-info-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -297,6 +297,10 @@
|
|||||||
| {{ self.state.errorReportingEnabled ? 'Disable' : 'Enable'}} Error Reporting
|
| {{ self.state.errorReportingEnabled ? 'Disable' : 'Enable'}} Error Reporting
|
||||||
.sk-panel-row
|
.sk-panel-row
|
||||||
a(ng-click="self.openErrorReportingDialog()").sk-a What data is being sent?
|
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-footer
|
||||||
.sk-panel-row
|
.sk-panel-row
|
||||||
.sk-p.left.neutral
|
.sk-p.left.neutral
|
||||||
@@ -311,7 +315,7 @@
|
|||||||
)
|
)
|
||||||
| Cancel
|
| Cancel
|
||||||
a.sk-a.right.danger.capitalize(
|
a.sk-a.right.danger.capitalize(
|
||||||
ng-click='self.destroyLocalData()',
|
ng-click='self.signOut()',
|
||||||
ng-if=`
|
ng-if=`
|
||||||
!self.state.formData.showLogin &&
|
!self.state.formData.showLogin &&
|
||||||
!self.state.formData.showRegister`
|
!self.state.formData.showRegister`
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
"@standardnotes/sncrypto-web": "^1.2.10",
|
"@standardnotes/sncrypto-web": "^1.2.10",
|
||||||
"@standardnotes/snjs": "^2.0.74",
|
"@standardnotes/snjs": "^2.0.74",
|
||||||
"mobx": "^6.1.6",
|
"mobx": "^6.1.6",
|
||||||
|
"mobx-react-lite": "^3.2.0",
|
||||||
"preact": "^10.5.12"
|
"preact": "^10.5.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6094,6 +6094,11 @@ mixin-deep@^1.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
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:
|
mobx@^6.1.6:
|
||||||
version "6.1.6"
|
version "6.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.1.6.tgz#ae75e57ec07d190ed187273864002163fa357224"
|
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.1.6.tgz#ae75e57ec07d190ed187273864002163fa357224"
|
||||||
|
|||||||
Reference in New Issue
Block a user