Merge pull request #474 from standardnotes/custom-challenges
feat: custom challenges
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { WebDirective } from './../../types';
|
import { WebDirective } from './../../types';
|
||||||
import { isDesktopApplication, isNullOrUndefined, preventRefreshing } from '@/utils';
|
import { isDesktopApplication, preventRefreshing } from '@/utils';
|
||||||
import template from '%/directives/account-menu.pug';
|
import template from '%/directives/account-menu.pug';
|
||||||
import { ProtectedAction, ContentType } from 'snjs';
|
import { ProtectedAction, ContentType } from 'snjs';
|
||||||
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
||||||
@@ -26,7 +26,6 @@ import { SyncOpStatus } from 'snjs/dist/@types/services/sync/sync_op_status';
|
|||||||
import { PasswordWizardType } from '@/types';
|
import { PasswordWizardType } from '@/types';
|
||||||
import { BackupFile } from 'snjs/dist/@types/services/protocol_service';
|
import { BackupFile } from 'snjs/dist/@types/services/protocol_service';
|
||||||
import { confirmDialog, alertDialog } from '@/services/alertService';
|
import { confirmDialog, alertDialog } from '@/services/alertService';
|
||||||
import { HttpResponse } from 'snjs/dist/@types/services/api/http_service';
|
|
||||||
|
|
||||||
const ELEMENT_ID_IMPORT_PASSWORD_INPUT = 'import-password-request';
|
const ELEMENT_ID_IMPORT_PASSWORD_INPUT = 'import-password-request';
|
||||||
|
|
||||||
@@ -44,8 +43,6 @@ type FormData = {
|
|||||||
showPasscodeForm: boolean
|
showPasscodeForm: boolean
|
||||||
strictSignin?: boolean
|
strictSignin?: boolean
|
||||||
ephemeral: boolean
|
ephemeral: boolean
|
||||||
mfa: HttpResponse
|
|
||||||
userMfaCode?: string
|
|
||||||
mergeLocal?: boolean
|
mergeLocal?: boolean
|
||||||
url: string
|
url: string
|
||||||
authenticating: boolean
|
authenticating: boolean
|
||||||
@@ -220,12 +217,10 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> {
|
|||||||
formData.user_password!,
|
formData.user_password!,
|
||||||
formData.strictSignin,
|
formData.strictSignin,
|
||||||
formData.ephemeral,
|
formData.ephemeral,
|
||||||
formData.mfa && formData.mfa.payload.mfa_key,
|
|
||||||
formData.userMfaCode,
|
|
||||||
formData.mergeLocal
|
formData.mergeLocal
|
||||||
);
|
);
|
||||||
const hasError = !response || response.error;
|
const error = response.error;
|
||||||
if (!hasError) {
|
if (!error) {
|
||||||
await this.setFormDataState({
|
await this.setFormDataState({
|
||||||
authenticating: false,
|
authenticating: false,
|
||||||
user_password: undefined
|
user_password: undefined
|
||||||
@@ -233,29 +228,13 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> {
|
|||||||
this.close();
|
this.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const error = response
|
await this.setFormDataState({
|
||||||
? response.error!
|
showLogin: true,
|
||||||
: {
|
status: undefined,
|
||||||
message: "An unknown error occured.",
|
user_password: undefined
|
||||||
tag: undefined,
|
});
|
||||||
status: 500
|
if (error.message) {
|
||||||
} as HttpResponse;
|
this.application!.alertService!.alert(error.message);
|
||||||
if (error.tag === 'mfa-required' || error.tag === 'mfa-invalid') {
|
|
||||||
await this.setFormDataState({
|
|
||||||
showLogin: false,
|
|
||||||
mfa: error,
|
|
||||||
status: undefined
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await this.setFormDataState({
|
|
||||||
showLogin: true,
|
|
||||||
mfa: undefined,
|
|
||||||
status: undefined,
|
|
||||||
user_password: undefined
|
|
||||||
});
|
|
||||||
if (error.message) {
|
|
||||||
this.application!.alertService!.alert(error.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await this.setFormDataState({
|
await this.setFormDataState({
|
||||||
authenticating: false
|
authenticating: false
|
||||||
@@ -281,17 +260,11 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> {
|
|||||||
this.getState().formData.ephemeral,
|
this.getState().formData.ephemeral,
|
||||||
this.getState().formData.mergeLocal
|
this.getState().formData.mergeLocal
|
||||||
);
|
);
|
||||||
if (!response || response.error) {
|
const error = response.error;
|
||||||
|
if (error) {
|
||||||
await this.setFormDataState({
|
await this.setFormDataState({
|
||||||
status: undefined
|
status: undefined
|
||||||
});
|
});
|
||||||
const error = response
|
|
||||||
? response.error!
|
|
||||||
: {
|
|
||||||
message: "An unknown error occured.",
|
|
||||||
tag: undefined,
|
|
||||||
status: 500
|
|
||||||
} as HttpResponse;
|
|
||||||
await this.setFormDataState({
|
await this.setFormDataState({
|
||||||
authenticating: false
|
authenticating: false
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { WebDirective } from './../../types';
|
|||||||
import template from '%/directives/actions-menu.pug';
|
import template from '%/directives/actions-menu.pug';
|
||||||
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
||||||
import { SNItem, Action, SNActionsExtension, UuidString } from 'snjs/dist/@types';
|
import { SNItem, Action, SNActionsExtension, UuidString } from 'snjs/dist/@types';
|
||||||
import { ActionResponse } from 'snjs/dist/@types/services/actions_service';
|
import { ActionResponse } from 'snjs';
|
||||||
import { ActionsExtensionMutator } from 'snjs/dist/@types/models/app/extension';
|
import { ActionsExtensionMutator } from 'snjs/dist/@types/models/app/extension';
|
||||||
|
|
||||||
type ActionsMenuScope = {
|
type ActionsMenuScope = {
|
||||||
@@ -144,8 +144,8 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async updateAction(
|
private async updateAction(
|
||||||
action: Action,
|
action: Action,
|
||||||
extension: SNActionsExtension,
|
extension: SNActionsExtension,
|
||||||
params: UpdateActionParams
|
params: UpdateActionParams
|
||||||
) {
|
) {
|
||||||
const updatedExtension = await this.application.changeItem(extension.uuid, (mutator) => {
|
const updatedExtension = await this.application.changeItem(extension.uuid, (mutator) => {
|
||||||
|
|||||||
@@ -4,12 +4,35 @@ import template from '%/directives/password-wizard.pug';
|
|||||||
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
||||||
|
|
||||||
const DEFAULT_CONTINUE_TITLE = "Continue";
|
const DEFAULT_CONTINUE_TITLE = "Continue";
|
||||||
const Steps = {
|
enum Steps {
|
||||||
PasswordStep: 1,
|
PasswordStep = 1,
|
||||||
FinishStep: 2
|
FinishStep = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
class PasswordWizardCtrl extends PureViewCtrl implements PasswordWizardScope {
|
type FormData = {
|
||||||
|
currentPassword?: string,
|
||||||
|
newPassword?: string,
|
||||||
|
newPasswordConfirmation?: string,
|
||||||
|
status?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
lockContinue: boolean
|
||||||
|
formData: FormData,
|
||||||
|
continueTitle: string,
|
||||||
|
step: Steps,
|
||||||
|
title: string,
|
||||||
|
showSpinner: boolean
|
||||||
|
processing: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
type: PasswordWizardType,
|
||||||
|
changePassword: boolean,
|
||||||
|
securityUpdate: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
class PasswordWizardCtrl extends PureViewCtrl<Props, State> implements PasswordWizardScope {
|
||||||
$element: JQLite
|
$element: JQLite
|
||||||
application!: WebApplication
|
application!: WebApplication
|
||||||
type!: PasswordWizardType
|
type!: PasswordWizardType
|
||||||
@@ -70,7 +93,7 @@ class PasswordWizardCtrl extends PureViewCtrl implements PasswordWizardScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.isContinuing = true;
|
this.isContinuing = true;
|
||||||
this.setState({
|
await this.setState({
|
||||||
showSpinner: true,
|
showSpinner: true,
|
||||||
continueTitle: "Generating Keys..."
|
continueTitle: "Generating Keys..."
|
||||||
});
|
});
|
||||||
@@ -92,7 +115,7 @@ class PasswordWizardCtrl extends PureViewCtrl implements PasswordWizardScope {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setFormDataState(formData: any) {
|
async setFormDataState(formData: Partial<FormData>) {
|
||||||
return this.setState({
|
return this.setState({
|
||||||
formData: {
|
formData: {
|
||||||
...this.state.formData,
|
...this.state.formData,
|
||||||
@@ -121,7 +144,9 @@ class PasswordWizardCtrl extends PureViewCtrl implements PasswordWizardScope {
|
|||||||
this.application.alertService!.alert(
|
this.application.alertService!.alert(
|
||||||
"Your new password does not match its confirmation."
|
"Your new password does not match its confirmation."
|
||||||
);
|
);
|
||||||
this.state.formData.status = null;
|
this.setFormDataState({
|
||||||
|
status: undefined
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,13 +154,15 @@ class PasswordWizardCtrl extends PureViewCtrl implements PasswordWizardScope {
|
|||||||
this.application.alertService!.alert(
|
this.application.alertService!.alert(
|
||||||
"We don't have your email stored. Please log out then log back in to fix this issue."
|
"We don't have your email stored. Please log out then log back in to fix this issue."
|
||||||
);
|
);
|
||||||
this.state.formData.status = null;
|
this.setFormDataState({
|
||||||
|
status: undefined
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Validate current password */
|
/** Validate current password */
|
||||||
const success = await this.application.validateAccountPassword(
|
const success = await this.application.validateAccountPassword(
|
||||||
this.state.formData.currentPassword
|
this.state.formData.currentPassword!
|
||||||
);
|
);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
this.application.alertService!.alert(
|
this.application.alertService!.alert(
|
||||||
@@ -146,37 +173,31 @@ class PasswordWizardCtrl extends PureViewCtrl implements PasswordWizardScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async processPasswordChange() {
|
async processPasswordChange() {
|
||||||
this.setState({
|
await this.setState({
|
||||||
lockContinue: true,
|
lockContinue: true,
|
||||||
processing: true
|
processing: true
|
||||||
});
|
});
|
||||||
this.setFormDataState({
|
await this.setFormDataState({
|
||||||
status: "Processing encryption keys..."
|
status: "Processing encryption keys..."
|
||||||
});
|
});
|
||||||
const newPassword = this.props.securityUpdate
|
const newPassword = this.props.securityUpdate
|
||||||
? this.state.formData.currentPassword
|
? this.state.formData.currentPassword
|
||||||
: this.state.formData.newPassword;
|
: this.state.formData.newPassword;
|
||||||
const response = await this.application.changePassword(
|
const response = await this.application.changePassword(
|
||||||
this.state.formData.currentPassword,
|
this.state.formData.currentPassword!,
|
||||||
newPassword
|
newPassword!
|
||||||
);
|
);
|
||||||
const success = !response || !response.error;
|
const success = !response.error;
|
||||||
this.setFormDataState({
|
await this.setState({
|
||||||
statusError: !success,
|
processing: false,
|
||||||
processing: success
|
lockContinue: false,
|
||||||
});
|
});
|
||||||
if (!success) {
|
if (!success) {
|
||||||
this.application.alertService!.alert(
|
|
||||||
response?.error?.message
|
|
||||||
? response.error.message
|
|
||||||
: "There was an error changing your password. Please try again."
|
|
||||||
);
|
|
||||||
this.setFormDataState({
|
this.setFormDataState({
|
||||||
status: "Unable to process your password. Please try again."
|
status: "Unable to process your password. Please try again."
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
lockContinue: false,
|
|
||||||
formData: {
|
formData: {
|
||||||
...this.state.formData,
|
...this.state.formData,
|
||||||
status: this.props.changePassword
|
status: this.props.changePassword
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable prefer-promise-reject-errors */
|
/* eslint-disable prefer-promise-reject-errors */
|
||||||
import { SNAlertService, ButtonType, DismissBlockingDialog } from 'snjs';
|
import { SNAlertService, ButtonType } from 'snjs';
|
||||||
import { SKAlert } from 'sn-stylekit';
|
import { SKAlert } from 'sn-stylekit';
|
||||||
|
|
||||||
/** @returns a promise resolving to true if the user confirmed, false if they canceled */
|
/** @returns a promise resolving to true if the user confirmed, false if they canceled */
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ApplicationGroup } from './../ui_models/application_group';
|
import { ApplicationService } from 'snjs';
|
||||||
import { WebApplication } from '@/ui_models/application';
|
import { WebApplication } from '@/ui_models/application';
|
||||||
import { isDesktopApplication } from '@/utils';
|
import { isDesktopApplication } from '@/utils';
|
||||||
import { AppStateEvent } from '@/ui_models/app_state';
|
import { AppStateEvent } from '@/ui_models/app_state';
|
||||||
@@ -13,26 +13,21 @@ const LOCK_INTERVAL_ONE_HOUR = 3600 * MILLISECONDS_PER_SECOND;
|
|||||||
|
|
||||||
const STORAGE_KEY_AUTOLOCK_INTERVAL = "AutoLockIntervalKey";
|
const STORAGE_KEY_AUTOLOCK_INTERVAL = "AutoLockIntervalKey";
|
||||||
|
|
||||||
export class AutolockService {
|
export class AutolockService extends ApplicationService {
|
||||||
|
|
||||||
private application: WebApplication
|
|
||||||
private unsubState: any
|
private unsubState: any
|
||||||
private pollFocusInterval: any
|
private pollFocusInterval: any
|
||||||
private lastFocusState?: 'hidden' | 'visible'
|
private lastFocusState?: 'hidden' | 'visible'
|
||||||
private lockAfterDate?: Date
|
private lockAfterDate?: Date
|
||||||
private lockTimeout?: any
|
private lockTimeout?: any
|
||||||
|
|
||||||
constructor(
|
onAppLaunch() {
|
||||||
application: WebApplication
|
this.observeVisibility();
|
||||||
) {
|
return super.onAppLaunch();
|
||||||
this.application = application;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.observeVisibility();
|
|
||||||
}, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
observeVisibility() {
|
observeVisibility() {
|
||||||
this.unsubState = this.application.getAppState().addObserver(
|
this.unsubState = (this.application as WebApplication).getAppState().addObserver(
|
||||||
async (eventName) => {
|
async (eventName) => {
|
||||||
if (eventName === AppStateEvent.WindowDidBlur) {
|
if (eventName === AppStateEvent.WindowDidBlur) {
|
||||||
this.documentVisibilityChanged(false);
|
this.documentVisibilityChanged(false);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { WebApplication } from '@/ui_models/application';
|
import { WebApplication } from '@/ui_models/application';
|
||||||
import _ from 'lodash';
|
|
||||||
import {
|
import {
|
||||||
StorageValueModes,
|
StorageValueModes,
|
||||||
EncryptionIntent,
|
EncryptionIntent,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/** @generic */
|
/** @generic */
|
||||||
export const STRING_SESSION_EXPIRED = "Your session has expired. New changes will not be pulled in. Please sign out and sign back in to refresh your session.";
|
export const STRING_SESSION_EXPIRED = "Your session has expired. New changes will not be pulled in. Please sign in to refresh your session.";
|
||||||
export const STRING_DEFAULT_FILE_ERROR = "Please use FileSafe or the Bold Editor to attach images and files. Learn more at standardnotes.org/filesafe.";
|
export const STRING_DEFAULT_FILE_ERROR = "Please use FileSafe or the Bold Editor to attach images and files. Learn more at standardnotes.org/filesafe.";
|
||||||
export const STRING_GENERIC_SYNC_ERROR = "There was an error syncing. Please try again. If all else fails, try signing out and signing back in.";
|
export const STRING_GENERIC_SYNC_ERROR = "There was an error syncing. Please try again. If all else fails, try signing out and signing back in.";
|
||||||
export function StringSyncException(data: any) {
|
export function StringSyncException(data: any) {
|
||||||
@@ -46,12 +46,6 @@ export const STRING_INVALID_IMPORT_FILE = "Unable to open file. Ensure it is a p
|
|||||||
export function StringImportError(errorCount: number) {
|
export function StringImportError(errorCount: number) {
|
||||||
return `Import complete. ${errorCount} items were not imported because there was an error decrypting them. Make sure the password is correct and try again.`;
|
return `Import complete. ${errorCount} items were not imported because there was an error decrypting them. Make sure the password is correct and try again.`;
|
||||||
}
|
}
|
||||||
export const STRING_ENTER_ACCOUNT_PASSCODE = 'Enter your application passcode to unlock the application';
|
|
||||||
export const STRING_ENTER_ACCOUNT_PASSWORD = 'Enter your account password';
|
|
||||||
export const STRING_ENTER_PASSCODE_FOR_MIGRATION = 'Your application passcode is required to perform an upgrade of your local data storage structure.';
|
|
||||||
export const STRING_ENTER_PASSCODE_FOR_LOGIN_REGISTER = 'Enter your application passcode before signing in or registering';
|
|
||||||
export const STRING_STORAGE_UPDATE = 'Storage Update';
|
|
||||||
export const STRING_AUTHENTICATION_REQUIRED = 'Authentication Required';
|
|
||||||
export const STRING_UNSUPPORTED_BACKUP_FILE_VERSION = 'This backup file was created using an unsupported version of the application and cannot be imported here. Please update your application and try again.';
|
export const STRING_UNSUPPORTED_BACKUP_FILE_VERSION = 'This backup file was created using an unsupported version of the application and cannot be imported here. Please update your application and try again.';
|
||||||
|
|
||||||
/** @password_change */
|
/** @password_change */
|
||||||
@@ -71,8 +65,8 @@ export const STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_REMOVAL =
|
|||||||
|
|
||||||
export const STRING_UPGRADE_ACCOUNT_CONFIRM_TITLE = 'Encryption upgrade available';
|
export const STRING_UPGRADE_ACCOUNT_CONFIRM_TITLE = 'Encryption upgrade available';
|
||||||
export const STRING_UPGRADE_ACCOUNT_CONFIRM_TEXT =
|
export const STRING_UPGRADE_ACCOUNT_CONFIRM_TEXT =
|
||||||
'Encryption version 004 is available for your account and local data storage. ' +
|
'Encryption version 004 is available. ' +
|
||||||
'This version strengthens the encryption algorithms for your account and ' +
|
'This version strengthens the encryption algorithms your account and ' +
|
||||||
'disk use. To learn more about this upgrade, visit our ' +
|
'local storage use. To learn more about this upgrade, visit our ' +
|
||||||
'<a href="https://standardnotes.org/help/security" target="_blank">Security Upgrade page.</a>';
|
'<a href="https://standardnotes.org/help/security" target="_blank">Security Upgrade page.</a>';
|
||||||
export const STRING_UPGRADE_ACCOUNT_CONFIRM_BUTTON = 'Upgrade';
|
export const STRING_UPGRADE_ACCOUNT_CONFIRM_BUTTON = 'Upgrade';
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import { EditorGroup } from '@/ui_models/editor_group';
|
|||||||
import { InputModalScope } from '@/directives/views/inputModal';
|
import { InputModalScope } from '@/directives/views/inputModal';
|
||||||
import { PasswordWizardType, PasswordWizardScope } from '@/types';
|
import { PasswordWizardType, PasswordWizardScope } from '@/types';
|
||||||
import {
|
import {
|
||||||
Environment,
|
|
||||||
SNApplication,
|
SNApplication,
|
||||||
SNAlertService,
|
|
||||||
platformFromString,
|
platformFromString,
|
||||||
Challenge,
|
Challenge,
|
||||||
ProtectedAction
|
ProtectedAction
|
||||||
@@ -167,21 +165,6 @@ export class WebApplication extends SNApplication {
|
|||||||
angular.element(document.body).append(el);
|
angular.element(document.body).append(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
async performProtocolUpgrade() {
|
|
||||||
const result = await this.upgradeProtocolVersion();
|
|
||||||
if (result.success) {
|
|
||||||
this.alertService!.alert(
|
|
||||||
"Success! Your encryption version has been upgraded." +
|
|
||||||
" You'll be asked to enter your credentials again on other devices you're signed into."
|
|
||||||
);
|
|
||||||
} else if (result.error) {
|
|
||||||
console.error(result.error);
|
|
||||||
this.alertService!.alert(
|
|
||||||
"Unable to upgrade encryption version. Please try again."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async presentPrivilegesModal(
|
async presentPrivilegesModal(
|
||||||
action: ProtectedAction,
|
action: ProtectedAction,
|
||||||
onSuccess?: any,
|
onSuccess?: any,
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ export class PureViewCtrl<P = CtrlProps, S = CtrlState> {
|
|||||||
private unsubApp: any
|
private unsubApp: any
|
||||||
private unsubState: any
|
private unsubState: any
|
||||||
private stateTimeout?: ng.IPromise<void>
|
private stateTimeout?: ng.IPromise<void>
|
||||||
|
/**
|
||||||
|
* Subclasses can optionally add an ng-if=ctrl.templateReady to make sure that
|
||||||
|
* no Angular handlebars/syntax render in the UI before display data is ready.
|
||||||
|
*/
|
||||||
|
protected templateReady = false
|
||||||
|
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
@@ -28,6 +33,7 @@ export class PureViewCtrl<P = CtrlProps, S = CtrlState> {
|
|||||||
}
|
}
|
||||||
this.addAppEventObserver();
|
this.addAppEventObserver();
|
||||||
this.addAppStateObserver();
|
this.addAppStateObserver();
|
||||||
|
this.templateReady = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit() {
|
deinit() {
|
||||||
@@ -114,7 +120,7 @@ export class PureViewCtrl<P = CtrlProps, S = CtrlState> {
|
|||||||
await this.onAppLaunch();
|
await this.onAppLaunch();
|
||||||
} else if (eventName === ApplicationEvent.CompletedIncrementalSync) {
|
} else if (eventName === ApplicationEvent.CompletedIncrementalSync) {
|
||||||
this.onAppIncrementalSync();
|
this.onAppIncrementalSync();
|
||||||
} else if (eventName === ApplicationEvent.CompletedFullSync) {
|
} else if (eventName === ApplicationEvent.CompletedFullSync) {
|
||||||
this.onAppFullSync();
|
this.onAppFullSync();
|
||||||
} else if (eventName === ApplicationEvent.KeyStatusChanged) {
|
} else if (eventName === ApplicationEvent.KeyStatusChanged) {
|
||||||
this.onAppKeyChange();
|
this.onAppKeyChange();
|
||||||
|
|||||||
@@ -10,12 +10,10 @@ import {
|
|||||||
PANEL_NAME_TAGS
|
PANEL_NAME_TAGS
|
||||||
} from '@/views/constants';
|
} from '@/views/constants';
|
||||||
import {
|
import {
|
||||||
STRING_SESSION_EXPIRED,
|
|
||||||
STRING_DEFAULT_FILE_ERROR
|
STRING_DEFAULT_FILE_ERROR
|
||||||
} from '@/strings';
|
} from '@/strings';
|
||||||
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
||||||
import { PermissionDialog } from 'snjs/dist/@types/services/component_manager';
|
import { PermissionDialog } from 'snjs/dist/@types/services/component_manager';
|
||||||
import { alertDialog } from '@/services/alertService';
|
|
||||||
|
|
||||||
class ApplicationViewCtrl extends PureViewCtrl {
|
class ApplicationViewCtrl extends PureViewCtrl {
|
||||||
private $compile?: ng.ICompileService
|
private $compile?: ng.ICompileService
|
||||||
@@ -29,7 +27,6 @@ class ApplicationViewCtrl extends PureViewCtrl {
|
|||||||
private showingDownloadStatus = false
|
private showingDownloadStatus = false
|
||||||
private uploadSyncStatus: any
|
private uploadSyncStatus: any
|
||||||
private lastAlertShownTimeStamp = 0;
|
private lastAlertShownTimeStamp = 0;
|
||||||
private showingInvalidSessionAlert = false;
|
|
||||||
|
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
@@ -126,8 +123,6 @@ class ApplicationViewCtrl extends PureViewCtrl {
|
|||||||
this.syncStatus = this.application!.getStatusService().removeStatus(this.syncStatus);
|
this.syncStatus = this.application!.getStatusService().removeStatus(this.syncStatus);
|
||||||
this.completedInitialSync = true;
|
this.completedInitialSync = true;
|
||||||
}
|
}
|
||||||
} else if (eventName === ApplicationEvent.InvalidSyncSession) {
|
|
||||||
this.showInvalidSessionAlert();
|
|
||||||
} else if (eventName === ApplicationEvent.LocalDatabaseReadError) {
|
} else if (eventName === ApplicationEvent.LocalDatabaseReadError) {
|
||||||
this.application!.alertService!.alert(
|
this.application!.alertService!.alert(
|
||||||
'Unable to load local database. Please restart the app and try again.'
|
'Unable to load local database. Please restart the app and try again.'
|
||||||
@@ -258,24 +253,6 @@ class ApplicationViewCtrl extends PureViewCtrl {
|
|||||||
this.application!.componentManager!.presentPermissionsDialog = this.presentPermissionsDialog;
|
this.application!.componentManager!.presentPermissionsDialog = this.presentPermissionsDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
showInvalidSessionAlert() {
|
|
||||||
/** Don't show repeatedly; at most 30 seconds in between */
|
|
||||||
const SHOW_INTERVAL = 30 * 1000;
|
|
||||||
if (
|
|
||||||
!this.showingInvalidSessionAlert &&
|
|
||||||
(Date.now() - this.lastAlertShownTimeStamp) > SHOW_INTERVAL
|
|
||||||
) {
|
|
||||||
this.lastAlertShownTimeStamp = Date.now();
|
|
||||||
this.showingInvalidSessionAlert = true;
|
|
||||||
setTimeout(async () => {
|
|
||||||
await alertDialog({
|
|
||||||
text: STRING_SESSION_EXPIRED
|
|
||||||
});
|
|
||||||
this.showingInvalidSessionAlert = false;
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addDragDropHandlers() {
|
addDragDropHandlers() {
|
||||||
/**
|
/**
|
||||||
* Disable dragging and dropping of files (but allow text) into main SN interface.
|
* Disable dragging and dropping of files (but allow text) into main SN interface.
|
||||||
|
|||||||
@@ -1,26 +1,30 @@
|
|||||||
.sk-modal-background(ng-click="ctrl.cancel()")
|
.sk-modal-background(ng-click="ctrl.cancel()")
|
||||||
.challenge-modal.sk-modal-content
|
.challenge-modal.sk-modal-content(ng-if='ctrl.templateReady')
|
||||||
.sn-component
|
.sn-component
|
||||||
.sk-panel
|
.sk-panel
|
||||||
.sk-panel-header
|
.sk-panel-header
|
||||||
.sk-panel-header-title {{ctrl.title}}
|
.sk-panel-header-title {{ctrl.challenge.modalTitle}}
|
||||||
.sk-panel-content
|
.sk-panel-content
|
||||||
.sk-panel-section
|
.sk-panel-section
|
||||||
div(ng-repeat="type in ctrl.state.types")
|
.sk-p.sk-panel-row.centered.prompt
|
||||||
.sk-p.sk-panel-row.centered.prompt
|
strong {{ctrl.challenge.heading}}
|
||||||
strong {{ctrl.promptForChallenge(type)}}
|
.sk-p.sk-panel-row.centered.subprompt(ng-if='ctrl.challenge.subheading')
|
||||||
|
| {{ctrl.challenge.subheading}}
|
||||||
|
.sk-panel-section
|
||||||
|
div(ng-repeat="prompt in ctrl.state.prompts track by prompt.id")
|
||||||
.sk-panel-row
|
.sk-panel-row
|
||||||
input.sk-input.contrast(
|
input.sk-input.contrast(
|
||||||
ng-model="ctrl.state.values[type].value"
|
ng-model="ctrl.state.values[prompt.id].value"
|
||||||
should-focus="$index == 0"
|
should-focus="$index == 0"
|
||||||
sn-autofocus="true"
|
sn-autofocus="true"
|
||||||
sn-enter="ctrl.submit()" ,
|
sn-enter="ctrl.submit()" ,
|
||||||
ng-change="ctrl.onTextValueChange(type)"
|
ng-change="ctrl.onTextValueChange(prompt)"
|
||||||
type="password"
|
ng-attr-type="{{prompt.secureTextEntry ? 'password' : 'text'}}",
|
||||||
|
ng-attr-placeholder="{{prompt.placeholder}}"
|
||||||
)
|
)
|
||||||
.sk-panel-row.centered
|
.sk-panel-row.centered
|
||||||
label.sk-label.danger(
|
label.sk-label.danger(
|
||||||
ng-if="ctrl.state.values[type].invalid"
|
ng-if="ctrl.state.values[prompt.id].invalid"
|
||||||
) Invalid authentication. Please try again.
|
) Invalid authentication. Please try again.
|
||||||
.sk-panel-footer.extra-padding
|
.sk-panel-footer.extra-padding
|
||||||
.sk-button.info.big.block.bold(
|
.sk-button.info.big.block.bold(
|
||||||
|
|||||||
@@ -1,43 +1,39 @@
|
|||||||
import { WebApplication } from '@/ui_models/application';
|
import { WebApplication } from '@/ui_models/application';
|
||||||
import template from './challenge-modal.pug';
|
import template from './challenge-modal.pug';
|
||||||
import {
|
import {
|
||||||
ChallengeType,
|
|
||||||
ChallengeValue,
|
ChallengeValue,
|
||||||
removeFromArray,
|
removeFromArray,
|
||||||
Challenge,
|
Challenge,
|
||||||
ChallengeReason,
|
ChallengeReason,
|
||||||
|
ChallengePrompt
|
||||||
} from 'snjs';
|
} from 'snjs';
|
||||||
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
||||||
import { WebDirective } from '@/types';
|
import { WebDirective } from '@/types';
|
||||||
import { confirmDialog } from '@/services/alertService';
|
import { confirmDialog } from '@/services/alertService';
|
||||||
import {
|
import {
|
||||||
STRING_SIGN_OUT_CONFIRMATION,
|
STRING_SIGN_OUT_CONFIRMATION,
|
||||||
STRING_ENTER_ACCOUNT_PASSCODE,
|
|
||||||
STRING_ENTER_ACCOUNT_PASSWORD,
|
|
||||||
STRING_ENTER_PASSCODE_FOR_MIGRATION,
|
|
||||||
STRING_STORAGE_UPDATE,
|
|
||||||
STRING_AUTHENTICATION_REQUIRED,
|
|
||||||
STRING_ENTER_PASSCODE_FOR_LOGIN_REGISTER,
|
|
||||||
} from '@/strings';
|
} from '@/strings';
|
||||||
|
|
||||||
type InputValue = {
|
type InputValue = {
|
||||||
|
prompt: ChallengePrompt
|
||||||
value: string
|
value: string
|
||||||
invalid: boolean
|
invalid: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type Values = Record<ChallengeType, InputValue>
|
type Values = Record<number, InputValue>
|
||||||
|
|
||||||
type ChallengeModalState = {
|
type ChallengeModalState = {
|
||||||
types: ChallengeType[]
|
prompts: ChallengePrompt[]
|
||||||
values: Partial<Values>
|
values: Partial<Values>
|
||||||
processing: boolean,
|
processing: boolean,
|
||||||
forgotPasscode: boolean,
|
forgotPasscode: boolean,
|
||||||
showForgotPasscodeLink: boolean,
|
showForgotPasscodeLink: boolean,
|
||||||
|
processingPrompts: ChallengePrompt[],
|
||||||
|
hasAccount: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChallengeModalCtrl extends PureViewCtrl {
|
class ChallengeModalCtrl extends PureViewCtrl<{}, ChallengeModalState> {
|
||||||
private $element: JQLite
|
private $element: JQLite
|
||||||
private processingTypes: ChallengeType[] = []
|
|
||||||
application!: WebApplication
|
application!: WebApplication
|
||||||
challenge!: Challenge
|
challenge!: Challenge
|
||||||
private cancelable = false
|
private cancelable = false
|
||||||
@@ -58,14 +54,15 @@ class ChallengeModalCtrl extends PureViewCtrl {
|
|||||||
$onInit() {
|
$onInit() {
|
||||||
super.$onInit();
|
super.$onInit();
|
||||||
const values = {} as Values;
|
const values = {} as Values;
|
||||||
const types = this.challenge.types;
|
const prompts = this.challenge.prompts;
|
||||||
for (const type of types) {
|
for (const prompt of prompts) {
|
||||||
values[type] = {
|
values[prompt.id] = {
|
||||||
|
prompt,
|
||||||
value: '',
|
value: '',
|
||||||
invalid: false
|
invalid: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let showForgotPasscodeLink: boolean;
|
let showForgotPasscodeLink = false;
|
||||||
switch (this.challenge.reason) {
|
switch (this.challenge.reason) {
|
||||||
case ChallengeReason.ApplicationUnlock:
|
case ChallengeReason.ApplicationUnlock:
|
||||||
showForgotPasscodeLink = true;
|
showForgotPasscodeLink = true;
|
||||||
@@ -86,29 +83,40 @@ class ChallengeModalCtrl extends PureViewCtrl {
|
|||||||
}
|
}
|
||||||
this.cancelable = !showForgotPasscodeLink
|
this.cancelable = !showForgotPasscodeLink
|
||||||
this.setState({
|
this.setState({
|
||||||
types,
|
prompts,
|
||||||
values,
|
values,
|
||||||
processing: false,
|
processing: false,
|
||||||
forgotPasscode: false,
|
forgotPasscode: false,
|
||||||
showForgotPasscodeLink,
|
showForgotPasscodeLink,
|
||||||
hasAccount: this.application.hasAccount(),
|
hasAccount: this.application.hasAccount(),
|
||||||
|
processingPrompts: []
|
||||||
});
|
});
|
||||||
this.application.setChallengeCallbacks({
|
this.application.addChallengeObserver(
|
||||||
challenge: this.challenge,
|
this.challenge,
|
||||||
onValidValue: (value) => {
|
{
|
||||||
this.getState().values[value.type]!.invalid = false;
|
onValidValue: (value) => {
|
||||||
removeFromArray(this.processingTypes, value.type);
|
this.getState().values[value.prompt.id]!.invalid = false;
|
||||||
this.reloadProcessingStatus();
|
removeFromArray(this.state.processingPrompts, value.prompt);
|
||||||
},
|
this.reloadProcessingStatus();
|
||||||
onInvalidValue: (value) => {
|
},
|
||||||
this.getState().values[value.type]!.invalid = true;
|
onInvalidValue: (value) => {
|
||||||
removeFromArray(this.processingTypes, value.type);
|
this.getState().values[value.prompt.id]!.invalid = true;
|
||||||
this.reloadProcessingStatus();
|
/** If custom validation, treat all values together and not individually */
|
||||||
},
|
if (!value.prompt.validates) {
|
||||||
onComplete: () => {
|
this.setState({ processingPrompts: [], processing: false });
|
||||||
this.dismiss();
|
} else {
|
||||||
},
|
removeFromArray(this.state.processingPrompts, value.prompt);
|
||||||
});
|
this.reloadProcessingStatus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
this.dismiss();
|
||||||
|
},
|
||||||
|
onCancel: () => {
|
||||||
|
this.dismiss();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit() {
|
deinit() {
|
||||||
@@ -118,34 +126,11 @@ class ChallengeModalCtrl extends PureViewCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reloadProcessingStatus() {
|
reloadProcessingStatus() {
|
||||||
this.setState({
|
return this.setState({
|
||||||
processing: this.processingTypes.length > 0
|
processing: this.state.processingPrompts.length > 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get title(): string {
|
|
||||||
if (this.challenge.reason === ChallengeReason.Migration) {
|
|
||||||
return STRING_STORAGE_UPDATE;
|
|
||||||
} else {
|
|
||||||
return STRING_AUTHENTICATION_REQUIRED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
promptForChallenge(challenge: ChallengeType): string {
|
|
||||||
if (challenge === ChallengeType.LocalPasscode) {
|
|
||||||
switch (this.challenge.reason) {
|
|
||||||
case ChallengeReason.Migration:
|
|
||||||
return STRING_ENTER_PASSCODE_FOR_MIGRATION;
|
|
||||||
case ChallengeReason.ResaveRootKey:
|
|
||||||
return STRING_ENTER_PASSCODE_FOR_LOGIN_REGISTER;
|
|
||||||
default:
|
|
||||||
return STRING_ENTER_ACCOUNT_PASSCODE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return STRING_ENTER_ACCOUNT_PASSWORD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async destroyLocalData() {
|
async destroyLocalData() {
|
||||||
if (await confirmDialog({
|
if (await confirmDialog({
|
||||||
text: STRING_SIGN_OUT_CONFIRMATION,
|
text: STRING_SIGN_OUT_CONFIRMATION,
|
||||||
@@ -156,10 +141,10 @@ class ChallengeModalCtrl extends PureViewCtrl {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @template */
|
||||||
cancel() {
|
cancel() {
|
||||||
if (this.cancelable) {
|
if (this.cancelable) {
|
||||||
this.application!.cancelChallenge(this.challenge);
|
this.application!.cancelChallenge(this.challenge);
|
||||||
this.dismiss();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,18 +154,18 @@ class ChallengeModalCtrl extends PureViewCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextValueChange(challenge: ChallengeType) {
|
onTextValueChange(prompt: ChallengePrompt) {
|
||||||
const values = this.getState().values;
|
const values = this.getState().values;
|
||||||
values[challenge]!.invalid = false;
|
values[prompt.id]!.invalid = false;
|
||||||
this.setState({ values });
|
this.setState({ values });
|
||||||
}
|
}
|
||||||
|
|
||||||
validate() {
|
validate() {
|
||||||
const failed = [];
|
const failed = [];
|
||||||
for (const type of this.getState().types) {
|
for (const prompt of this.getState().prompts) {
|
||||||
const value = this.getState().values[type];
|
const value = this.getState().values[prompt.id];
|
||||||
if (!value || value.value.length === 0) {
|
if (!value || value.value.length === 0) {
|
||||||
this.getState().values[type]!.invalid = true;
|
this.getState().values[prompt.id]!.invalid = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return failed.length === 0;
|
return failed.length === 0;
|
||||||
@@ -191,22 +176,29 @@ class ChallengeModalCtrl extends PureViewCtrl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.setState({ processing: true });
|
await this.setState({ processing: true });
|
||||||
const values = [];
|
const values: ChallengeValue[] = [];
|
||||||
for (const key of Object.keys(this.getState().values)) {
|
for (const inputValue of Object.values(this.getState().values)) {
|
||||||
const type = Number(key) as ChallengeType;
|
const rawValue = inputValue!!.value;
|
||||||
if (this.getState().values[type]!.invalid) {
|
const value = new ChallengeValue(inputValue!.prompt, rawValue);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const rawValue = this.getState().values[type]!.value;
|
|
||||||
const value = new ChallengeValue(type, rawValue);
|
|
||||||
values.push(value);
|
values.push(value);
|
||||||
}
|
}
|
||||||
this.processingTypes = values.map((v) => v.type);
|
const processingPrompts = values.map((v) => v.prompt);
|
||||||
if (values.length > 0) {
|
await this.setState({
|
||||||
this.application.submitValuesForChallenge(this.challenge, values);
|
processingPrompts: processingPrompts,
|
||||||
} else {
|
processing: processingPrompts.length > 0
|
||||||
this.setState({ processing: false });
|
})
|
||||||
}
|
/**
|
||||||
|
* 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
|
||||||
|
* (crypto key generation)
|
||||||
|
*/
|
||||||
|
this.$timeout(() => {
|
||||||
|
if (values.length > 0) {
|
||||||
|
this.application.submitValuesForChallenge(this.challenge, values);
|
||||||
|
} else {
|
||||||
|
this.setState({ processing: false });
|
||||||
|
}
|
||||||
|
}, 50)
|
||||||
}
|
}
|
||||||
|
|
||||||
dismiss() {
|
dismiss() {
|
||||||
|
|||||||
@@ -347,7 +347,7 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
|
|||||||
confirmButtonText: STRING_UPGRADE_ACCOUNT_CONFIRM_BUTTON,
|
confirmButtonText: STRING_UPGRADE_ACCOUNT_CONFIRM_BUTTON,
|
||||||
})) {
|
})) {
|
||||||
preventRefreshing(STRING_CONFIRM_APP_QUIT_DURING_UPGRADE, async () => {
|
preventRefreshing(STRING_CONFIRM_APP_QUIT_DURING_UPGRADE, async () => {
|
||||||
await this.application.performProtocolUpgrade();
|
await this.application.upgradeProtocolVersion();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { SNNote, SNTag } from 'snjs';
|
import { SNNote } from 'snjs';
|
||||||
|
|
||||||
export enum NoteSortKey {
|
export enum NoteSortKey {
|
||||||
CreatedAt = 'created_at',
|
CreatedAt = 'created_at',
|
||||||
|
|||||||
@@ -13,8 +13,9 @@
|
|||||||
|
|
||||||
.challenge-modal {
|
.challenge-modal {
|
||||||
max-width: 480px;
|
max-width: 480px;
|
||||||
|
min-width: 400px !important;
|
||||||
|
|
||||||
.prompt {
|
.prompt, .subprompt {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.sk-panel .sk-panel-header {
|
.sk-panel .sk-panel-header {
|
||||||
@@ -90,10 +91,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#password-wizard {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#item-preview-modal {
|
#item-preview-modal {
|
||||||
> .sk-modal-content {
|
> .sk-modal-content {
|
||||||
width: 800px;
|
width: 800px;
|
||||||
|
|||||||
@@ -8,8 +8,7 @@
|
|||||||
ng-if=`
|
ng-if=`
|
||||||
!self.state.user &&
|
!self.state.user &&
|
||||||
!self.state.formData.showLogin &&
|
!self.state.formData.showLogin &&
|
||||||
!self.state.formData.showRegister &&
|
!self.state.formData.showRegister`
|
||||||
!self.state.formData.mfa`
|
|
||||||
)
|
)
|
||||||
.sk-panel-row
|
.sk-panel-row
|
||||||
.sk-h1 Sign in or register to enable sync and end-to-end encryption.
|
.sk-h1 Sign in or register to enable sync and end-to-end encryption.
|
||||||
@@ -131,35 +130,10 @@
|
|||||||
type='checkbox'
|
type='checkbox'
|
||||||
)
|
)
|
||||||
p.sk-p Merge local data ({{self.notesAndTagsCount()}} notes and tags)
|
p.sk-p Merge local data ({{self.notesAndTagsCount()}} notes and tags)
|
||||||
.sk-panel-section(ng-if='self.state.formData.mfa')
|
|
||||||
form.sk-panel-form(ng-submit='self.submitMfaForm()')
|
|
||||||
.sk-p.sk-panel-row {{self.state.formData.mfa.message}}
|
|
||||||
.sk-panel-row
|
|
||||||
input.sk-input.contrast(
|
|
||||||
autofocus='true',
|
|
||||||
name='mfa',
|
|
||||||
ng-model='self.state.formData.userMfaCode',
|
|
||||||
placeholder='Enter Code',
|
|
||||||
required='',
|
|
||||||
should-focus='true',
|
|
||||||
sn-autofocus='true'
|
|
||||||
)
|
|
||||||
.sk-button-group.stretch.sk-panel-row.form-submit(
|
|
||||||
ng-if='!self.state.formData.status'
|
|
||||||
)
|
|
||||||
button.sk-button.info.featured(type='submit')
|
|
||||||
.sk-label Sign In
|
|
||||||
.sk-panel-section.no-bottom-pad(ng-if='self.state.formData.status')
|
|
||||||
.sk-panel-row
|
|
||||||
.sk-panel-row
|
|
||||||
.sk-horizontal-group
|
|
||||||
.sk-spinner.small.neutral
|
|
||||||
.sk-label {{self.state.formData.status}}
|
|
||||||
div(
|
div(
|
||||||
ng-if=`
|
ng-if=`
|
||||||
!self.state.formData.showLogin &&
|
!self.state.formData.showLogin &&
|
||||||
!self.state.formData.showRegister &&
|
!self.state.formData.showRegister`
|
||||||
!self.state.formData.mfa`
|
|
||||||
)
|
)
|
||||||
.sk-panel-section(ng-if='self.state.user')
|
.sk-panel-section(ng-if='self.state.user')
|
||||||
.sk-notification.danger(ng-if='self.syncStatus.error')
|
.sk-notification.danger(ng-if='self.syncStatus.error')
|
||||||
|
|||||||
@@ -13,37 +13,36 @@
|
|||||||
.sk-panel-column.stretch
|
.sk-panel-column.stretch
|
||||||
form.sk-panel-form
|
form.sk-panel-form
|
||||||
input.sk-input.contrast(
|
input.sk-input.contrast(
|
||||||
ng-model='ctrl.state.formData.currentPassword',
|
ng-model='ctrl.state.formData.currentPassword',
|
||||||
placeholder='Current Password',
|
placeholder='Current Password',
|
||||||
should-focus='true',
|
should-focus='true',
|
||||||
sn-autofocus='true',
|
sn-autofocus='true',
|
||||||
|
type='password'
|
||||||
|
)
|
||||||
|
.sk-panel-row
|
||||||
|
input.sk-input.contrast(
|
||||||
|
ng-if='ctrl.props.changePassword',
|
||||||
|
ng-model='ctrl.state.formData.newPassword',
|
||||||
|
placeholder='New Password',
|
||||||
type='password'
|
type='password'
|
||||||
)
|
)
|
||||||
input.sk-input.contrast(
|
input.sk-input.contrast(
|
||||||
ng-if='ctrl.props.changePassword',
|
ng-if='ctrl.props.changePassword',
|
||||||
ng-model='ctrl.state.formData.newPassword',
|
|
||||||
placeholder='New Password',
|
|
||||||
type='password'
|
|
||||||
)
|
|
||||||
input.sk-input.contrast(
|
|
||||||
ng-if='ctrl.props.changePassword',
|
|
||||||
ng-model='ctrl.state.formData.newPasswordConfirmation',
|
ng-model='ctrl.state.formData.newPasswordConfirmation',
|
||||||
placeholder='Confirm New Password',
|
placeholder='Confirm New Password',
|
||||||
type='password'
|
type='password'
|
||||||
)
|
)
|
||||||
.sk-panel-section(ng-if='ctrl.state.step == 2')
|
.sk-panel-section(ng-if='ctrl.state.step == 2')
|
||||||
div(ng-if='ctrl.props.changePassword')
|
.sk-label.sk-bold.info(ng-if='ctrl.props.changePassword')
|
||||||
p.sk-p.sk-panel-row.info-i Your password has been successfully changed.
|
| Your password has been successfully changed.
|
||||||
div(ng-if='ctrl.props.securityUpdate')
|
p.sk-p.info-i(ng-if='ctrl.props.securityUpdate')
|
||||||
p.sk-p.sk-panel-row.info-i
|
| The account update has been successfully applied to your account.
|
||||||
| The account update has been successfully applied to your account.
|
p.sk-p
|
||||||
p.sk-p.sk-panel-row
|
| Please ensure you are running the latest version of Standard Notes
|
||||||
| Please ensure you are running the latest version of Standard Notes
|
|
||||||
| on all platforms to ensure maximum compatibility.
|
| on all platforms to ensure maximum compatibility.
|
||||||
.sk-panel-footer
|
.sk-panel-footer
|
||||||
.empty
|
.sk-button.info(
|
||||||
a.sk-a.info.right(
|
ng-click='ctrl.nextStep()',
|
||||||
ng-click='ctrl.nextStep()',
|
ng-disabled='ctrl.state.lockContinue'
|
||||||
ng-disabled='ctrl.state.lockContinue')
|
)
|
||||||
.sk-spinner.small.inline.info.mr-5(ng-if='ctrl.state.showSpinner')
|
.sk-label {{ctrl.state.continueTitle}}
|
||||||
| {{ctrl.state.continueTitle}}
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { WebApplication } from '@/ui_models/application';
|
import { ApplicationService } from 'snjs';
|
||||||
export declare class AutolockService {
|
export declare class AutolockService extends ApplicationService {
|
||||||
private application;
|
|
||||||
private unsubState;
|
private unsubState;
|
||||||
private pollFocusInterval;
|
private pollFocusInterval;
|
||||||
private lastFocusState?;
|
private lastFocusState?;
|
||||||
private lockAfterDate?;
|
private lockAfterDate?;
|
||||||
private lockTimeout?;
|
private lockTimeout?;
|
||||||
constructor(application: WebApplication);
|
onAppLaunch(): Promise<void>;
|
||||||
observeVisibility(): void;
|
observeVisibility(): void;
|
||||||
deinit(): void;
|
deinit(): void;
|
||||||
private lockApplication;
|
private lockApplication;
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ declare type StatusCallback = (string: string) => void;
|
|||||||
export declare class StatusManager {
|
export declare class StatusManager {
|
||||||
private statuses;
|
private statuses;
|
||||||
private observers;
|
private observers;
|
||||||
statusFromString(string: string): {
|
replaceStatusWithString(status: FooterStatus, string: string): {
|
||||||
|
string: string;
|
||||||
|
};
|
||||||
|
addStatusFromString(string: string): {
|
||||||
string: string;
|
string: string;
|
||||||
};
|
};
|
||||||
replaceStatusWithString(status: FooterStatus, string: string): FooterStatus;
|
|
||||||
addStatusFromString(string: string): FooterStatus;
|
|
||||||
addStatus(status: FooterStatus): FooterStatus;
|
|
||||||
removeStatus(status: FooterStatus): undefined;
|
removeStatus(status: FooterStatus): undefined;
|
||||||
getStatusString(): string;
|
|
||||||
notifyObservers(): void;
|
|
||||||
addStatusObserver(callback: StatusCallback): () => void;
|
addStatusObserver(callback: StatusCallback): () => void;
|
||||||
|
private notifyObservers;
|
||||||
|
private getStatusString;
|
||||||
}
|
}
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
11
dist/@types/app/assets/javascripts/strings.d.ts
vendored
11
dist/@types/app/assets/javascripts/strings.d.ts
vendored
@@ -1,5 +1,5 @@
|
|||||||
/** @generic */
|
/** @generic */
|
||||||
export declare const STRING_SESSION_EXPIRED = "Your session has expired. New changes will not be pulled in. Please sign out and sign back in to refresh your session.";
|
export declare const STRING_SESSION_EXPIRED = "Your session has expired. New changes will not be pulled in. Please sign in to refresh your session.";
|
||||||
export declare const STRING_DEFAULT_FILE_ERROR = "Please use FileSafe or the Bold Editor to attach images and files. Learn more at standardnotes.org/filesafe.";
|
export declare const STRING_DEFAULT_FILE_ERROR = "Please use FileSafe or the Bold Editor to attach images and files. Learn more at standardnotes.org/filesafe.";
|
||||||
export declare const STRING_GENERIC_SYNC_ERROR = "There was an error syncing. Please try again. If all else fails, try signing out and signing back in.";
|
export declare const STRING_GENERIC_SYNC_ERROR = "There was an error syncing. Please try again. If all else fails, try signing out and signing back in.";
|
||||||
export declare function StringSyncException(data: any): string;
|
export declare function StringSyncException(data: any): string;
|
||||||
@@ -32,15 +32,12 @@ export declare const STRING_GENERATING_LOGIN_KEYS = "Generating Login Keys...";
|
|||||||
export declare const STRING_GENERATING_REGISTER_KEYS = "Generating Account 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 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 function StringImportError(errorCount: number): string;
|
||||||
export declare const STRING_ENTER_ACCOUNT_PASSCODE = "Enter your application passcode to unlock the application";
|
|
||||||
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_ENTER_PASSCODE_FOR_LOGIN_REGISTER = "Enter your application passcode before signing in or registering";
|
|
||||||
export declare const STRING_STORAGE_UPDATE = "Storage Update";
|
|
||||||
export declare const STRING_AUTHENTICATION_REQUIRED = "Authentication Required";
|
|
||||||
export declare const STRING_UNSUPPORTED_BACKUP_FILE_VERSION = "This backup file was created using an unsupported version of the application and cannot be imported here. Please update your application and try again.";
|
export declare const STRING_UNSUPPORTED_BACKUP_FILE_VERSION = "This backup file was created using an unsupported version of the application and cannot be imported here. Please update your application and try again.";
|
||||||
/** @password_change */
|
/** @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_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;
|
export declare const STRING_CONFIRM_APP_QUIT_DURING_UPGRADE: string;
|
||||||
export declare const STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_CHANGE: string;
|
export declare const STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_CHANGE: string;
|
||||||
export declare const STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_REMOVAL: string;
|
export declare const STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_REMOVAL: string;
|
||||||
|
export declare const STRING_UPGRADE_ACCOUNT_CONFIRM_TITLE = "Encryption upgrade available";
|
||||||
|
export declare const STRING_UPGRADE_ACCOUNT_CONFIRM_TEXT: string;
|
||||||
|
export declare const STRING_UPGRADE_ACCOUNT_CONFIRM_BUTTON = "Upgrade";
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ export declare class WebApplication extends SNApplication {
|
|||||||
checkForSecurityUpdate(): Promise<boolean>;
|
checkForSecurityUpdate(): Promise<boolean>;
|
||||||
presentPasswordWizard(type: PasswordWizardType): void;
|
presentPasswordWizard(type: PasswordWizardType): void;
|
||||||
promptForChallenge(challenge: Challenge): void;
|
promptForChallenge(challenge: Challenge): void;
|
||||||
performProtocolUpgrade(): Promise<void>;
|
|
||||||
presentPrivilegesModal(action: ProtectedAction, onSuccess?: any, onCancel?: any): Promise<void>;
|
presentPrivilegesModal(action: ProtectedAction, onSuccess?: any, onCancel?: any): Promise<void>;
|
||||||
presentPrivilegesManagementModal(): void;
|
presentPrivilegesManagementModal(): void;
|
||||||
authenticationInProgress(): boolean;
|
authenticationInProgress(): boolean;
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ export declare class PureViewCtrl<P = CtrlProps, S = CtrlState> {
|
|||||||
private unsubApp;
|
private unsubApp;
|
||||||
private unsubState;
|
private unsubState;
|
||||||
private stateTimeout?;
|
private stateTimeout?;
|
||||||
|
/**
|
||||||
|
* Subclasses can optionally add an ng-if=ctrl.templateReady to make sure that
|
||||||
|
* no Angular handlebars/syntax render in the UI before display data is ready.
|
||||||
|
*/
|
||||||
|
protected templateReady: boolean;
|
||||||
constructor($timeout: ng.ITimeoutService, props?: P);
|
constructor($timeout: ng.ITimeoutService, props?: P);
|
||||||
$onInit(): void;
|
$onInit(): void;
|
||||||
deinit(): void;
|
deinit(): void;
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -10956,8 +10956,8 @@
|
|||||||
"from": "github:standardnotes/sncrypto#8794c88daa967eaae493cd5fdec7506d52b257ad"
|
"from": "github:standardnotes/sncrypto#8794c88daa967eaae493cd5fdec7506d52b257ad"
|
||||||
},
|
},
|
||||||
"snjs": {
|
"snjs": {
|
||||||
"version": "github:standardnotes/snjs#f922ede72a3e90984605048854dc20db8a88c790",
|
"version": "github:standardnotes/snjs#99d73922326b20e58f914ec9dd666efd6e5e4ac9",
|
||||||
"from": "github:standardnotes/snjs#f922ede72a3e90984605048854dc20db8a88c790"
|
"from": "github:standardnotes/snjs#99d73922326b20e58f914ec9dd666efd6e5e4ac9"
|
||||||
},
|
},
|
||||||
"sockjs": {
|
"sockjs": {
|
||||||
"version": "0.3.20",
|
"version": "0.3.20",
|
||||||
|
|||||||
@@ -68,6 +68,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sncrypto": "github:standardnotes/sncrypto#8794c88daa967eaae493cd5fdec7506d52b257ad",
|
"sncrypto": "github:standardnotes/sncrypto#8794c88daa967eaae493cd5fdec7506d52b257ad",
|
||||||
"snjs": "github:standardnotes/snjs#f922ede72a3e90984605048854dc20db8a88c790"
|
"snjs": "github:standardnotes/snjs#99d73922326b20e58f914ec9dd666efd6e5e4ac9"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user