diff --git a/.eslintrc b/.eslintrc index 657cd2c81..0f893399a 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,11 +1,10 @@ { - "extends": ["eslint:recommended", "semistandard", "prettier"], - "parser": "babel-eslint", + "extends": ["eslint:recommended", "prettier"], + "parser": "@typescript-eslint/parser", "rules": { - "standard/no-callback-literal": 0, // Disable this as we have too many callbacks relying on literals - "no-throw-literal": 0, - // "no-console": "error", - "semi": 1 + "standard/no-callback-literal": "off", // Disable this as we have too many callbacks relying on literals + "no-throw-literal": "off", + "camelcase": "off" }, "env": { "browser": true diff --git a/app/assets/javascripts/directives/views/accountMenu.ts b/app/assets/javascripts/directives/views/accountMenu.ts index 21ed1a884..17ddd2cf0 100644 --- a/app/assets/javascripts/directives/views/accountMenu.ts +++ b/app/assets/javascripts/directives/views/accountMenu.ts @@ -1,7 +1,6 @@ import { WebDirective } from './../../types'; import { isDesktopApplication, preventRefreshing } from '@/utils'; import template from '%/directives/account-menu.pug'; -import { ProtectedAction, ContentType } from '@standardnotes/snjs'; import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl'; import { STRING_ACCOUNT_MENU_UNCHECK_MERGE, @@ -10,8 +9,6 @@ import { STRING_LOCAL_ENC_ENABLED, STRING_ENC_NOT_ENABLED, STRING_IMPORT_SUCCESS, - STRING_REMOVE_PASSCODE_CONFIRMATION, - STRING_REMOVE_PASSCODE_OFFLINE_ADDENDUM, STRING_NON_MATCHING_PASSCODES, STRING_NON_MATCHING_PASSWORDS, STRING_INVALID_IMPORT_FILE, @@ -23,7 +20,7 @@ import { STRING_UNSUPPORTED_BACKUP_FILE_VERSION } from '@/strings'; import { PasswordWizardType } from '@/types'; -import { BackupFile } from '@standardnotes/snjs'; +import { BackupFile, ContentType } from '@standardnotes/snjs'; import { confirmDialog, alertDialog } from '@/services/alertService'; import { autorun, IReactionDisposer } from 'mobx'; import { storage, StorageKey } from '@/services/localStorage'; @@ -35,22 +32,22 @@ const ELEMENT_NAME_AUTH_PASSWORD = 'password'; const ELEMENT_NAME_AUTH_PASSWORD_CONF = 'password_conf'; type FormData = { - email: string - user_password: string - password_conf: string - confirmPassword: boolean - showLogin: boolean - showRegister: boolean - showPasscodeForm: boolean - strictSignin?: boolean - ephemeral: boolean - mergeLocal?: boolean - url: string - authenticating: boolean - status: string - passcode: string - confirmPasscode: string - changingPasscode: boolean + email: string; + user_password: string; + password_conf: string; + confirmPassword: boolean; + showLogin: boolean; + showRegister: boolean; + showPasscodeForm: boolean; + strictSignin?: boolean; + ephemeral: boolean; + mergeLocal?: boolean; + url: string; + authenticating: boolean; + status: string; + passcode: string; + confirmPasscode: string; + changingPasscode: boolean; } type AccountMenuState = { @@ -72,7 +69,7 @@ type AccountMenuState = { showSessions: boolean; } -class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> { +class AccountMenuCtrl extends PureViewCtrl { public appVersion: string /** @template */ @@ -331,26 +328,6 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> { this.appState.openSessionsModal(); } - async openPrivilegesModal() { - const run = () => { - this.application!.presentPrivilegesManagementModal(); - this.close(); - }; - const needsPrivilege = await this.application!.privilegesService!.actionRequiresPrivilege( - ProtectedAction.ManagePrivileges - ); - if (needsPrivilege) { - this.application!.presentPrivilegesModal( - ProtectedAction.ManagePrivileges, - () => { - run(); - } - ); - } else { - run(); - } - } - async destroyLocalData() { if (await confirmDialog({ text: STRING_SIGN_OUT_CONFIRMATION, @@ -394,56 +371,46 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> { * @template */ async importFileSelected(files: File[]) { - const run = async () => { - const file = files[0]; - const data = await this.readFile(file); - if (!data) { + const file = files[0]; + const data = await this.readFile(file); + if (!data) { + return; + } + if (data.version || data.auth_params || data.keyParams) { + const version = data.version || data.keyParams?.version || data.auth_params?.version; + if ( + !this.application!.protocolService!.supportedVersions().includes(version) + ) { + await this.setState({ importData: null }); + alertDialog({ text: STRING_UNSUPPORTED_BACKUP_FILE_VERSION }); return; } - if (data.version || data.auth_params || data.keyParams) { - const version = data.version || data.keyParams?.version || data.auth_params?.version; - if ( - !this.application!.protocolService!.supportedVersions().includes(version) - ) { - await this.setState({ importData: null }); - alertDialog({ text: STRING_UNSUPPORTED_BACKUP_FILE_VERSION }); - return; - } - if (data.keyParams || data.auth_params) { - await this.setState({ - importData: { - ...this.getState().importData, - requestPassword: true, - data, - } - }); - const element = document.getElementById( - ELEMENT_ID_IMPORT_PASSWORD_INPUT - ); - if (element) { - element.scrollIntoView(false); + if (data.keyParams || data.auth_params) { + await this.setState({ + importData: { + ...this.getState().importData, + requestPassword: true, + data, } - } else { - await this.performImport(data, undefined); + }); + const element = document.getElementById( + ELEMENT_ID_IMPORT_PASSWORD_INPUT + ); + if (element) { + element.scrollIntoView(false); } } else { await this.performImport(data, undefined); } - }; - const needsPrivilege = await this.application!.privilegesService!.actionRequiresPrivilege( - ProtectedAction.ManageBackups - ); - if (needsPrivilege) { - this.application!.presentPrivilegesModal( - ProtectedAction.ManageBackups, - run - ); } else { - run(); + await this.performImport(data, undefined); } } async performImport(data: BackupFile, password?: string) { + if (!(await this.application.authorizeFileImport())) { + return; + } await this.setState({ importData: { ...this.getState().importData, @@ -499,23 +466,11 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> { } async selectAutoLockInterval(interval: number) { - const run = async () => { - await this.application!.getAutolockService().setAutoLockInterval(interval); - this.reloadAutoLockInterval(); - }; - const needsPrivilege = await this.application!.privilegesService!.actionRequiresPrivilege( - ProtectedAction.ManagePasscode - ); - if (needsPrivilege) { - this.application!.presentPrivilegesModal( - ProtectedAction.ManagePasscode, - () => { - run(); - } - ); - } else { - run(); + if (!(await this.application.authorizeAutolockIntervalChange())) { + return; } + await this.application!.getAutolockService().setAutoLockInterval(interval); + this.reloadAutoLockInterval(); } hidePasswordForm() { @@ -562,53 +517,18 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> { } async changePasscodePressed() { - const run = () => { - this.getState().formData.changingPasscode = true; - this.addPasscodeClicked(); - }; - const needsPrivilege = await this.application!.privilegesService!.actionRequiresPrivilege( - ProtectedAction.ManagePasscode - ); - if (needsPrivilege) { - this.application!.presentPrivilegesModal( - ProtectedAction.ManagePasscode, - run - ); - } else { - run(); - } + this.getState().formData.changingPasscode = true; + this.addPasscodeClicked(); } async removePasscodePressed() { - const run = async () => { - const signedIn = this.application!.hasAccount(); - let message = STRING_REMOVE_PASSCODE_CONFIRMATION; - if (!signedIn) { - message += STRING_REMOVE_PASSCODE_OFFLINE_ADDENDUM; - } - if (await confirmDialog({ - text: message, - confirmButtonStyle: 'danger' - })) { - await preventRefreshing(STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_REMOVAL, async () => { - await this.application.getAutolockService().deleteAutolockPreference(); - await this.application!.removePasscode(); - await this.reloadAutoLockInterval(); - }); + await preventRefreshing(STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_REMOVAL, async () => { + if (await this.application!.removePasscode()) { + await this.application.getAutolockService().deleteAutolockPreference(); + await this.reloadAutoLockInterval(); this.refreshEncryptionStatus(); } - }; - const needsPrivilege = await this.application!.privilegesService!.actionRequiresPrivilege( - ProtectedAction.ManagePasscode - ); - if (needsPrivilege) { - this.application!.presentPrivilegesModal( - ProtectedAction.ManagePasscode, - run - ); - } else { - run(); - } + }); } openErrorReportingDialog() { diff --git a/app/assets/javascripts/services/desktopManager.ts b/app/assets/javascripts/services/desktopManager.ts index baae0aca2..057c20c3c 100644 --- a/app/assets/javascripts/services/desktopManager.ts +++ b/app/assets/javascripts/services/desktopManager.ts @@ -1,9 +1,17 @@ -import { SNComponent, PurePayload, ComponentMutator, AppDataField, ContentType } from '@standardnotes/snjs'; +import { + SNComponent, + PurePayload, + ComponentMutator, + AppDataField, + EncryptionIntent, + ApplicationService, + ApplicationEvent, + removeFromArray, +} from '@standardnotes/snjs'; /* eslint-disable camelcase */ import { WebApplication } from '@/ui_models/application'; // An interface used by the Desktop app to interact with SN import { isDesktopApplication } from '@/utils'; -import { EncryptionIntent, ApplicationService, ApplicationEvent, removeFromArray } from '@standardnotes/snjs'; import { Bridge } from './bridge'; type UpdateObserverCallback = (component: SNComponent) => void @@ -20,7 +28,8 @@ export class DesktopManager extends ApplicationService { componentActivationObservers: ComponentActivationObserver[] = [] updateObservers: { callback: UpdateObserverCallback - }[] = [] + }[] = []; + isDesktop = isDesktopApplication(); dataLoaded = false diff --git a/app/assets/javascripts/ui_models/app_state.ts b/app/assets/javascripts/ui_models/app_state.ts index b15105d20..e973f48f8 100644 --- a/app/assets/javascripts/ui_models/app_state.ts +++ b/app/assets/javascripts/ui_models/app_state.ts @@ -1,13 +1,10 @@ import { isDesktopApplication, isDev } from '@/utils'; import pull from 'lodash/pull'; import { - ProtectedAction, ApplicationEvent, SNTag, SNNote, - SNUserPrefs, ContentType, - SNSmartTag, PayloadSource, DeinitSource, UuidString, @@ -231,8 +228,7 @@ export class AppState { return; }; - const approved = this.application.authorizeNoteAccess(note); - if (approved === true || await approved) { + if (await this.application.authorizeNoteAccess(note)) { const activeEditor = this.getActiveEditor(); if (!activeEditor) { this.application.editorGroup.createEditor(noteUuid); diff --git a/app/assets/javascripts/ui_models/application.ts b/app/assets/javascripts/ui_models/application.ts index 79f628799..76dadcc68 100644 --- a/app/assets/javascripts/ui_models/application.ts +++ b/app/assets/javascripts/ui_models/application.ts @@ -1,4 +1,3 @@ -import { PermissionDialog } from '@standardnotes/snjs'; import { ComponentModalScope } from './../directives/views/componentModal'; import { AccountSwitcherScope, PermissionsModalScope } from './../types'; import { ComponentGroup } from './component_group'; @@ -9,7 +8,9 @@ import { SNApplication, platformFromString, Challenge, - ProtectedAction, SNComponent + SNComponent, + PermissionDialog, + DeinitSource, } from '@standardnotes/snjs'; import angular from 'angular'; import { getPlatformString } from '@/utils'; @@ -27,17 +28,16 @@ import { import { AppState } from '@/ui_models/app_state'; import { SNWebCrypto } from '@standardnotes/sncrypto-web'; import { Bridge } from '@/services/bridge'; -import { DeinitSource } from '@standardnotes/snjs'; type WebServices = { - appState: AppState - desktopService: DesktopManager - autolockService: AutolockService - archiveService: ArchiveManager - nativeExtService: NativeExtManager - statusManager: StatusManager - themeService: ThemeManager - keyboardService: KeyboardManager + appState: AppState; + desktopService: DesktopManager; + autolockService: AutolockService; + archiveService: ArchiveManager; + nativeExtService: NativeExtManager; + statusManager: StatusManager; + themeService: ThemeManager; + keyboardService: KeyboardManager; } export class WebApplication extends SNApplication { @@ -78,7 +78,7 @@ export class WebApplication extends SNApplication { } /** @override */ - deinit(source: DeinitSource) { + deinit(source: DeinitSource): void { for (const service of Object.values(this.webServices)) { if ('deinit' in service) { service.deinit?.(source); @@ -107,15 +107,15 @@ export class WebApplication extends SNApplication { this.componentManager!.presentPermissionsDialog = this.presentPermissionsDialog; } - setWebServices(services: WebServices) { + setWebServices(services: WebServices): void { this.webServices = services; } - public getAppState() { + public getAppState(): AppState { return this.webServices.appState; } - public getDesktopService() { + public getDesktopService(): DesktopManager { return this.webServices.desktopService; } @@ -170,46 +170,6 @@ export class WebApplication extends SNApplication { this.applicationElement.append(el); } - async presentPrivilegesModal( - action: ProtectedAction, - onSuccess?: any, - onCancel?: any - ) { - if (this.authenticationInProgress()) { - onCancel && onCancel(); - return; - } - - const customSuccess = async () => { - onSuccess && await onSuccess(); - this.currentAuthenticationElement = undefined; - }; - const customCancel = async () => { - onCancel && await onCancel(); - this.currentAuthenticationElement = undefined; - }; - - const scope: any = this.scope!.$new(true); - scope.action = action; - scope.onSuccess = customSuccess; - scope.onCancel = customCancel; - scope.application = this; - const el = this.$compile!(` - - `)(scope); - this.applicationElement.append(el); - - this.currentAuthenticationElement = el; - } - - presentPrivilegesManagementModal() { - const scope: any = this.scope!.$new(true); - scope.application = this; - const el = this.$compile!("")(scope); - this.applicationElement.append(el); - } - authenticationInProgress() { return this.currentAuthenticationElement != null; } diff --git a/app/assets/javascripts/views/challenge_modal/challenge-modal.pug b/app/assets/javascripts/views/challenge_modal/challenge-modal.pug index bb44ffbf9..6277aef49 100644 --- a/app/assets/javascripts/views/challenge_modal/challenge-modal.pug +++ b/app/assets/javascripts/views/challenge_modal/challenge-modal.pug @@ -12,7 +12,9 @@ | {{ctrl.challenge.subheading}} .sk-panel-section div(ng-repeat="prompt in ctrl.state.prompts track by prompt.id") - .sk-panel-row + .sk-panel-row( + ng-if="prompt.validation != ctrl.protectionsSessionValidation" + ) input.sk-input.contrast( ng-model="ctrl.state.values[prompt.id].value" should-focus="$index == 0" @@ -21,7 +23,17 @@ ng-change="ctrl.onTextValueChange(prompt)" ng-attr-type="{{prompt.secureTextEntry ? 'password' : 'text'}}", ng-attr-placeholder="{{prompt.title}}" - ) + ) + .sk-horizontal-group( + ng-if="prompt.validation == ctrl.protectionsSessionValidation" + ) + .sk-p.sk-bold Remember For + a.sk-a.info( + ng-repeat="option in ctrl.protectionsSessionDurations" + ng-class="{'boxed' : option.valueInSeconds == ctrl.state.values[prompt.id].value}" + ng-click="ctrl.onValueChange(prompt, option.valueInSeconds);" + ) + | {{option.label}} .sk-panel-row.centered label.sk-label.danger( ng-if="ctrl.state.values[prompt.id].invalid" diff --git a/app/assets/javascripts/views/challenge_modal/challenge_modal.ts b/app/assets/javascripts/views/challenge_modal/challenge_modal.ts index a605dac9f..a69f88b33 100644 --- a/app/assets/javascripts/views/challenge_modal/challenge_modal.ts +++ b/app/assets/javascripts/views/challenge_modal/challenge_modal.ts @@ -5,45 +5,46 @@ import { removeFromArray, Challenge, ChallengeReason, - ChallengePrompt + ChallengePrompt, + ChallengeValidation, + ProtectionSessionDurations, } from '@standardnotes/snjs'; import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl'; import { WebDirective } from '@/types'; import { confirmDialog } from '@/services/alertService'; -import { - STRING_SIGN_OUT_CONFIRMATION, -} from '@/strings'; +import { STRING_SIGN_OUT_CONFIRMATION } from '@/strings'; type InputValue = { - prompt: ChallengePrompt - value: string - invalid: boolean -} + prompt: ChallengePrompt; + value: string | number | boolean; + invalid: boolean; +}; -type Values = Record +type Values = Record; type ChallengeModalState = { - prompts: ChallengePrompt[] - values: Partial - processing: boolean, - forgotPasscode: boolean, - showForgotPasscodeLink: boolean, - processingPrompts: ChallengePrompt[], - hasAccount: boolean, -} + prompts: ChallengePrompt[]; + values: Partial; + processing: boolean; + forgotPasscode: boolean; + showForgotPasscodeLink: boolean; + processingPrompts: ChallengePrompt[]; + hasAccount: boolean; + protectedNoteAccessDuration: number; +}; -class ChallengeModalCtrl extends PureViewCtrl<{}, ChallengeModalState> { - private $element: JQLite - application!: WebApplication - challenge!: Challenge +class ChallengeModalCtrl extends PureViewCtrl { + application!: WebApplication; + challenge!: Challenge; + + /** @template */ + protectionsSessionDurations = ProtectionSessionDurations; + protectionsSessionValidation = + ChallengeValidation.ProtectionSessionDuration; /* @ngInject */ - constructor( - $element: JQLite, - $timeout: ng.ITimeoutService - ) { + constructor(private $element: JQLite, $timeout: ng.ITimeoutService) { super($timeout); - this.$element = $element; } getState() { @@ -57,13 +58,13 @@ class ChallengeModalCtrl extends PureViewCtrl<{}, ChallengeModalState> { for (const prompt of prompts) { values[prompt.id] = { prompt, - value: '', - invalid: false + value: prompt.initialValue ?? '', + invalid: false, }; } const showForgotPasscodeLink = [ ChallengeReason.ApplicationUnlock, - ChallengeReason.Migration + ChallengeReason.Migration, ].includes(this.challenge.reason); this.setState({ prompts, @@ -72,34 +73,32 @@ class ChallengeModalCtrl extends PureViewCtrl<{}, ChallengeModalState> { forgotPasscode: false, showForgotPasscodeLink, hasAccount: this.application.hasAccount(), - processingPrompts: [] + processingPrompts: [], + protectedNoteAccessDuration: ProtectionSessionDurations[0].valueInSeconds, }); - this.application.addChallengeObserver( - this.challenge, - { - onValidValue: (value) => { - this.getState().values[value.prompt.id]!.invalid = false; + this.application.addChallengeObserver(this.challenge, { + onValidValue: (value) => { + this.getState().values[value.prompt.id]!.invalid = false; + removeFromArray(this.state.processingPrompts, value.prompt); + this.reloadProcessingStatus(); + }, + onInvalidValue: (value) => { + this.getState().values[value.prompt.id]!.invalid = true; + /** If custom validation, treat all values together and not individually */ + if (!value.prompt.validates) { + this.setState({ processingPrompts: [], processing: false }); + } else { removeFromArray(this.state.processingPrompts, value.prompt); this.reloadProcessingStatus(); - }, - onInvalidValue: (value) => { - this.getState().values[value.prompt.id]!.invalid = true; - /** If custom validation, treat all values together and not individually */ - if (!value.prompt.validates) { - this.setState({ processingPrompts: [], processing: false }); - } else { - removeFromArray(this.state.processingPrompts, value.prompt); - this.reloadProcessingStatus(); - } - }, - onComplete: () => { - this.dismiss(); - }, - onCancel: () => { - this.dismiss(); - }, - } - ); + } + }, + onComplete: () => { + this.dismiss(); + }, + onCancel: () => { + this.dismiss(); + }, + }); } deinit() { @@ -110,18 +109,20 @@ class ChallengeModalCtrl extends PureViewCtrl<{}, ChallengeModalState> { reloadProcessingStatus() { return this.setState({ - processing: this.state.processingPrompts.length > 0 + processing: this.state.processingPrompts.length > 0, }); } async destroyLocalData() { - if (await confirmDialog({ - text: STRING_SIGN_OUT_CONFIRMATION, - confirmButtonStyle: "danger" - })) { + if ( + await confirmDialog({ + text: STRING_SIGN_OUT_CONFIRMATION, + confirmButtonStyle: 'danger', + }) + ) { await this.application.signOut(); this.dismiss(); - }; + } } /** @template */ @@ -133,7 +134,7 @@ class ChallengeModalCtrl extends PureViewCtrl<{}, ChallengeModalState> { onForgotPasscodeClick() { this.setState({ - forgotPasscode: true + forgotPasscode: true, }); } @@ -143,15 +144,22 @@ class ChallengeModalCtrl extends PureViewCtrl<{}, ChallengeModalState> { this.setState({ values }); } + onValueChange(prompt: ChallengePrompt, value: number) { + const values = this.state.values; + values[prompt.id]!.invalid = false; + values[prompt.id]!.value = value; + } + validate() { - const failed = []; - for (const prompt of this.getState().prompts) { - const value = this.getState().values[prompt.id]; - if (!value || value.value.length === 0) { - this.getState().values[prompt.id]!.invalid = true; + let failed = 0; + for (const prompt of this.state.prompts) { + const value = this.state.values[prompt.id]!; + if (typeof value.value === 'string' && value.value.length === 0) { + this.state.values[prompt.id]!.invalid = true; + failed++; } } - return failed.length === 0; + return failed === 0; } async submit() { @@ -161,15 +169,15 @@ class ChallengeModalCtrl extends PureViewCtrl<{}, ChallengeModalState> { await this.setState({ processing: true }); const values: ChallengeValue[] = []; for (const inputValue of Object.values(this.getState().values)) { - const rawValue = inputValue!!.value; + const rawValue = inputValue!.value; const value = new ChallengeValue(inputValue!.prompt, rawValue); values.push(value); } const processingPrompts = values.map((v) => v.prompt); await this.setState({ processingPrompts: processingPrompts, - processing: processingPrompts.length > 0 - }) + processing: processingPrompts.length > 0, + }); /** * Unfortunately neccessary to wait 50ms so that the above setState call completely * updates the UI to change processing state, before we enter into UI blocking operation @@ -181,7 +189,7 @@ class ChallengeModalCtrl extends PureViewCtrl<{}, ChallengeModalState> { } else { this.setState({ processing: false }); } - }, 50) + }, 50); } dismiss() { @@ -202,7 +210,7 @@ export class ChallengeModal extends WebDirective { this.bindToController = true; this.scope = { challenge: '=', - application: '=' + application: '=', }; } } diff --git a/app/assets/javascripts/views/editor/editor_view.ts b/app/assets/javascripts/views/editor/editor_view.ts index b1c1d3c6c..d6ff57ee5 100644 --- a/app/assets/javascripts/views/editor/editor_view.ts +++ b/app/assets/javascripts/views/editor/editor_view.ts @@ -8,7 +8,6 @@ import { isPayloadSourceRetrieved, isPayloadSourceInternalChange, ContentType, - ProtectedAction, SNComponent, SNNote, SNTag, @@ -24,7 +23,7 @@ import { isDesktopApplication } from '@/utils'; import { KeyboardModifier, KeyboardKey } from '@/services/keyboardManager'; import template from './editor-view.pug'; import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl'; -import { AppStateEvent, EventSource } from '@/ui_models/app_state'; +import { EventSource } from '@/ui_models/app_state'; import { STRING_DELETED_NOTE, STRING_INVALID_NOTE, @@ -85,7 +84,7 @@ type EditorState = { * then re-initialized. Used when reloading spellcheck status. */ textareaUnloading: boolean /** Fields that can be directly mutated by the template */ - mutable: {} + mutable: any } type EditorValues = { @@ -98,7 +97,7 @@ function sortAlphabetically(array: SNComponent[]): SNComponent[] { return array.sort((a, b) => a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1); } -class EditorViewCtrl extends PureViewCtrl<{}, EditorState> { +class EditorViewCtrl extends PureViewCtrl { /** Passed through template */ readonly application!: WebApplication readonly editor!: Editor @@ -248,7 +247,7 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> { case ApplicationEvent.HighLatencySync: this.setState({ syncTakingTooLong: true }); break; - case ApplicationEvent.CompletedFullSync: + case ApplicationEvent.CompletedFullSync: { this.setState({ syncTakingTooLong: false }); const isInErrorState = this.state.saveError; /** if we're still dirty, don't change status, a sync is likely upcoming. */ @@ -256,6 +255,7 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> { this.showAllChangesSavedStatus(); } break; + } case ApplicationEvent.FailedSync: /** * Only show error status in editor if the note is dirty. @@ -601,10 +601,12 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> { this.setMenuState('showOptionsMenu', false); } + // eslint-disable-next-line @typescript-eslint/no-empty-function onTitleFocus() { } + // eslint-disable-next-line @typescript-eslint/no-empty-function onTitleBlur() { } @@ -627,50 +629,35 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> { ); return; } - const run = async () => { - if (this.note.locked) { - this.application.alertService!.alert( - STRING_DELETE_LOCKED_ATTEMPT - ); - return; - } - const title = this.note.safeTitle().length - ? `'${this.note.title}'` - : "this note"; - const text = StringDeleteNote( - title, - permanently + if (this.note.locked) { + this.application.alertService!.alert( + STRING_DELETE_LOCKED_ATTEMPT ); - if (await confirmDialog({ - text, - confirmButtonStyle: 'danger' - })) { - if (permanently) { - this.performNoteDeletion(this.note); - } else { - this.saveNote( - true, - false, - true, - (mutator) => { - mutator.trashed = true; - } - ); - } - }; - }; - const requiresPrivilege = await this.application.privilegesService!.actionRequiresPrivilege( - ProtectedAction.DeleteNote + return; + } + const title = this.note.safeTitle().length + ? `'${this.note.title}'` + : "this note"; + const text = StringDeleteNote( + title, + permanently ); - if (requiresPrivilege) { - this.application.presentPrivilegesModal( - ProtectedAction.DeleteNote, - () => { - run(); - } - ); - } else { - run(); + if (await confirmDialog({ + text, + confirmButtonStyle: 'danger' + })) { + if (permanently) { + this.performNoteDeletion(this.note); + } else { + this.saveNote( + true, + false, + true, + (mutator) => { + mutator.trashed = true; + } + ); + } } } diff --git a/app/assets/javascripts/views/footer/footer_view.ts b/app/assets/javascripts/views/footer/footer_view.ts index b6491d070..1e029617a 100644 --- a/app/assets/javascripts/views/footer/footer_view.ts +++ b/app/assets/javascripts/views/footer/footer_view.ts @@ -5,7 +5,6 @@ import { dateToLocalizedString, preventRefreshing } from '@/utils'; import { ApplicationEvent, SyncQueueStrategy, - ProtectedAction, ContentType, SNComponent, SNTheme, @@ -44,7 +43,7 @@ type DockShortcut = { } } -class FooterViewCtrl extends PureViewCtrl<{}, { +class FooterViewCtrl extends PureViewCtrl { - this.$timeout(() => { - this.roomShowState[room.uuid] = !this.roomShowState[room.uuid]; - }); - }; - - if (!this.roomShowState[room.uuid]) { - const requiresPrivilege = await this.application.privilegesService! - .actionRequiresPrivilege( - ProtectedAction.ManageExtensions - ); - if (requiresPrivilege) { - this.application.presentPrivilegesModal( - ProtectedAction.ManageExtensions, - run - ); - } else { - run(); - } - } else { - run(); - } + this.$timeout(() => { + this.roomShowState[room.uuid] = !this.roomShowState[room.uuid]; + }); } displayBetaDialog() { diff --git a/app/assets/templates/directives/account-menu.pug b/app/assets/templates/directives/account-menu.pug index 6c3572309..1cc5892d0 100644 --- a/app/assets/templates/directives/account-menu.pug +++ b/app/assets/templates/directives/account-menu.pug @@ -168,10 +168,6 @@ ng-click="self.openSessionsModal()" ng-if="self.state.showSessions" ) Manage Sessions - a.sk-a.info.sk-panel-row.condensed( - ng-click="self.openPrivilegesModal('')", - ng-show='self.state.user' - ) Manage Privileges .sk-panel-section .sk-panel-section-title Encryption .sk-panel-section-subtitle.info(ng-if='self.state.encryptionEnabled') diff --git a/package.json b/package.json index 1659fd071..805665532 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,8 @@ "@types/mocha": "^7.0.2", "@types/pug": "^2.0.4", "@types/react": "^17.0.0", - "@typescript-eslint/eslint-plugin": "^2.23.0", - "@typescript-eslint/parser": "^2.23.0", + "@typescript-eslint/eslint-plugin": "^3.10.1", + "@typescript-eslint/parser": "^3.10.1", "angular": "^1.8.2", "apply-loader": "^2.0.0", "babel-eslint": "^10.1.0", @@ -71,9 +71,12 @@ "@reach/alert-dialog": "^0.12.1", "@reach/dialog": "^0.12.1", "@standardnotes/sncrypto-web": "^1.2.9", - "@standardnotes/snjs": "^2.0.35", + "@standardnotes/snjs": "^2.0.41", "babel-loader": "^8.2.2", "mobx": "^6.0.4", "preact": "^10.5.7" + }, + "peerDependencies": { + "react": "^16.8.0" } } diff --git a/yarn.lock b/yarn.lock index 556ffb807..af8099b2f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1045,10 +1045,10 @@ "@standardnotes/sncrypto-common" "^1.2.7" libsodium-wrappers "^0.7.8" -"@standardnotes/snjs@^2.0.35": - version "2.0.35" - resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.0.35.tgz#9e9c3058ebbfc9af7a5fc3ae18497f02a9b1e71b" - integrity sha512-uA4HXorgiV8yFGN1dtO52istUudnc3ZzEQiFLgf0bkKvA0wH3Xt+R9bBviYAdIBkTBKHCyGsBdsCiKr1QS/X9g== +"@standardnotes/snjs@^2.0.41": + version "2.0.41" + resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.0.41.tgz#131f3a206b220be5359c43963c47b1929b92f037" + integrity sha512-/6hoBcb/Ib8voqwk1YLVTAM6L2ZJV/AZDXJz/oEKY9cjO0onFMbl0T+AAWEal1d6zojyjC4Wv6Pc9fkNInvBSQ== dependencies: "@standardnotes/sncrypto-common" "^1.2.9" @@ -1188,49 +1188,66 @@ "@types/webpack-sources" "*" source-map "^0.6.0" -"@typescript-eslint/eslint-plugin@^2.23.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9" - integrity sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ== +"@typescript-eslint/eslint-plugin@^3.10.1": + version "3.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.10.1.tgz#7e061338a1383f59edc204c605899f93dc2e2c8f" + integrity sha512-PQg0emRtzZFWq6PxBcdxRH3QIQiyFO3WCVpRL3fgj5oQS3CDs3AeAKfv4DxNhzn8ITdNJGJ4D3Qw8eAJf3lXeQ== dependencies: - "@typescript-eslint/experimental-utils" "2.34.0" + "@typescript-eslint/experimental-utils" "3.10.1" + debug "^4.1.1" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" + semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@2.34.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" - integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== +"@typescript-eslint/experimental-utils@3.10.1": + version "3.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz#e179ffc81a80ebcae2ea04e0332f8b251345a686" + integrity sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.34.0" + "@typescript-eslint/types" "3.10.1" + "@typescript-eslint/typescript-estree" "3.10.1" eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@^2.23.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.34.0.tgz#50252630ca319685420e9a39ca05fe185a256bc8" - integrity sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA== +"@typescript-eslint/parser@^3.10.1": + version "3.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.10.1.tgz#1883858e83e8b442627e1ac6f408925211155467" + integrity sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.34.0" - "@typescript-eslint/typescript-estree" "2.34.0" + "@typescript-eslint/experimental-utils" "3.10.1" + "@typescript-eslint/types" "3.10.1" + "@typescript-eslint/typescript-estree" "3.10.1" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/typescript-estree@2.34.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" - integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== +"@typescript-eslint/types@3.10.1": + version "3.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727" + integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ== + +"@typescript-eslint/typescript-estree@3.10.1": + version "3.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853" + integrity sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w== dependencies: + "@typescript-eslint/types" "3.10.1" + "@typescript-eslint/visitor-keys" "3.10.1" debug "^4.1.1" - eslint-visitor-keys "^1.1.0" glob "^7.1.6" is-glob "^4.0.1" lodash "^4.17.15" semver "^7.3.2" tsutils "^3.17.1" +"@typescript-eslint/visitor-keys@3.10.1": + version "3.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz#cd4274773e3eb63b2e870ac602274487ecd1e931" + integrity sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ== + dependencies: + eslint-visitor-keys "^1.1.0" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"