diff --git a/app/assets/javascripts/directives/views/accountMenu.js b/app/assets/javascripts/directives/views/accountMenu.js deleted file mode 100644 index a14389ed65f..00000000000 --- a/app/assets/javascripts/directives/views/accountMenu.js +++ /dev/null @@ -1,622 +0,0 @@ -import { isDesktopApplication, isNullOrUndefined } from '@/utils'; -import { PrivilegesManager } from '@/services/privilegesManager'; -import template from '%/directives/account-menu.pug'; -import { protocolManager } from 'snjs'; -import { PureCtrl } from '@Controllers'; -import { - STRING_ACCOUNT_MENU_UNCHECK_MERGE, - STRING_SIGN_OUT_CONFIRMATION, - STRING_ERROR_DECRYPTING_IMPORT, - STRING_E2E_ENABLED, - 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, - STRING_GENERATING_LOGIN_KEYS, - STRING_GENERATING_REGISTER_KEYS, - StringImportError -} from '@/strings'; - -const ELEMENT_ID_IMPORT_PASSWORD_INPUT = 'import-password-request'; - -const ELEMENT_NAME_AUTH_EMAIL = 'email'; -const ELEMENT_NAME_AUTH_PASSWORD = 'password'; -const ELEMENT_NAME_AUTH_PASSWORD_CONF = 'password_conf'; - -class AccountMenuCtrl extends PureCtrl { - /* @ngInject */ - constructor( - $scope, - $rootScope, - $timeout, - alertManager, - archiveManager, - appVersion, - authManager, - modelManager, - passcodeManager, - privilegesManager, - storageManager, - syncManager, - ) { - super($timeout); - this.$scope = $scope; - this.$rootScope = $rootScope; - this.$timeout = $timeout; - this.alertManager = alertManager; - this.archiveManager = archiveManager; - this.authManager = authManager; - this.modelManager = modelManager; - this.passcodeManager = passcodeManager; - this.privilegesManager = privilegesManager; - this.storageManager = storageManager; - this.syncManager = syncManager; - - this.state = { - appVersion: 'v' + (window.electronAppVersion || appVersion), - user: this.authManager.user, - canAddPasscode: !this.authManager.isEphemeralSession(), - passcodeAutoLockOptions: this.passcodeManager.getAutoLockIntervalOptions(), - formData: { - mergeLocal: true, - ephemeral: false - }, - mutable: { - backupEncrypted: this.encryptedBackupsAvailable() - } - }; - - this.syncStatus = this.syncManager.syncStatus; - this.syncManager.getServerURL().then((url) => { - this.setState({ - server: url, - formData: { ...this.state.formData, url: url } - }); - }); - this.authManager.checkForSecurityUpdate().then((available) => { - this.setState({ - securityUpdateAvailable: available - }); - }); - this.reloadAutoLockInterval(); - } - - $onInit() { - this.initProps({ - closeFunction: this.closeFunction - }); - } - - close() { - this.$timeout(() => { - this.props.closeFunction()(); - }); - } - - encryptedBackupsAvailable() { - return !isNullOrUndefined(this.authManager.user) || this.passcodeManager.hasPasscode(); - } - - submitMfaForm() { - const params = { - [this.state.formData.mfa.payload.mfa_key]: this.state.formData.userMfaCode - }; - this.login(params); - } - - blurAuthFields() { - const names = [ - ELEMENT_NAME_AUTH_EMAIL, - ELEMENT_NAME_AUTH_PASSWORD, - ELEMENT_NAME_AUTH_PASSWORD_CONF - ]; - for(const name of names) { - const element = document.getElementsByName(name)[0]; - if(element) { - element.blur(); - } - } - } - - submitAuthForm() { - if (!this.state.formData.email || !this.state.formData.user_password) { - return; - } - this.blurAuthFields(); - if (this.state.formData.showLogin) { - this.login(); - } else { - this.register(); - } - } - - async setFormDataState(formData) { - return this.setState({ - formData: { - ...this.state.formData, - ...formData - } - }); - } - - async login(extraParams) { - /** Prevent a timed sync from occuring while signing in. */ - this.syncManager.lockSyncing(); - await this.setFormDataState({ - status: STRING_GENERATING_LOGIN_KEYS, - authenticating: true - }); - const response = await this.authManager.login( - this.state.formData.url, - this.state.formData.email, - this.state.formData.user_password, - this.state.formData.ephemeral, - this.state.formData.strictSignin, - extraParams - ); - const hasError = !response || response.error; - if (!hasError) { - this.setFormDataState({ - user_password: null - }); - await this.onAuthSuccess(); - this.syncManager.unlockSyncing(); - this.syncManager.sync({ performIntegrityCheck: true }); - return; - } - this.syncManager.unlockSyncing(); - await this.setFormDataState({ - status: null - }); - const error = response - ? response.error - : { message: "An unknown error occured." }; - - if (error.tag === 'mfa-required' || error.tag === 'mfa-invalid') { - await this.setFormDataState({ - showLogin: false, - mfa: error - }); - } else { - await this.setFormDataState({ - showLogin: true, - mfa: null - }); - if (error.message) { - this.alertManager.alert({ - text: error.message - }); - } - } - await this.setFormDataState({ - authenticating: false, - }); - } - - async register() { - const confirmation = this.state.formData.password_conf; - if (confirmation !== this.state.formData.user_password) { - this.alertManager.alert({ - text: STRING_NON_MATCHING_PASSWORDS - }); - return; - } - await this.setFormDataState({ - confirmPassword: false, - status: STRING_GENERATING_REGISTER_KEYS, - authenticating: true - }); - const response = await this.authManager.register( - this.state.formData.url, - this.state.formData.email, - this.state.formData.user_password, - this.state.formData.ephemeral - ); - if (!response || response.error) { - await this.setFormDataState({ - status: null - }); - const error = response - ? response.error - : { message: "An unknown error occured." }; - await this.setFormDataState({ - authenticating: false - }); - this.alertManager.alert({ - text: error.message - }); - } else { - await this.onAuthSuccess(); - this.syncManager.sync(); - } - } - - mergeLocalChanged() { - if (!this.state.formData.mergeLocal) { - this.alertManager.confirm({ - text: STRING_ACCOUNT_MENU_UNCHECK_MERGE, - destructive: true, - onCancel: () => { - this.setFormDataState({ - mergeLocal: true - }); - } - }); - } - } - - async onAuthSuccess() { - if (this.state.formData.mergeLocal) { - this.$rootScope.$broadcast('major-data-change'); - await this.clearDatabaseAndRewriteAllItems({ alternateUuids: true }); - } else { - this.modelManager.removeAllItemsFromMemory(); - await this.storageManager.clearAllModels(); - } - await this.setFormDataState({ - authenticating: false - }); - this.syncManager.refreshErroredItems(); - this.close(); - } - - openPasswordWizard(type) { - this.close(); - this.authManager.presentPasswordWizard(type); - } - - async openPrivilegesModal() { - this.close(); - const run = () => { - this.privilegesManager.presentPrivilegesManagementModal(); - }; - const needsPrivilege = await this.privilegesManager.actionRequiresPrivilege( - PrivilegesManager.ActionManagePrivileges - ); - if (needsPrivilege) { - this.privilegesManager.presentPrivilegesModal( - PrivilegesManager.ActionManagePrivileges, - () => { - run(); - } - ); - } else { - run(); - } - } - - /** - * Allows IndexedDB unencrypted logs to be deleted - * `clearAllModels` will remove data from backing store, - * but not from working memory See: - * https://github.com/standardnotes/desktop/issues/131 - */ - async clearDatabaseAndRewriteAllItems({ alternateUuids } = {}) { - await this.storageManager.clearAllModels(); - await this.syncManager.markAllItemsDirtyAndSaveOffline(alternateUuids); - } - - destroyLocalData() { - this.alertManager.confirm({ - text: STRING_SIGN_OUT_CONFIRMATION, - destructive: true, - onConfirm: async () => { - await this.authManager.signout(true); - window.location.reload(); - } - }); - } - - async submitImportPassword() { - await this.performImport( - this.state.importData.data, - this.state.importData.password - ); - } - - async readFile(file) { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = function (e) { - try { - const data = JSON.parse(e.target.result); - resolve(data); - } catch (e) { - this.alertManager.alert({ - text: STRING_INVALID_IMPORT_FILE - }); - } - }; - reader.readAsText(file); - }); - } - - /** - * @template - */ - async importFileSelected(files) { - const run = async () => { - const file = files[0]; - const data = await this.readFile(file); - if (!data) { - return; - } - if (data.auth_params) { - await this.setState({ - importData: { - ...this.state.importData, - requestPassword: true, - data: data - } - }); - const element = document.getElementById( - ELEMENT_ID_IMPORT_PASSWORD_INPUT - ); - if (element) { - element.scrollIntoView(false); - } - } else { - await this.performImport(data, null); - } - }; - const needsPrivilege = await this.privilegesManager.actionRequiresPrivilege( - PrivilegesManager.ActionManageBackups - ); - if (needsPrivilege) { - this.privilegesManager.presentPrivilegesModal( - PrivilegesManager.ActionManageBackups, - run - ); - } else { - run(); - } - } - - async performImport(data, password) { - await this.setState({ - importData: { - ...this.state.importData, - loading: true - } - }); - const errorCount = await this.importJSONData(data, password); - this.setState({ - importData: null - }); - if (errorCount > 0) { - const message = StringImportError({ errorCount: errorCount }); - this.alertManager.alert({ - text: message - }); - } else { - this.alertManager.alert({ - text: STRING_IMPORT_SUCCESS - }); - } - } - - async importJSONData(data, password) { - let errorCount = 0; - if (data.auth_params) { - const keys = await protocolManager.computeEncryptionKeysForUser( - password, - data.auth_params - ); - try { - const throws = false; - await protocolManager.decryptMultipleItems(data.items, keys, throws); - const items = []; - for (const item of data.items) { - item.enc_item_key = null; - item.auth_hash = null; - if (item.errorDecrypting) { - errorCount++; - } else { - items.push(item); - } - } - data.items = items; - } catch (e) { - this.alertManager.alert({ - text: STRING_ERROR_DECRYPTING_IMPORT - }); - return; - } - } - - const items = await this.modelManager.importItems(data.items); - for (const item of items) { - /** - * Don't want to activate any components during import process in - * case of exceptions breaking up the import proccess - */ - if (item.content_type === 'SN|Component') { - item.active = false; - } - } - - this.syncManager.sync(); - return errorCount; - } - - async downloadDataArchive() { - this.archiveManager.downloadBackup(this.state.mutable.backupEncrypted); - this.close(); - } - - notesAndTagsCount() { - return this.modelManager.allItemsMatchingTypes([ - 'Note', - 'Tag' - ]).length; - } - - encryptionStatusForNotes() { - const length = this.notesAndTagsCount(); - return length + "/" + length + " notes and tags encrypted"; - } - - encryptionEnabled() { - return this.passcodeManager.hasPasscode() || !this.authManager.offline(); - } - - encryptionSource() { - if (!this.authManager.offline()) { - return "Account keys"; - } else if (this.passcodeManager.hasPasscode()) { - return "Local Passcode"; - } else { - return null; - } - } - - encryptionStatusString() { - if (!this.authManager.offline()) { - return STRING_E2E_ENABLED; - } else if (this.passcodeManager.hasPasscode()) { - return STRING_LOCAL_ENC_ENABLED; - } else { - return STRING_ENC_NOT_ENABLED; - } - } - - async reloadAutoLockInterval() { - const interval = await this.passcodeManager.getAutoLockInterval(); - this.setState({ - selectedAutoLockInterval: interval - }); - } - - async selectAutoLockInterval(interval) { - const run = async () => { - await this.passcodeManager.setAutoLockInterval(interval); - this.reloadAutoLockInterval(); - }; - const needsPrivilege = await this.privilegesManager.actionRequiresPrivilege( - PrivilegesManager.ActionManagePasscode - ); - if (needsPrivilege) { - this.privilegesManager.presentPrivilegesModal( - PrivilegesManager.ActionManagePasscode, - () => { - run(); - } - ); - } else { - run(); - } - } - - hidePasswordForm() { - this.setFormDataState({ - showLogin: false, - showRegister: false, - user_password: null, - password_conf: null - }); - } - - hasPasscode() { - return this.passcodeManager.hasPasscode(); - } - - addPasscodeClicked() { - this.setFormDataState({ - showPasscodeForm: true - }); - } - - submitPasscodeForm() { - const passcode = this.state.formData.passcode; - if (passcode !== this.state.formData.confirmPasscode) { - this.alertManager.alert({ - text: STRING_NON_MATCHING_PASSCODES - }); - return; - } - const func = this.state.formData.changingPasscode - ? this.passcodeManager.changePasscode.bind(this.passcodeManager) - : this.passcodeManager.setPasscode.bind(this.passcodeManager); - func(passcode, async () => { - await this.setFormDataState({ - passcode: null, - confirmPasscode: null, - showPasscodeForm: false - }); - if (await this.authManager.offline()) { - this.$rootScope.$broadcast('major-data-change'); - this.clearDatabaseAndRewriteAllItems(); - } - }); - } - - async changePasscodePressed() { - const run = () => { - this.state.formData.changingPasscode = true; - this.addPasscodeClicked(); - }; - const needsPrivilege = await this.privilegesManager.actionRequiresPrivilege( - PrivilegesManager.ActionManagePasscode - ); - if (needsPrivilege) { - this.privilegesManager.presentPrivilegesModal( - PrivilegesManager.ActionManagePasscode, - run - ); - } else { - run(); - } - } - - async removePasscodePressed() { - const run = () => { - const signedIn = !this.authManager.offline(); - let message = STRING_REMOVE_PASSCODE_CONFIRMATION; - if (!signedIn) { - message += STRING_REMOVE_PASSCODE_OFFLINE_ADDENDUM; - } - this.alertManager.confirm({ - text: message, - destructive: true, - onConfirm: () => { - this.passcodeManager.clearPasscode(); - if (this.authManager.offline()) { - this.syncManager.markAllItemsDirtyAndSaveOffline(); - } - } - }); - }; - const needsPrivilege = await this.privilegesManager.actionRequiresPrivilege( - PrivilegesManager.ActionManagePasscode - ); - if (needsPrivilege) { - this.privilegesManager.presentPrivilegesModal( - PrivilegesManager.ActionManagePasscode, - run - ); - } else { - run(); - } - } - - isDesktopApplication() { - return isDesktopApplication(); - } -} - -export class AccountMenu { - constructor() { - this.restrict = 'E'; - this.template = template; - this.controller = AccountMenuCtrl; - this.controllerAs = 'self'; - this.bindToController = true; - this.scope = { - closeFunction: '&' - }; - } -} diff --git a/app/assets/javascripts/directives/views/actionsMenu.ts b/app/assets/javascripts/directives/views/actionsMenu.ts index a38321c30c3..a85f2b7ba36 100644 --- a/app/assets/javascripts/directives/views/actionsMenu.ts +++ b/app/assets/javascripts/directives/views/actionsMenu.ts @@ -137,8 +137,8 @@ class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope { ...action, running: params?.running, error: params?.error, - subrows: params?.subrows || act?.subrows, - }; + subrows: params?.subrows || act?.subrows, + } as Action; } return act; }); @@ -147,12 +147,12 @@ class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope { } private async updateExtension( - extension: SNActionsExtension, + extension: SNActionsExtension, params?: UpdateExtensionParams ) { const updatedExtension = await this.application.changeItem(extension.uuid, (mutator) => { const extensionMutator = mutator as ActionsExtensionMutator; - extensionMutator.hidden = params && params.hidden; + extensionMutator.hidden = Boolean(params?.hidden); }) as SNActionsExtension; const extensions = this.state.extensions.map((ext: SNActionsExtension) => { if (extension.uuid === ext.uuid) { diff --git a/app/assets/javascripts/directives/views/componentView.js b/app/assets/javascripts/directives/views/componentView.js deleted file mode 100644 index 5be36b3fed2..00000000000 --- a/app/assets/javascripts/directives/views/componentView.js +++ /dev/null @@ -1,280 +0,0 @@ -import template from '%/directives/component-view.pug'; -import { isDesktopApplication } from '../../utils'; -/** - * The maximum amount of time we'll wait for a component - * to load before displaying error - */ -const MAX_LOAD_THRESHOLD = 4000; - -const VISIBILITY_CHANGE_LISTENER_KEY = 'visibilitychange'; - -class ComponentViewCtrl { - /* @ngInject */ - constructor( - $scope, - $rootScope, - $timeout, - componentManager, - desktopManager, - themeManager - ) { - this.$rootScope = $rootScope; - this.$timeout = $timeout; - this.themeManager = themeManager; - this.desktopManager = desktopManager; - this.componentManager = componentManager; - this.componentValid = true; - this.destroyed = false; - - $scope.$watch('ctrl.component', (component, prevComponent) => { - this.componentValueDidSet(component, prevComponent); - }); - $scope.$on('ext-reload-complete', () => { - this.reloadStatus(false); - }); - $scope.$on('$destroy', () => { - this.destroyed = true; - this.destroy(); - }); - } - - $onInit() { - this.registerComponentHandlers(); - this.registerPackageUpdateObserver(); - }; - - registerPackageUpdateObserver() { - this.updateObserver = this.desktopManager - .registerUpdateObserver((component) => { - if(component === this.component && component.active) { - this.reloadComponent(); - } - }); - } - - registerComponentHandlers() { - this.themeHandlerIdentifier = 'component-view-' + Math.random(); - this.componentManager.registerHandler({ - identifier: this.themeHandlerIdentifier, - areas: ['themes'], - activationHandler: (component) => { - this.reloadThemeStatus(); - } - }); - - this.identifier = 'component-view-' + Math.random(); - this.componentManager.registerHandler({ - identifier: this.identifier, - areas: [this.component.area], - activationHandler: (component) => { - if(component !== this.component) { - return; - } - this.$timeout(() => { - this.handleActivation(); - }); - }, - actionHandler: (component, action, data) => { - if(action === 'set-size') { - this.componentManager.handleSetSizeEvent(component, data); - } - } - }); - } - - onVisibilityChange() { - if(document.visibilityState === 'hidden') { - return; - } - if(this.issueLoading) { - this.reloadComponent(); - } - } - - async reloadComponent() { - this.componentValid = false; - await this.componentManager.reloadComponent(this.component); - if (this.destroyed) return; - this.reloadStatus(); - } - - reloadStatus(doManualReload = true) { - this.reloading = true; - const component = this.component; - const previouslyValid = this.componentValid; - const offlineRestricted = component.offlineOnly && !isDesktopApplication(); - const hasUrlError = function(){ - if(isDesktopApplication()) { - return !component.local_url && !component.hasValidHostedUrl(); - } else { - return !component.hasValidHostedUrl(); - } - }(); - this.expired = component.valid_until && component.valid_until <= new Date(); - if(!component.lockReadonly) { - component.readonly = this.expired; - } - this.componentValid = !offlineRestricted && !hasUrlError; - if(!this.componentValid) { - this.loading = false; - } - if(offlineRestricted) { - this.error = 'offline-restricted'; - } else if(hasUrlError) { - this.error = 'url-missing'; - } else { - this.error = null; - } - if(this.componentValid !== previouslyValid) { - if(this.componentValid) { - this.componentManager.reloadComponent(component, true); - } - } - if(this.expired && doManualReload) { - this.$rootScope.$broadcast('reload-ext-dat'); - } - this.reloadThemeStatus(); - this.$timeout(() => { - this.reloading = false; - }, 500); - } - - handleActivation() { - if(!this.component.active) { - return; - } - const iframe = this.componentManager.iframeForComponent( - this.component - ); - if(!iframe) { - return; - } - this.loading = true; - if(this.loadTimeout) { - this.$timeout.cancel(this.loadTimeout); - } - this.loadTimeout = this.$timeout(() => { - this.handleIframeLoadTimeout(); - }, MAX_LOAD_THRESHOLD); - - iframe.onload = (event) => { - this.handleIframeLoad(iframe); - }; - } - - async handleIframeLoadTimeout() { - if(this.loading) { - this.loading = false; - this.issueLoading = true; - if(!this.didAttemptReload) { - this.didAttemptReload = true; - this.reloadComponent(); - } else { - document.addEventListener( - VISIBILITY_CHANGE_LISTENER_KEY, - this.onVisibilityChange.bind(this) - ); - } - } - } - - async handleIframeLoad(iframe) { - let desktopError = false; - if(isDesktopApplication()) { - try { - /** Accessing iframe.contentWindow.origin only allowed in desktop app. */ - if(!iframe.contentWindow.origin || iframe.contentWindow.origin === 'null') { - desktopError = true; - } - } catch (e) {} - } - this.$timeout.cancel(this.loadTimeout); - await this.componentManager.registerComponentWindow( - this.component, - iframe.contentWindow - ); - const avoidFlickerTimeout = 7; - this.$timeout(() => { - this.loading = false; - // eslint-disable-next-line no-unneeded-ternary - this.issueLoading = desktopError ? true : false; - this.onLoad && this.onLoad(this.component); - }, avoidFlickerTimeout); - } - - componentValueDidSet(component, prevComponent) { - const dontSync = true; - if(prevComponent && component !== prevComponent) { - this.componentManager.deactivateComponent( - prevComponent, - dontSync - ); - } - if(component) { - this.componentManager.activateComponent( - component, - dontSync - ); - this.reloadStatus(); - } - } - - reloadThemeStatus() { - if(this.component.acceptsThemes()) { - return; - } - if(this.themeManager.hasActiveTheme()) { - if(!this.dismissedNoThemesMessage) { - this.showNoThemesMessage = true; - } - } else { - this.showNoThemesMessage = false; - } - } - - dismissNoThemesMessage() { - this.showNoThemesMessage = false; - this.dismissedNoThemesMessage = true; - } - - disableActiveTheme() { - this.themeManager.deactivateAllThemes(); - this.dismissNoThemesMessage(); - } - - getUrl() { - const url = this.componentManager.urlForComponent(this.component); - this.component.runningLocally = (url === this.component.local_url); - return url; - } - - destroy() { - this.componentManager.deregisterHandler(this.themeHandlerIdentifier); - this.componentManager.deregisterHandler(this.identifier); - if(this.component && !this.manualDealloc) { - const dontSync = true; - this.componentManager.deactivateComponent(this.component, dontSync); - } - - this.desktopManager.deregisterUpdateObserver(this.updateObserver); - document.removeEventListener( - VISIBILITY_CHANGE_LISTENER_KEY, - this.onVisibilityChange.bind(this) - ); - } -} - -export class ComponentView { - constructor() { - this.restrict = 'E'; - this.template = template; - this.scope = { - component: '=', - onLoad: '=?', - manualDealloc: '=?' - }; - this.controller = ComponentViewCtrl; - this.controllerAs = 'ctrl'; - this.bindToController = true; - } -} diff --git a/app/assets/javascripts/directives/views/passwordWizard.js b/app/assets/javascripts/directives/views/passwordWizard.js deleted file mode 100644 index c18666d30f6..00000000000 --- a/app/assets/javascripts/directives/views/passwordWizard.js +++ /dev/null @@ -1,302 +0,0 @@ -import { protocolManager } from 'snjs'; -import template from '%/directives/password-wizard.pug'; -import { STRING_FAILED_PASSWORD_CHANGE } from '@/strings'; - -const DEFAULT_CONTINUE_TITLE = "Continue"; -const Steps = { - IntroStep: 0, - BackupStep: 1, - SignoutStep: 2, - PasswordStep: 3, - SyncStep: 4, - FinishStep: 5 -}; - -class PasswordWizardCtrl { - /* @ngInject */ - constructor( - $element, - $scope, - $timeout, - alertManager, - archiveManager, - authManager, - modelManager, - syncManager, - ) { - this.$element = $element; - this.$timeout = $timeout; - this.$scope = $scope; - this.alertManager = alertManager; - this.archiveManager = archiveManager; - this.authManager = authManager; - this.modelManager = modelManager; - this.syncManager = syncManager; - this.registerWindowUnloadStopper(); - } - - $onInit() { - this.syncStatus = this.syncManager.syncStatus; - this.formData = {}; - this.configureDefaults(); - } - - configureDefaults() { - if (this.type === 'change-pw') { - this.title = "Change Password"; - this.changePassword = true; - } else if (this.type === 'upgrade-security') { - this.title = "Security Update"; - this.securityUpdate = true; - } - this.continueTitle = DEFAULT_CONTINUE_TITLE; - this.step = Steps.IntroStep; - } - - /** Confirms with user before closing tab */ - registerWindowUnloadStopper() { - window.onbeforeunload = (e) => { - return true; - }; - this.$scope.$on("$destroy", () => { - window.onbeforeunload = null; - }); - } - - titleForStep(step) { - switch (step) { - case Steps.BackupStep: - return "Download a backup of your data"; - case Steps.SignoutStep: - return "Sign out of all your devices"; - case Steps.PasswordStep: - return this.changePassword - ? "Password information" - : "Enter your current password"; - case Steps.SyncStep: - return "Encrypt and sync data with new keys"; - case Steps.FinishStep: - return "Sign back in to your devices"; - default: - return null; - } - } - - async nextStep() { - if (this.lockContinue || this.isContinuing) { - return; - } - this.isContinuing = true; - if (this.step === Steps.FinishStep) { - this.dismiss(); - return; - } - const next = () => { - this.step++; - this.initializeStep(this.step); - this.isContinuing = false; - }; - const preprocessor = this.preprocessorForStep(this.step); - if (preprocessor) { - await preprocessor().then((success) => { - if(success) { - next(); - } else { - this.$timeout(() => { - this.isContinuing = false; - }); - } - }).catch(() => { - this.isContinuing = false; - }); - } else { - next(); - } - } - - preprocessorForStep(step) { - if (step === Steps.PasswordStep) { - return async () => { - this.showSpinner = true; - this.continueTitle = "Generating Keys..."; - const success = await this.validateCurrentPassword(); - this.showSpinner = false; - this.continueTitle = DEFAULT_CONTINUE_TITLE; - return success; - }; - } - } - - async initializeStep(step) { - if (step === Steps.SyncStep) { - await this.initializeSyncingStep(); - } else if (step === Steps.FinishStep) { - this.continueTitle = "Finish"; - } - } - - async initializeSyncingStep() { - this.lockContinue = true; - this.formData.status = "Processing encryption keys..."; - this.formData.processing = true; - - const passwordSuccess = await this.processPasswordChange(); - this.formData.statusError = !passwordSuccess; - this.formData.processing = passwordSuccess; - if(!passwordSuccess) { - this.formData.status = "Unable to process your password. Please try again."; - return; - } - this.formData.status = "Encrypting and syncing data with new keys..."; - - const syncSuccess = await this.resyncData(); - this.formData.statusError = !syncSuccess; - this.formData.processing = !syncSuccess; - if (syncSuccess) { - this.lockContinue = false; - if (this.changePassword) { - this.formData.status = "Successfully changed password and synced all items."; - } else if (this.securityUpdate) { - this.formData.status = "Successfully performed security update and synced all items."; - } - } else { - this.formData.status = STRING_FAILED_PASSWORD_CHANGE; - } - } - - async validateCurrentPassword() { - const currentPassword = this.formData.currentPassword; - const newPass = this.securityUpdate ? currentPassword : this.formData.newPassword; - if (!currentPassword || currentPassword.length === 0) { - this.alertManager.alert({ - text: "Please enter your current password." - }); - return false; - } - if (this.changePassword) { - if (!newPass || newPass.length === 0) { - this.alertManager.alert({ - text: "Please enter a new password." - }); - return false; - } - if (newPass !== this.formData.newPasswordConfirmation) { - this.alertManager.alert({ - text: "Your new password does not match its confirmation." - }); - this.formData.status = null; - return false; - } - } - if (!this.authManager.user.email) { - this.alertManager.alert({ - text: "We don't have your email stored. Please log out then log back in to fix this issue." - }); - this.formData.status = null; - return false; - } - - const minLength = this.authManager.getMinPasswordLength(); - if (!this.securityUpdate && newPass.length < minLength) { - const message = `Your password must be at least ${minLength} characters in length. For your security, please choose a longer password or, ideally, a passphrase, and try again.`; - this.alertManager.alert({ - text: message - }); - return false; - } - - /** Validate current password */ - const authParams = await this.authManager.getAuthParams(); - const password = this.formData.currentPassword; - const keys = await protocolManager.computeEncryptionKeysForUser( - password, - authParams - ); - const success = keys.mk === (await this.authManager.keys()).mk; - if (success) { - this.currentServerPw = keys.pw; - } else { - this.alertManager.alert({ - text: "The current password you entered is not correct. Please try again." - }); - } - return success; - } - - async resyncData() { - await this.modelManager.setAllItemsDirty(); - const response = await this.syncManager.sync(); - if (!response || response.error) { - this.alertManager.alert({ - text: STRING_FAILED_PASSWORD_CHANGE - }); - return false; - } else { - return true; - } - } - - async processPasswordChange() { - const newUserPassword = this.securityUpdate - ? this.formData.currentPassword - : this.formData.newPassword; - - const currentServerPw = this.currentServerPw; - const results = await protocolManager.generateInitialKeysAndAuthParamsForUser( - this.authManager.user.email, - newUserPassword - ); - const newKeys = results.keys; - const newAuthParams = results.authParams; - /** - * Perform a sync beforehand to pull in any last minutes changes before we change - * the encryption key (and thus cant decrypt new changes). - */ - await this.syncManager.sync(); - const response = await this.authManager.changePassword( - await this.syncManager.getServerURL(), - this.authManager.user.email, - currentServerPw, - newKeys, - newAuthParams - ); - if (response.error) { - this.alertManager.alert({ - text: response.error.message - ? response.error.message - : "There was an error changing your password. Please try again." - }); - return false; - } else { - return true; - } - } - - downloadBackup(encrypted) { - this.archiveManager.downloadBackup(encrypted); - } - - dismiss() { - if (this.lockContinue) { - this.alertManager.alert({ - text: "Cannot close window until pending tasks are complete." - }); - } else { - this.$element.remove(); - this.$scope.$destroy(); - } - } -} - -export class PasswordWizard { - constructor() { - this.restrict = 'E'; - this.template = template; - this.controller = PasswordWizardCtrl; - this.controllerAs = 'ctrl'; - this.bindToController = true; - this.scope = { - type: '=' - }; - } -} diff --git a/app/assets/javascripts/directives/views/passwordWizard.ts b/app/assets/javascripts/directives/views/passwordWizard.ts index 0ef223263b3..7f533da130a 100644 --- a/app/assets/javascripts/directives/views/passwordWizard.ts +++ b/app/assets/javascripts/directives/views/passwordWizard.ts @@ -167,8 +167,8 @@ class PasswordWizardCtrl extends PureViewCtrl implements PasswordWizardScope { }); if (!success) { this.application.alertService!.alert( - response!.error.message - ? response!.error.message + response?.error?.message + ? response.error.message : "There was an error changing your password. Please try again." ); this.setFormDataState({ diff --git a/app/assets/javascripts/directives/views/revisionPreviewModal.ts b/app/assets/javascripts/directives/views/revisionPreviewModal.ts index 931160dcd56..b2a123cedac 100644 --- a/app/assets/javascripts/directives/views/revisionPreviewModal.ts +++ b/app/assets/javascripts/directives/views/revisionPreviewModal.ts @@ -9,6 +9,7 @@ import { } from 'snjs'; import template from '%/directives/revision-preview-modal.pug'; import { PayloadContent } from '@node_modules/snjs/dist/@types/protocol/payloads/generator'; +import { confirmDialog } from '@/services/alertService'; interface RevisionPreviewScope { uuid: string @@ -88,7 +89,7 @@ class RevisionPreviewModalCtrl implements RevisionPreviewScope { } } - restore(asCopy: boolean) { + async restore(asCopy: boolean) { const run = async () => { if (asCopy) { const contentCopy = Object.assign({}, this.content); @@ -109,15 +110,12 @@ class RevisionPreviewModalCtrl implements RevisionPreviewScope { }; if (!asCopy) { - this.application.alertService!.confirm( - "Are you sure you want to replace the current note's contents with what you see in this preview?", - undefined, - undefined, - undefined, - run, - undefined, - true, - ); + if (await confirmDialog({ + text: "Are you sure you want to replace the current note's contents with what you see in this preview?", + confirmButtonStyle: 'danger', + })) { + run(); + } } else { run(); } diff --git a/app/assets/javascripts/directives/views/sessionHistoryMenu.ts b/app/assets/javascripts/directives/views/sessionHistoryMenu.ts index a4baabd3544..c4f7f031eb6 100644 --- a/app/assets/javascripts/directives/views/sessionHistoryMenu.ts +++ b/app/assets/javascripts/directives/views/sessionHistoryMenu.ts @@ -2,6 +2,7 @@ import { WebDirective } from './../../types'; import { WebApplication } from '@/ui_models/application'; import template from '%/directives/session-history-menu.pug'; import { SNItem, ItemHistoryEntry, ItemHistory } from '@node_modules/snjs/dist/@types'; +import { confirmDialog } from '@/services/alertService'; interface SessionHistoryScope { application: WebApplication @@ -57,43 +58,33 @@ class SessionHistoryMenuCtrl implements SessionHistoryScope { } } - clearItemHistory() { - this.application.alertService!.confirm( - "Are you sure you want to delete the local session history for this note?", - undefined, - undefined, - undefined, - () => { - this.application.historyManager!.clearHistoryForItem(this.item).then(() => { - this.$timeout(() => { - this.reloadHistory(); - }); + async clearItemHistory() { + if (await confirmDialog({ + text: "Are you sure you want to delete the local session history for this note?", + confirmButtonStyle: 'danger', + })) { + this.application.historyManager!.clearHistoryForItem(this.item).then(() => { + this.$timeout(() => { + this.reloadHistory(); }); - }, - undefined, - true, - ); + }); + } } clearAllHistory() { - this.application.alertService!.confirm( - "Are you sure you want to delete the local session history for all notes?", - undefined, - undefined, - undefined, - () => { - this.application.historyManager!.clearAllHistory().then(() => { - this.$timeout(() => { - this.reloadHistory(); - }); + if (confirmDialog({ + text: "Are you sure you want to delete the local session history for all notes?", + confirmButtonStyle: 'danger' + })) { + this.application.historyManager!.clearAllHistory().then(() => { + this.$timeout(() => { + this.reloadHistory(); }); - }, - undefined, - true, - ); + }); + } } - toggleDiskSaving() { + async toggleDiskSaving() { const run = () => { this.application.historyManager!.toggleDiskSaving().then(() => { this.$timeout(() => { @@ -102,17 +93,14 @@ class SessionHistoryMenuCtrl implements SessionHistoryScope { }); }; if (!this.application.historyManager!.isDiskEnabled()) { - this.application.alertService!.confirm( - `Are you sure you want to save history to disk? This will decrease general - performance, especially as you type. You are advised to disable this feature - if you experience any lagging.`, - undefined, - undefined, - undefined, - run, - undefined, - true, - ); + if (await confirmDialog({ + text: `Are you sure you want to save history to disk? This will decrease general + performance, especially as you type. You are advised to disable this feature + if you experience any lagging.`, + confirmButtonStyle: 'danger', + })) { + run(); + } } else { run(); } diff --git a/app/assets/javascripts/services/archiveManager.js b/app/assets/javascripts/services/archiveManager.js deleted file mode 100644 index 06a429d9f1b..00000000000 --- a/app/assets/javascripts/services/archiveManager.js +++ /dev/null @@ -1,163 +0,0 @@ -import { PrivilegesManager } from '@/services/privilegesManager'; - -export class ArchiveManager { - /* @ngInject */ - constructor(passcodeManager, authManager, modelManager, privilegesManager) { - this.passcodeManager = passcodeManager; - this.authManager = authManager; - this.modelManager = modelManager; - this.privilegesManager = privilegesManager; - } - - /* - Public - */ - - async downloadBackup(encrypted) { - return this.downloadBackupOfItems(this.modelManager.allItems, encrypted); - } - - async downloadBackupOfItems(items, encrypted) { - const run = async () => { - // download in Standard Notes format - let keys, authParams; - if(encrypted) { - if(this.authManager.offline() && this.passcodeManager.hasPasscode()) { - keys = this.passcodeManager.keys(); - authParams = this.passcodeManager.passcodeAuthParams(); - } else { - keys = await this.authManager.keys(); - authParams = await this.authManager.getAuthParams(); - } - const data = await this.__itemsData(items, keys, authParams); - this.__downloadData(data, - `Standard Notes Encrypted Backup and Import File - ${this.__formattedDate()}.txt`); - } else { - this.__downloadZippedItems(items); - } - }; - - if(await this.privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageBackups)) { - this.privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManageBackups, () => { - run(); - }); - } else { - run(); - } - } - - /* - Private - */ - - __formattedDate() { - var string = `${new Date()}`; - // Match up to the first parenthesis, i.e do not include '(Central Standard Time)' - var matches = string.match(/^(.*?) \(/); - if(matches.length >= 2) { - return matches[1]; - } - return string; - } - - async __itemsData(items, keys, authParams) { - const data = await this.modelManager.getJSONDataForItems(items, keys, authParams); - const blobData = new Blob([data], {type: 'text/json'}); - return blobData; - } - - __loadZip(callback) { - if(window.zip) { - callback(); - return; - } - - var scriptTag = document.createElement('script'); - scriptTag.src = "/assets/zip/zip.js"; - scriptTag.async = false; - var headTag = document.getElementsByTagName('head')[0]; - headTag.appendChild(scriptTag); - scriptTag.onload = function() { - zip.workerScriptsPath = "assets/zip/"; - callback(); - }; - } - - __downloadZippedItems(items) { - this.__loadZip(() => { - zip.createWriter(new zip.BlobWriter("application/zip"), async (zipWriter) => { - var index = 0; - - const data = await this.modelManager.getJSONDataForItems(items); - await new Promise((resolve) => { - const blob = new Blob([data], {type: 'text/plain'}); - zipWriter.add( - 'Standard Notes Backup and Import File.txt', - new zip.BlobReader(blob), - resolve - ); - }); - - const nextFile = () => { - var item = items[index]; - var name, contents; - - if(item.content_type === "Note") { - name = item.content.title; - contents = item.content.text; - } else { - name = item.content_type; - contents = JSON.stringify(item.content, null, 2); - } - - if(!name) { - name = ""; - } - - const blob = new Blob([contents], {type: 'text/plain'}); - let filePrefix = name.replace(/\//g, "").replace(/\\+/g, ""); - const fileSuffix = `-${item.uuid.split("-")[0]}.txt`; - // Standard max filename length is 255. Slice the note name down to allow filenameEnd - filePrefix = filePrefix.slice(0, (255 - fileSuffix.length)); - const fileName = `Items/${item.content_type}/${filePrefix}${fileSuffix}`; - zipWriter.add(fileName, new zip.BlobReader(blob), () => { - index++; - if(index < items.length) { - nextFile(); - } else { - zipWriter.close((blob) => { - this.__downloadData(blob, `Standard Notes Backup - ${this.__formattedDate()}.zip`); - zipWriter = null; - }); - } - }); - }; - - nextFile(); - }, onerror); - }); - } - - - __hrefForData(data) { - // If we are replacing a previously generated file we need to - // manually revoke the object URL to avoid memory leaks. - if (this.textFile !== null) { - window.URL.revokeObjectURL(this.textFile); - } - - this.textFile = window.URL.createObjectURL(data); - - // returns a URL you can use as a href - return this.textFile; - } - - __downloadData(data, fileName) { - var link = document.createElement('a'); - link.setAttribute('download', fileName); - link.href = this.__hrefForData(data); - document.body.appendChild(link); - link.click(); - link.remove(); - } -} diff --git a/dist/@types/app/assets/javascripts/controllers/editor.d.ts b/dist/@types/app/assets/javascripts/controllers/editor.d.ts index f985f8c5f6d..997e98e2dd3 100644 --- a/dist/@types/app/assets/javascripts/controllers/editor.d.ts +++ b/dist/@types/app/assets/javascripts/controllers/editor.d.ts @@ -1,4 +1,116 @@ -import { WebDirective } from './../types'; -export declare class EditorPanel extends WebDirective { - constructor(); +/// +export class EditorPanel { + restrict: string; + scope: {}; + template: import("pug").compileTemplate; + replace: boolean; + controller: typeof EditorCtrl; + controllerAs: string; + bindToController: boolean; } +declare class EditorCtrl { + constructor($timeout: any, $rootScope: any, alertManager: any, appState: any, authManager: any, actionsManager: any, componentManager: any, desktopManager: any, keyboardManager: any, modelManager: any, preferencesManager: any, privilegesManager: any, sessionHistory: any, syncManager: any); + $rootScope: any; + alertManager: any; + appState: any; + actionsManager: any; + authManager: any; + componentManager: any; + desktopManager: any; + keyboardManager: any; + modelManager: any; + preferencesManager: any; + privilegesManager: any; + syncManager: any; + state: { + componentStack: never[]; + editorDebounce: number; + isDesktop: any; + spellcheck: boolean; + mutable: { + tagsString: string; + }; + }; + leftResizeControl: {}; + rightResizeControl: {}; + /** Used by .pug template */ + prefKeyMonospace: any; + prefKeySpellcheck: any; + prefKeyMarginResizers: any; + addAppStateObserver(): void; + handleNoteSelectionChange(note: any, previousNote: any): Promise; + addMappingObservers(): void; + addSyncEventHandler(): void; + addSyncStatusObserver(): void; + syncStatusObserver: any; + editorForNote(note: any): any; + setMenuState(menu: any, state: any): void; + toggleMenu(menu: any): void; + closeAllMenus({ exclude }?: { + exclude: any; + }): void; + editorMenuOnSelect: (component: any) => void; + hasAvailableExtensions(): boolean; + performFirefoxPinnedTabFix(): void; + saveNote({ bypassDebouncer, updateClientModified, dontUpdatePreviews }: { + bypassDebouncer: any; + updateClientModified: any; + dontUpdatePreviews: any; + }): void; + saveTimeout: any; + didShowErrorAlert: boolean | undefined; + showSavingStatus(): void; + showAllChangesSavedStatus(): void; + showErrorStatus(error: any): void; + setStatus(status: any, wait?: boolean): void; + statusTimeout: any; + contentChanged(): void; + onTitleEnter($event: any): void; + onTitleChange(): void; + focusEditor(): void; + lastEditorFocusEventSource: any; + focusTitle(): void; + clickedTextArea(): void; + onNameFocus(): void; + editingName: boolean | undefined; + onContentFocus(): void; + onNameBlur(): void; + selectedMenuItem(hide: any): void; + deleteNote(permanently: any): Promise; + performNoteDeletion(note: any): void; + restoreTrashedNote(): void; + deleteNotePermanantely(): void; + getTrashCount(): any; + emptyTrash(): void; + togglePin(): void; + toggleLockNote(): void; + toggleProtectNote(): void; + toggleNotePreview(): void; + toggleArchiveNote(): void; + reloadTagsString(): void; + addTag(tag: any): void; + removeTag(tag: any): void; + saveTags({ strings }?: { + strings: any; + }): void; + onPanelResizeFinish: (width: any, left: any, isMaxWidth: any) => void; + loadPreferences(): void; + reloadFont(): void; + toggleKey(key: any): Promise; + /** @components */ + onEditorLoad: (editor: any) => void; + registerComponentHandler(): void; + reloadComponentStackArray(): void; + reloadComponentContext(): void; + toggleStackComponentForCurrentItem(component: any): void; + disassociateComponentWithCurrentNote(component: any): void; + associateComponentWithCurrentNote(component: any): void; + registerKeyboardShortcuts(): void; + altKeyObserver: any; + trashKeyObserver: any; + deleteKeyObserver: any; + onSystemEditorLoad(): void; + loadedTabListener: boolean | undefined; + tabObserver: any; +} +export {}; diff --git a/dist/@types/app/assets/javascripts/directives/functional/autofocus.d.ts b/dist/@types/app/assets/javascripts/directives/functional/autofocus.d.ts index c2dd7788ff2..19784f72aab 100644 --- a/dist/@types/app/assets/javascripts/directives/functional/autofocus.d.ts +++ b/dist/@types/app/assets/javascripts/directives/functional/autofocus.d.ts @@ -4,5 +4,5 @@ export declare function autofocus($timeout: ng.ITimeoutService): { scope: { shouldFocus: string; }; - link: ($scope: import("angular").IScope, $element: JQLite) => void; + link: ($scope: ng.IScope, $element: JQLite) => void; }; diff --git a/dist/@types/app/assets/javascripts/directives/functional/click-outside.d.ts b/dist/@types/app/assets/javascripts/directives/functional/click-outside.d.ts index 7d1821e2d71..3f973b6fa95 100644 --- a/dist/@types/app/assets/javascripts/directives/functional/click-outside.d.ts +++ b/dist/@types/app/assets/javascripts/directives/functional/click-outside.d.ts @@ -2,5 +2,5 @@ export declare function clickOutside($document: ng.IDocumentService): { restrict: string; replace: boolean; - link: ($scope: import("angular").IScope, $element: JQLite, attrs: any) => void; + link($scope: ng.IScope, $element: JQLite, attrs: any): void; }; diff --git a/dist/@types/app/assets/javascripts/directives/functional/delay-hide.d.ts b/dist/@types/app/assets/javascripts/directives/functional/delay-hide.d.ts index 92b3913b679..b11bee1e7fb 100644 --- a/dist/@types/app/assets/javascripts/directives/functional/delay-hide.d.ts +++ b/dist/@types/app/assets/javascripts/directives/functional/delay-hide.d.ts @@ -1,9 +1,9 @@ -import angular from 'angular'; +/// export declare function delayHide($timeout: ng.ITimeoutService): { restrict: string; scope: { show: string; delay: string; }; - link: (scope: angular.IScope, elem: JQLite) => void; + link: (scope: ng.IScope, elem: JQLite) => void; }; diff --git a/dist/@types/app/assets/javascripts/directives/functional/elemReady.d.ts b/dist/@types/app/assets/javascripts/directives/functional/elemReady.d.ts index 439232246c5..3e318b4c712 100644 --- a/dist/@types/app/assets/javascripts/directives/functional/elemReady.d.ts +++ b/dist/@types/app/assets/javascripts/directives/functional/elemReady.d.ts @@ -1,5 +1,5 @@ /// export declare function elemReady($parse: ng.IParseService): { restrict: string; - link: ($scope: import("angular").IScope, elem: JQLite, attrs: any) => void; + link: ($scope: ng.IScope, elem: JQLite, attrs: any) => void; }; diff --git a/dist/@types/app/assets/javascripts/directives/functional/file-change.d.ts b/dist/@types/app/assets/javascripts/directives/functional/file-change.d.ts index 3a555f1a7b4..aec283efc54 100644 --- a/dist/@types/app/assets/javascripts/directives/functional/file-change.d.ts +++ b/dist/@types/app/assets/javascripts/directives/functional/file-change.d.ts @@ -4,5 +4,5 @@ export declare function fileChange(): { scope: { handler: string; }; - link: (scope: import("angular").IScope, element: JQLite) => void; + link: (scope: ng.IScope, element: JQLite) => void; }; diff --git a/dist/@types/app/assets/javascripts/directives/functional/infiniteScroll.d.ts b/dist/@types/app/assets/javascripts/directives/functional/infiniteScroll.d.ts index 403e34066b1..f2f58fda5af 100644 --- a/dist/@types/app/assets/javascripts/directives/functional/infiniteScroll.d.ts +++ b/dist/@types/app/assets/javascripts/directives/functional/infiniteScroll.d.ts @@ -1,4 +1,4 @@ /// export declare function infiniteScroll(): { - link: (scope: import("angular").IScope, elem: JQLite, attrs: any) => void; + link: (scope: ng.IScope, elem: JQLite, attrs: any) => void; }; diff --git a/dist/@types/app/assets/javascripts/directives/functional/lowercase.d.ts b/dist/@types/app/assets/javascripts/directives/functional/lowercase.d.ts index 04f148a7d38..9b2439769f4 100644 --- a/dist/@types/app/assets/javascripts/directives/functional/lowercase.d.ts +++ b/dist/@types/app/assets/javascripts/directives/functional/lowercase.d.ts @@ -1,5 +1,5 @@ /// export declare function lowercase(): { require: string; - link: (scope: import("angular").IScope, _: JQLite, attrs: any, ctrl: any) => void; + link: (scope: ng.IScope, _: JQLite, attrs: any, ctrl: any) => void; }; diff --git a/dist/@types/app/assets/javascripts/directives/functional/selectOnFocus.d.ts b/dist/@types/app/assets/javascripts/directives/functional/selectOnFocus.d.ts index 988344a846e..49dd42194ef 100644 --- a/dist/@types/app/assets/javascripts/directives/functional/selectOnFocus.d.ts +++ b/dist/@types/app/assets/javascripts/directives/functional/selectOnFocus.d.ts @@ -1,5 +1,5 @@ /// export declare function selectOnFocus($window: ng.IWindowService): { restrict: string; - link: (scope: import("angular").IScope, element: JQLite) => void; + link: (scope: ng.IScope, element: JQLite) => void; }; diff --git a/dist/@types/app/assets/javascripts/directives/functional/snEnter.d.ts b/dist/@types/app/assets/javascripts/directives/functional/snEnter.d.ts index 7fc8454ecd9..3c195b49d6f 100644 --- a/dist/@types/app/assets/javascripts/directives/functional/snEnter.d.ts +++ b/dist/@types/app/assets/javascripts/directives/functional/snEnter.d.ts @@ -1,2 +1,2 @@ /// -export declare function snEnter(): (scope: import("angular").IScope, element: JQLite, attrs: any) => void; +export declare function snEnter(): (scope: ng.IScope, element: JQLite, attrs: any) => void; diff --git a/dist/@types/app/assets/javascripts/interface.d.ts b/dist/@types/app/assets/javascripts/interface.d.ts index 9ddc931e18f..97165f0d8a8 100644 --- a/dist/@types/app/assets/javascripts/interface.d.ts +++ b/dist/@types/app/assets/javascripts/interface.d.ts @@ -1,7 +1,9 @@ import { DeviceInterface, SNApplication } from 'snjs'; +import { Platform } from './services/platform'; export declare class WebDeviceInterface extends DeviceInterface { + private platform; private database; - constructor(namespace: string, timeout: any); + constructor(namespace: string, timeout: any, platform: Platform); setApplication(application: SNApplication): void; deinit(): void; getRawStorageValue(key: string): Promise; @@ -22,7 +24,7 @@ export declare class WebDeviceInterface extends DeviceInterface { saveRawDatabasePayloads(payloads: any[]): Promise; removeRawDatabasePayloadWithId(id: string): Promise; removeAllRawDatabasePayloads(): Promise; - getKeychainValue(): Promise; + getKeychainValue(): Promise; setKeychainValue(value: any): Promise; clearKeychainValue(): Promise; openUrl(url: string): void; diff --git a/dist/@types/app/assets/javascripts/services/alertService.d.ts b/dist/@types/app/assets/javascripts/services/alertService.d.ts index d5d23a46d81..1d3afcdb99b 100644 --- a/dist/@types/app/assets/javascripts/services/alertService.d.ts +++ b/dist/@types/app/assets/javascripts/services/alertService.d.ts @@ -1,5 +1,22 @@ -import { SNAlertService } from 'snjs'; -export declare class AlertService extends SNAlertService { - alert(text: string, title: string, closeButtonText: string | undefined, onClose: () => void): Promise; - confirm(text: string, title: string, confirmButtonText: string | undefined, cancelButtonText: string | undefined, onConfirm: () => void, onCancel: () => void, destructive?: boolean): Promise; +import { SNAlertService, ButtonType } from 'snjs'; +/** @returns a promise resolving to true if the user confirmed, false if they canceled */ +export declare function confirmDialog({ text, title, confirmButtonText, cancelButtonText, confirmButtonStyle, }: { + text: string; + title?: string; + confirmButtonText?: string; + cancelButtonText?: string; + confirmButtonStyle?: 'danger' | 'info'; +}): Promise; +export declare function alertDialog({ title, text, closeButtonText, }: { + title?: string; + text: string; + closeButtonText?: string; +}): Promise; +export declare class AlertService implements SNAlertService { + /** + * @deprecated use the standalone `alertDialog` function instead + */ + alert(text: string, title?: string, closeButtonText?: string): Promise; + confirm(text: string, title?: string, confirmButtonText?: string, confirmButtonType?: ButtonType, cancelButtonText?: string): Promise; + blockingDialog(text: string): () => void; } diff --git a/dist/@types/app/assets/javascripts/services/archiveManager.d.ts b/dist/@types/app/assets/javascripts/services/archiveManager.d.ts index 7001f94d57f..a1edf78d90d 100644 --- a/dist/@types/app/assets/javascripts/services/archiveManager.d.ts +++ b/dist/@types/app/assets/javascripts/services/archiveManager.d.ts @@ -1,11 +1,9 @@ import { WebApplication } from '@/ui_models/application'; -import { SNItem } from 'snjs'; export declare class ArchiveManager { private readonly application; private textFile?; constructor(application: WebApplication); downloadBackup(encrypted: boolean): Promise; - downloadBackupOfItems(items: SNItem[], encrypted: boolean): Promise; private formattedDate; private itemsData; private get zip(); diff --git a/dist/@types/app/assets/javascripts/services/platform.d.ts b/dist/@types/app/assets/javascripts/services/platform.d.ts new file mode 100644 index 00000000000..454e3a40c01 --- /dev/null +++ b/dist/@types/app/assets/javascripts/services/platform.d.ts @@ -0,0 +1,11 @@ +/** Platform-specific (i-e desktop/web) behavior is handled by a Platform object. */ +export interface Platform { + getKeychainValue(): Promise; + setKeychainValue(value: any): Promise; + clearKeychainValue(): Promise; +} +export declare class WebPlatform implements Platform { + getKeychainValue(): Promise; + setKeychainValue(value: any): Promise; + clearKeychainValue(): Promise; +} diff --git a/dist/@types/app/assets/javascripts/services/preferencesManager.d.ts b/dist/@types/app/assets/javascripts/services/preferencesManager.d.ts index 5d521f4d600..7b839c64a12 100644 --- a/dist/@types/app/assets/javascripts/services/preferencesManager.d.ts +++ b/dist/@types/app/assets/javascripts/services/preferencesManager.d.ts @@ -7,7 +7,7 @@ export declare class PreferencesManager extends ApplicationService { onAppLaunch(): Promise; get webApplication(): WebApplication; streamPreferences(): void; - private loadSingleton; + private reloadSingleton; syncUserPreferences(): void; getValue(key: WebPrefKey, defaultValue?: any): any; setUserPrefValue(key: WebPrefKey, value: any, sync?: boolean): Promise; diff --git a/dist/@types/app/assets/javascripts/services/themeManager.d.ts b/dist/@types/app/assets/javascripts/services/themeManager.d.ts index f141d6ea907..6cbe19433fb 100644 --- a/dist/@types/app/assets/javascripts/services/themeManager.d.ts +++ b/dist/@types/app/assets/javascripts/services/themeManager.d.ts @@ -2,7 +2,7 @@ import { WebApplication } from '@/ui_models/application'; import { ApplicationService, ApplicationEvent } from 'snjs'; export declare class ThemeManager extends ApplicationService { private activeThemes; - private unsubState; + private unsubState?; private unregisterDesktop; private unregisterComponent; /** @override */ diff --git a/dist/@types/app/assets/javascripts/strings.d.ts b/dist/@types/app/assets/javascripts/strings.d.ts index 9b5dd350030..420a28b1c3d 100644 --- a/dist/@types/app/assets/javascripts/strings.d.ts +++ b/dist/@types/app/assets/javascripts/strings.d.ts @@ -32,5 +32,11 @@ export declare const STRING_GENERATING_LOGIN_KEYS = "Generating Login Keys..."; export declare const STRING_GENERATING_REGISTER_KEYS = "Generating Account Keys..."; export declare const STRING_INVALID_IMPORT_FILE = "Unable to open file. Ensure it is a proper JSON file and try again."; export declare function StringImportError(errorCount: number): string; +export declare const STRING_ENTER_ACCOUNT_PASSCODE = "Enter your application passcode"; +export declare const STRING_ENTER_ACCOUNT_PASSWORD = "Enter your account password"; +export declare const STRING_ENTER_PASSCODE_FOR_MIGRATION = "Your application passcode is required to perform an upgrade of your local data storage structure."; +export declare const STRING_STORAGE_UPDATE = "Storage Update"; +export declare const STRING_AUTHENTICATION_REQUIRED = "Authentication Required"; /** @password_change */ export declare const STRING_FAILED_PASSWORD_CHANGE = "There was an error re-encrypting your items. Your password was changed, but not all your items were properly re-encrypted and synced. You should try syncing again. If all else fails, you should restore your notes from backup."; +export declare const STRING_CONFIRM_APP_QUIT_DURING_UPGRADE: string; diff --git a/dist/@types/app/assets/javascripts/ui_models/application.d.ts b/dist/@types/app/assets/javascripts/ui_models/application.d.ts index f66299bd6a1..14442536309 100644 --- a/dist/@types/app/assets/javascripts/ui_models/application.d.ts +++ b/dist/@types/app/assets/javascripts/ui_models/application.d.ts @@ -2,9 +2,10 @@ import { ComponentGroup } from './component_group'; import { EditorGroup } from '@/ui_models/editor_group'; import { PasswordWizardType } from '@/types'; -import { SNApplication, Challenge, ChallengeOrchestrator, ProtectedAction } from 'snjs'; +import { SNApplication, Challenge, ProtectedAction } from 'snjs'; import { DesktopManager, LockManager, ArchiveManager, NativeExtManager, StatusManager, ThemeManager, PreferencesManager, KeyboardManager } from '@/services'; import { AppState } from '@/ui_models/app_state'; +import { Platform } from '@/services/platform'; declare type WebServices = { appState: AppState; desktopService: DesktopManager; @@ -24,7 +25,7 @@ export declare class WebApplication extends SNApplication { private currentAuthenticationElement?; editorGroup: EditorGroup; componentGroup: ComponentGroup; - constructor($compile: ng.ICompileService, $timeout: ng.ITimeoutService, scope: ng.IScope, onDeinit: (app: WebApplication) => void); + constructor($compile: ng.ICompileService, $timeout: ng.ITimeoutService, scope: ng.IScope, onDeinit: (app: WebApplication) => void, platform: Platform); /** @override */ deinit(): void; setWebServices(services: WebServices): void; @@ -39,7 +40,7 @@ export declare class WebApplication extends SNApplication { getKeyboardService(): KeyboardManager; checkForSecurityUpdate(): Promise; presentPasswordWizard(type: PasswordWizardType): void; - promptForChallenge(challenge: Challenge, orchestrator: ChallengeOrchestrator): void; + promptForChallenge(challenge: Challenge): void; performProtocolUpgrade(): Promise; presentPrivilegesModal(action: ProtectedAction, onSuccess?: any, onCancel?: any): Promise; presentPrivilegesManagementModal(): void; diff --git a/dist/@types/app/assets/javascripts/ui_models/application_group.d.ts b/dist/@types/app/assets/javascripts/ui_models/application_group.d.ts index 921c71f052c..f8965f5b208 100644 --- a/dist/@types/app/assets/javascripts/ui_models/application_group.d.ts +++ b/dist/@types/app/assets/javascripts/ui_models/application_group.d.ts @@ -1,14 +1,16 @@ /// import { WebApplication } from './application'; +import { Platform } from '@/services/platform'; declare type AppManagerChangeCallback = () => void; export declare class ApplicationGroup { + private platform; $compile: ng.ICompileService; $rootScope: ng.IRootScopeService; $timeout: ng.ITimeoutService; applications: WebApplication[]; changeObservers: AppManagerChangeCallback[]; activeApplication?: WebApplication; - constructor($compile: ng.ICompileService, $rootScope: ng.IRootScopeService, $timeout: ng.ITimeoutService); + constructor($compile: ng.ICompileService, $rootScope: ng.IRootScopeService, $timeout: ng.ITimeoutService, platform: Platform); private createDefaultApplication; /** @callback */ onApplicationDeinit(application: WebApplication): void; @@ -20,7 +22,7 @@ export declare class ApplicationGroup { * Any application which is no longer active is destroyed, and * must be removed from the interface. */ - addApplicationChangeObserver(callback: AppManagerChangeCallback): void; + addApplicationChangeObserver(callback: AppManagerChangeCallback): () => void; private notifyObserversOfAppChange; } export {}; diff --git a/dist/@types/app/assets/javascripts/ui_models/editor.d.ts b/dist/@types/app/assets/javascripts/ui_models/editor.d.ts index 4ffa7278ec5..ab2c67a9e22 100644 --- a/dist/@types/app/assets/javascripts/ui_models/editor.d.ts +++ b/dist/@types/app/assets/javascripts/ui_models/editor.d.ts @@ -20,6 +20,7 @@ export declare class Editor { * Register to be notified when the editor's note changes. */ onNoteChange(callback: () => void): void; + clearNoteChangeListener(): void; /** * Register to be notified when the editor's note's values change * (and thus a new object reference is created) @@ -28,5 +29,5 @@ export declare class Editor { /** * Sets the editor contents by setting its note. */ - setNote(note: SNNote): void; + setNote(note: SNNote, isTemplate?: boolean): void; } diff --git a/dist/@types/app/assets/javascripts/utils.d.ts b/dist/@types/app/assets/javascripts/utils.d.ts index b342b33f3f6..1f8a5d89e46 100644 --- a/dist/@types/app/assets/javascripts/utils.d.ts +++ b/dist/@types/app/assets/javascripts/utils.d.ts @@ -1,3 +1,4 @@ +export declare const isDev: boolean; export declare function getParameterByName(name: string, url: string): string | null; export declare function isNullOrUndefined(value: any): boolean; export declare function getPlatformString(): string; diff --git a/dist/@types/app/assets/javascripts/views/abstract/pure_view_ctrl.d.ts b/dist/@types/app/assets/javascripts/views/abstract/pure_view_ctrl.d.ts index 20337fcefeb..efb6cea843d 100644 --- a/dist/@types/app/assets/javascripts/views/abstract/pure_view_ctrl.d.ts +++ b/dist/@types/app/assets/javascripts/views/abstract/pure_view_ctrl.d.ts @@ -3,16 +3,16 @@ import { ApplicationEvent } from 'snjs'; import { WebApplication } from '@/ui_models/application'; export declare type CtrlState = Partial>; export declare type CtrlProps = Partial>; -export declare class PureViewCtrl { +export declare class PureViewCtrl

{ + props: P; $timeout: ng.ITimeoutService; /** Passed through templates */ application: WebApplication; - props: CtrlProps; - state: CtrlState; + state: S; private unsubApp; private unsubState; - private stateTimeout; - constructor($timeout: ng.ITimeoutService); + private stateTimeout?; + constructor($timeout: ng.ITimeoutService, props?: P); $onInit(): void; deinit(): void; $onDestroy(): void; @@ -20,8 +20,8 @@ export declare class PureViewCtrl { /** @private */ resetState(): Promise; /** @override */ - getInitialState(): {}; - setState(state: CtrlState): Promise; + getInitialState(): S; + setState(state: Partial): Promise; updateUI(func: () => void): Promise; initProps(props: CtrlProps): void; addAppStateObserver(): void; diff --git a/dist/@types/vendor/assets/javascripts/angular-sanitize.d.ts b/dist/@types/vendor/assets/javascripts/angular-sanitize.d.ts index 7f565f6f287..6d93f46facd 100644 --- a/dist/@types/vendor/assets/javascripts/angular-sanitize.d.ts +++ b/dist/@types/vendor/assets/javascripts/angular-sanitize.d.ts @@ -149,7 +149,7 @@ declare class $SanitizeProvider { * @returns {boolean|$sanitizeProvider} Returns the currently configured value if called * without an argument or self for chaining otherwise. */ - enableSvg: (enableSvg: any) => any; + enableSvg: (enableSvg: any) => boolean | any; /** * @ngdoc method * @name $sanitizeProvider#addValidElements @@ -196,7 +196,7 @@ declare class $SanitizeProvider { * * @return {$sanitizeProvider} Returns self for chaining. */ - addValidElements: (elements: Object | string[]) => any; + addValidElements: (elements: Array | Object) => any; /** * @ngdoc method * @name $sanitizeProvider#addValidAttrs @@ -226,7 +226,7 @@ declare class $SanitizeProvider { * * @returns {$sanitizeProvider} Returns self for chaining. */ - addValidAttrs: (attrs: string[]) => any; + addValidAttrs: (attrs: Array) => any; } declare function sanitizeText(chars: any): string; declare var $sanitizeMinErr: any;