fix: typescript errors
This commit is contained in:
@@ -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: '&'
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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: '='
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,116 @@
|
||||
import { WebDirective } from './../types';
|
||||
export declare class EditorPanel extends WebDirective {
|
||||
constructor();
|
||||
/// <reference types="pug" />
|
||||
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<void>;
|
||||
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<void>;
|
||||
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<void>;
|
||||
/** @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 {};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import angular from 'angular';
|
||||
/// <reference types="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;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/// <reference types="angular" />
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference types="angular" />
|
||||
export declare function infiniteScroll(): {
|
||||
link: (scope: import("angular").IScope, elem: JQLite, attrs: any) => void;
|
||||
link: (scope: ng.IScope, elem: JQLite, attrs: any) => void;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/// <reference types="angular" />
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/// <reference types="angular" />
|
||||
export declare function selectOnFocus($window: ng.IWindowService): {
|
||||
restrict: string;
|
||||
link: (scope: import("angular").IScope, element: JQLite) => void;
|
||||
link: (scope: ng.IScope, element: JQLite) => void;
|
||||
};
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
/// <reference types="angular" />
|
||||
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;
|
||||
|
||||
@@ -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<string | null>;
|
||||
@@ -22,7 +24,7 @@ export declare class WebDeviceInterface extends DeviceInterface {
|
||||
saveRawDatabasePayloads(payloads: any[]): Promise<void>;
|
||||
removeRawDatabasePayloadWithId(id: string): Promise<void>;
|
||||
removeAllRawDatabasePayloads(): Promise<void>;
|
||||
getKeychainValue(): Promise<any>;
|
||||
getKeychainValue(): Promise<unknown>;
|
||||
setKeychainValue(value: any): Promise<void>;
|
||||
clearKeychainValue(): Promise<void>;
|
||||
openUrl(url: string): void;
|
||||
|
||||
@@ -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<unknown>;
|
||||
confirm(text: string, title: string, confirmButtonText: string | undefined, cancelButtonText: string | undefined, onConfirm: () => void, onCancel: () => void, destructive?: boolean): Promise<unknown>;
|
||||
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<boolean>;
|
||||
export declare function alertDialog({ title, text, closeButtonText, }: {
|
||||
title?: string;
|
||||
text: string;
|
||||
closeButtonText?: string;
|
||||
}): Promise<void>;
|
||||
export declare class AlertService implements SNAlertService {
|
||||
/**
|
||||
* @deprecated use the standalone `alertDialog` function instead
|
||||
*/
|
||||
alert(text: string, title?: string, closeButtonText?: string): Promise<void>;
|
||||
confirm(text: string, title?: string, confirmButtonText?: string, confirmButtonType?: ButtonType, cancelButtonText?: string): Promise<boolean>;
|
||||
blockingDialog(text: string): () => void;
|
||||
}
|
||||
|
||||
@@ -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<void>;
|
||||
downloadBackupOfItems(items: SNItem[], encrypted: boolean): Promise<void>;
|
||||
private formattedDate;
|
||||
private itemsData;
|
||||
private get zip();
|
||||
|
||||
11
dist/@types/app/assets/javascripts/services/platform.d.ts
vendored
Normal file
11
dist/@types/app/assets/javascripts/services/platform.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/** Platform-specific (i-e desktop/web) behavior is handled by a Platform object. */
|
||||
export interface Platform {
|
||||
getKeychainValue(): Promise<unknown>;
|
||||
setKeychainValue(value: any): Promise<void>;
|
||||
clearKeychainValue(): Promise<void>;
|
||||
}
|
||||
export declare class WebPlatform implements Platform {
|
||||
getKeychainValue(): Promise<unknown>;
|
||||
setKeychainValue(value: any): Promise<void>;
|
||||
clearKeychainValue(): Promise<void>;
|
||||
}
|
||||
@@ -7,7 +7,7 @@ export declare class PreferencesManager extends ApplicationService {
|
||||
onAppLaunch(): Promise<void>;
|
||||
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<void>;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<boolean>;
|
||||
presentPasswordWizard(type: PasswordWizardType): void;
|
||||
promptForChallenge(challenge: Challenge, orchestrator: ChallengeOrchestrator): void;
|
||||
promptForChallenge(challenge: Challenge): void;
|
||||
performProtocolUpgrade(): Promise<void>;
|
||||
presentPrivilegesModal(action: ProtectedAction, onSuccess?: any, onCancel?: any): Promise<void>;
|
||||
presentPrivilegesManagementModal(): void;
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
/// <reference types="angular" />
|
||||
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 {};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,16 +3,16 @@ import { ApplicationEvent } from 'snjs';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
export declare type CtrlState = Partial<Record<string, any>>;
|
||||
export declare type CtrlProps = Partial<Record<string, any>>;
|
||||
export declare class PureViewCtrl {
|
||||
export declare class PureViewCtrl<P = CtrlProps, S = CtrlState> {
|
||||
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<void>;
|
||||
/** @override */
|
||||
getInitialState(): {};
|
||||
setState(state: CtrlState): Promise<unknown>;
|
||||
getInitialState(): S;
|
||||
setState(state: Partial<S>): Promise<unknown>;
|
||||
updateUI(func: () => void): Promise<void>;
|
||||
initProps(props: CtrlProps): void;
|
||||
addAppStateObserver(): void;
|
||||
|
||||
@@ -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<string> | 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<string>) => any;
|
||||
}
|
||||
declare function sanitizeText(chars: any): string;
|
||||
declare var $sanitizeMinErr: any;
|
||||
|
||||
Reference in New Issue
Block a user