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: '&'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -138,7 +138,7 @@ class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope {
|
|||||||
running: params?.running,
|
running: params?.running,
|
||||||
error: params?.error,
|
error: params?.error,
|
||||||
subrows: params?.subrows || act?.subrows,
|
subrows: params?.subrows || act?.subrows,
|
||||||
};
|
} as Action;
|
||||||
}
|
}
|
||||||
return act;
|
return act;
|
||||||
});
|
});
|
||||||
@@ -152,7 +152,7 @@ class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope {
|
|||||||
) {
|
) {
|
||||||
const updatedExtension = await this.application.changeItem(extension.uuid, (mutator) => {
|
const updatedExtension = await this.application.changeItem(extension.uuid, (mutator) => {
|
||||||
const extensionMutator = mutator as ActionsExtensionMutator;
|
const extensionMutator = mutator as ActionsExtensionMutator;
|
||||||
extensionMutator.hidden = params && params.hidden;
|
extensionMutator.hidden = Boolean(params?.hidden);
|
||||||
}) as SNActionsExtension;
|
}) as SNActionsExtension;
|
||||||
const extensions = this.state.extensions.map((ext: SNActionsExtension) => {
|
const extensions = this.state.extensions.map((ext: SNActionsExtension) => {
|
||||||
if (extension.uuid === ext.uuid) {
|
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) {
|
if (!success) {
|
||||||
this.application.alertService!.alert(
|
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."
|
: "There was an error changing your password. Please try again."
|
||||||
);
|
);
|
||||||
this.setFormDataState({
|
this.setFormDataState({
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from 'snjs';
|
} from 'snjs';
|
||||||
import template from '%/directives/revision-preview-modal.pug';
|
import template from '%/directives/revision-preview-modal.pug';
|
||||||
import { PayloadContent } from '@node_modules/snjs/dist/@types/protocol/payloads/generator';
|
import { PayloadContent } from '@node_modules/snjs/dist/@types/protocol/payloads/generator';
|
||||||
|
import { confirmDialog } from '@/services/alertService';
|
||||||
|
|
||||||
interface RevisionPreviewScope {
|
interface RevisionPreviewScope {
|
||||||
uuid: string
|
uuid: string
|
||||||
@@ -88,7 +89,7 @@ class RevisionPreviewModalCtrl implements RevisionPreviewScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
restore(asCopy: boolean) {
|
async restore(asCopy: boolean) {
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
if (asCopy) {
|
if (asCopy) {
|
||||||
const contentCopy = Object.assign({}, this.content);
|
const contentCopy = Object.assign({}, this.content);
|
||||||
@@ -109,15 +110,12 @@ class RevisionPreviewModalCtrl implements RevisionPreviewScope {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!asCopy) {
|
if (!asCopy) {
|
||||||
this.application.alertService!.confirm(
|
if (await confirmDialog({
|
||||||
"Are you sure you want to replace the current note's contents with what you see in this preview?",
|
text: "Are you sure you want to replace the current note's contents with what you see in this preview?",
|
||||||
undefined,
|
confirmButtonStyle: 'danger',
|
||||||
undefined,
|
})) {
|
||||||
undefined,
|
run();
|
||||||
run,
|
}
|
||||||
undefined,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { WebDirective } from './../../types';
|
|||||||
import { WebApplication } from '@/ui_models/application';
|
import { WebApplication } from '@/ui_models/application';
|
||||||
import template from '%/directives/session-history-menu.pug';
|
import template from '%/directives/session-history-menu.pug';
|
||||||
import { SNItem, ItemHistoryEntry, ItemHistory } from '@node_modules/snjs/dist/@types';
|
import { SNItem, ItemHistoryEntry, ItemHistory } from '@node_modules/snjs/dist/@types';
|
||||||
|
import { confirmDialog } from '@/services/alertService';
|
||||||
|
|
||||||
interface SessionHistoryScope {
|
interface SessionHistoryScope {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
@@ -57,43 +58,33 @@ class SessionHistoryMenuCtrl implements SessionHistoryScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearItemHistory() {
|
async clearItemHistory() {
|
||||||
this.application.alertService!.confirm(
|
if (await confirmDialog({
|
||||||
"Are you sure you want to delete the local session history for this note?",
|
text: "Are you sure you want to delete the local session history for this note?",
|
||||||
undefined,
|
confirmButtonStyle: 'danger',
|
||||||
undefined,
|
})) {
|
||||||
undefined,
|
this.application.historyManager!.clearHistoryForItem(this.item).then(() => {
|
||||||
() => {
|
this.$timeout(() => {
|
||||||
this.application.historyManager!.clearHistoryForItem(this.item).then(() => {
|
this.reloadHistory();
|
||||||
this.$timeout(() => {
|
|
||||||
this.reloadHistory();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
});
|
||||||
undefined,
|
}
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearAllHistory() {
|
clearAllHistory() {
|
||||||
this.application.alertService!.confirm(
|
if (confirmDialog({
|
||||||
"Are you sure you want to delete the local session history for all notes?",
|
text: "Are you sure you want to delete the local session history for all notes?",
|
||||||
undefined,
|
confirmButtonStyle: 'danger'
|
||||||
undefined,
|
})) {
|
||||||
undefined,
|
this.application.historyManager!.clearAllHistory().then(() => {
|
||||||
() => {
|
this.$timeout(() => {
|
||||||
this.application.historyManager!.clearAllHistory().then(() => {
|
this.reloadHistory();
|
||||||
this.$timeout(() => {
|
|
||||||
this.reloadHistory();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
});
|
||||||
undefined,
|
}
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleDiskSaving() {
|
async toggleDiskSaving() {
|
||||||
const run = () => {
|
const run = () => {
|
||||||
this.application.historyManager!.toggleDiskSaving().then(() => {
|
this.application.historyManager!.toggleDiskSaving().then(() => {
|
||||||
this.$timeout(() => {
|
this.$timeout(() => {
|
||||||
@@ -102,17 +93,14 @@ class SessionHistoryMenuCtrl implements SessionHistoryScope {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
if (!this.application.historyManager!.isDiskEnabled()) {
|
if (!this.application.historyManager!.isDiskEnabled()) {
|
||||||
this.application.alertService!.confirm(
|
if (await confirmDialog({
|
||||||
`Are you sure you want to save history to disk? This will decrease general
|
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
|
performance, especially as you type. You are advised to disable this feature
|
||||||
if you experience any lagging.`,
|
if you experience any lagging.`,
|
||||||
undefined,
|
confirmButtonStyle: 'danger',
|
||||||
undefined,
|
})) {
|
||||||
undefined,
|
run();
|
||||||
run,
|
}
|
||||||
undefined,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
run();
|
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';
|
/// <reference types="pug" />
|
||||||
export declare class EditorPanel extends WebDirective {
|
export class EditorPanel {
|
||||||
constructor();
|
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: {
|
scope: {
|
||||||
shouldFocus: string;
|
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): {
|
export declare function clickOutside($document: ng.IDocumentService): {
|
||||||
restrict: string;
|
restrict: string;
|
||||||
replace: boolean;
|
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): {
|
export declare function delayHide($timeout: ng.ITimeoutService): {
|
||||||
restrict: string;
|
restrict: string;
|
||||||
scope: {
|
scope: {
|
||||||
show: string;
|
show: string;
|
||||||
delay: string;
|
delay: string;
|
||||||
};
|
};
|
||||||
link: (scope: angular.IScope, elem: JQLite) => void;
|
link: (scope: ng.IScope, elem: JQLite) => void;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/// <reference types="angular" />
|
/// <reference types="angular" />
|
||||||
export declare function elemReady($parse: ng.IParseService): {
|
export declare function elemReady($parse: ng.IParseService): {
|
||||||
restrict: string;
|
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: {
|
scope: {
|
||||||
handler: string;
|
handler: string;
|
||||||
};
|
};
|
||||||
link: (scope: import("angular").IScope, element: JQLite) => void;
|
link: (scope: ng.IScope, element: JQLite) => void;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/// <reference types="angular" />
|
/// <reference types="angular" />
|
||||||
export declare function infiniteScroll(): {
|
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" />
|
/// <reference types="angular" />
|
||||||
export declare function lowercase(): {
|
export declare function lowercase(): {
|
||||||
require: string;
|
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" />
|
/// <reference types="angular" />
|
||||||
export declare function selectOnFocus($window: ng.IWindowService): {
|
export declare function selectOnFocus($window: ng.IWindowService): {
|
||||||
restrict: string;
|
restrict: string;
|
||||||
link: (scope: import("angular").IScope, element: JQLite) => void;
|
link: (scope: ng.IScope, element: JQLite) => void;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
/// <reference types="angular" />
|
/// <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 { DeviceInterface, SNApplication } from 'snjs';
|
||||||
|
import { Platform } from './services/platform';
|
||||||
export declare class WebDeviceInterface extends DeviceInterface {
|
export declare class WebDeviceInterface extends DeviceInterface {
|
||||||
|
private platform;
|
||||||
private database;
|
private database;
|
||||||
constructor(namespace: string, timeout: any);
|
constructor(namespace: string, timeout: any, platform: Platform);
|
||||||
setApplication(application: SNApplication): void;
|
setApplication(application: SNApplication): void;
|
||||||
deinit(): void;
|
deinit(): void;
|
||||||
getRawStorageValue(key: string): Promise<string | null>;
|
getRawStorageValue(key: string): Promise<string | null>;
|
||||||
@@ -22,7 +24,7 @@ export declare class WebDeviceInterface extends DeviceInterface {
|
|||||||
saveRawDatabasePayloads(payloads: any[]): Promise<void>;
|
saveRawDatabasePayloads(payloads: any[]): Promise<void>;
|
||||||
removeRawDatabasePayloadWithId(id: string): Promise<void>;
|
removeRawDatabasePayloadWithId(id: string): Promise<void>;
|
||||||
removeAllRawDatabasePayloads(): Promise<void>;
|
removeAllRawDatabasePayloads(): Promise<void>;
|
||||||
getKeychainValue(): Promise<any>;
|
getKeychainValue(): Promise<unknown>;
|
||||||
setKeychainValue(value: any): Promise<void>;
|
setKeychainValue(value: any): Promise<void>;
|
||||||
clearKeychainValue(): Promise<void>;
|
clearKeychainValue(): Promise<void>;
|
||||||
openUrl(url: string): void;
|
openUrl(url: string): void;
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
import { SNAlertService } from 'snjs';
|
import { SNAlertService, ButtonType } from 'snjs';
|
||||||
export declare class AlertService extends SNAlertService {
|
/** @returns a promise resolving to true if the user confirmed, false if they canceled */
|
||||||
alert(text: string, title: string, closeButtonText: string | undefined, onClose: () => void): Promise<unknown>;
|
export declare function confirmDialog({ text, title, confirmButtonText, cancelButtonText, confirmButtonStyle, }: {
|
||||||
confirm(text: string, title: string, confirmButtonText: string | undefined, cancelButtonText: string | undefined, onConfirm: () => void, onCancel: () => void, destructive?: boolean): Promise<unknown>;
|
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 { WebApplication } from '@/ui_models/application';
|
||||||
import { SNItem } from 'snjs';
|
|
||||||
export declare class ArchiveManager {
|
export declare class ArchiveManager {
|
||||||
private readonly application;
|
private readonly application;
|
||||||
private textFile?;
|
private textFile?;
|
||||||
constructor(application: WebApplication);
|
constructor(application: WebApplication);
|
||||||
downloadBackup(encrypted: boolean): Promise<void>;
|
downloadBackup(encrypted: boolean): Promise<void>;
|
||||||
downloadBackupOfItems(items: SNItem[], encrypted: boolean): Promise<void>;
|
|
||||||
private formattedDate;
|
private formattedDate;
|
||||||
private itemsData;
|
private itemsData;
|
||||||
private get zip();
|
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>;
|
onAppLaunch(): Promise<void>;
|
||||||
get webApplication(): WebApplication;
|
get webApplication(): WebApplication;
|
||||||
streamPreferences(): void;
|
streamPreferences(): void;
|
||||||
private loadSingleton;
|
private reloadSingleton;
|
||||||
syncUserPreferences(): void;
|
syncUserPreferences(): void;
|
||||||
getValue(key: WebPrefKey, defaultValue?: any): any;
|
getValue(key: WebPrefKey, defaultValue?: any): any;
|
||||||
setUserPrefValue(key: WebPrefKey, value: any, sync?: boolean): Promise<void>;
|
setUserPrefValue(key: WebPrefKey, value: any, sync?: boolean): Promise<void>;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { WebApplication } from '@/ui_models/application';
|
|||||||
import { ApplicationService, ApplicationEvent } from 'snjs';
|
import { ApplicationService, ApplicationEvent } from 'snjs';
|
||||||
export declare class ThemeManager extends ApplicationService {
|
export declare class ThemeManager extends ApplicationService {
|
||||||
private activeThemes;
|
private activeThemes;
|
||||||
private unsubState;
|
private unsubState?;
|
||||||
private unregisterDesktop;
|
private unregisterDesktop;
|
||||||
private unregisterComponent;
|
private unregisterComponent;
|
||||||
/** @override */
|
/** @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_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";
|
||||||
|
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 */
|
/** @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;
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
import { ComponentGroup } from './component_group';
|
import { ComponentGroup } from './component_group';
|
||||||
import { EditorGroup } from '@/ui_models/editor_group';
|
import { EditorGroup } from '@/ui_models/editor_group';
|
||||||
import { PasswordWizardType } from '@/types';
|
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 { DesktopManager, LockManager, ArchiveManager, NativeExtManager, StatusManager, ThemeManager, PreferencesManager, KeyboardManager } from '@/services';
|
||||||
import { AppState } from '@/ui_models/app_state';
|
import { AppState } from '@/ui_models/app_state';
|
||||||
|
import { Platform } from '@/services/platform';
|
||||||
declare type WebServices = {
|
declare type WebServices = {
|
||||||
appState: AppState;
|
appState: AppState;
|
||||||
desktopService: DesktopManager;
|
desktopService: DesktopManager;
|
||||||
@@ -24,7 +25,7 @@ export declare class WebApplication extends SNApplication {
|
|||||||
private currentAuthenticationElement?;
|
private currentAuthenticationElement?;
|
||||||
editorGroup: EditorGroup;
|
editorGroup: EditorGroup;
|
||||||
componentGroup: ComponentGroup;
|
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 */
|
/** @override */
|
||||||
deinit(): void;
|
deinit(): void;
|
||||||
setWebServices(services: WebServices): void;
|
setWebServices(services: WebServices): void;
|
||||||
@@ -39,7 +40,7 @@ export declare class WebApplication extends SNApplication {
|
|||||||
getKeyboardService(): KeyboardManager;
|
getKeyboardService(): KeyboardManager;
|
||||||
checkForSecurityUpdate(): Promise<boolean>;
|
checkForSecurityUpdate(): Promise<boolean>;
|
||||||
presentPasswordWizard(type: PasswordWizardType): void;
|
presentPasswordWizard(type: PasswordWizardType): void;
|
||||||
promptForChallenge(challenge: Challenge, orchestrator: ChallengeOrchestrator): void;
|
promptForChallenge(challenge: Challenge): void;
|
||||||
performProtocolUpgrade(): Promise<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;
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
/// <reference types="angular" />
|
/// <reference types="angular" />
|
||||||
import { WebApplication } from './application';
|
import { WebApplication } from './application';
|
||||||
|
import { Platform } from '@/services/platform';
|
||||||
declare type AppManagerChangeCallback = () => void;
|
declare type AppManagerChangeCallback = () => void;
|
||||||
export declare class ApplicationGroup {
|
export declare class ApplicationGroup {
|
||||||
|
private platform;
|
||||||
$compile: ng.ICompileService;
|
$compile: ng.ICompileService;
|
||||||
$rootScope: ng.IRootScopeService;
|
$rootScope: ng.IRootScopeService;
|
||||||
$timeout: ng.ITimeoutService;
|
$timeout: ng.ITimeoutService;
|
||||||
applications: WebApplication[];
|
applications: WebApplication[];
|
||||||
changeObservers: AppManagerChangeCallback[];
|
changeObservers: AppManagerChangeCallback[];
|
||||||
activeApplication?: WebApplication;
|
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;
|
private createDefaultApplication;
|
||||||
/** @callback */
|
/** @callback */
|
||||||
onApplicationDeinit(application: WebApplication): void;
|
onApplicationDeinit(application: WebApplication): void;
|
||||||
@@ -20,7 +22,7 @@ export declare class ApplicationGroup {
|
|||||||
* Any application which is no longer active is destroyed, and
|
* Any application which is no longer active is destroyed, and
|
||||||
* must be removed from the interface.
|
* must be removed from the interface.
|
||||||
*/
|
*/
|
||||||
addApplicationChangeObserver(callback: AppManagerChangeCallback): void;
|
addApplicationChangeObserver(callback: AppManagerChangeCallback): () => void;
|
||||||
private notifyObserversOfAppChange;
|
private notifyObserversOfAppChange;
|
||||||
}
|
}
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export declare class Editor {
|
|||||||
* Register to be notified when the editor's note changes.
|
* Register to be notified when the editor's note changes.
|
||||||
*/
|
*/
|
||||||
onNoteChange(callback: () => void): void;
|
onNoteChange(callback: () => void): void;
|
||||||
|
clearNoteChangeListener(): void;
|
||||||
/**
|
/**
|
||||||
* Register to be notified when the editor's note's values change
|
* Register to be notified when the editor's note's values change
|
||||||
* (and thus a new object reference is created)
|
* (and thus a new object reference is created)
|
||||||
@@ -28,5 +29,5 @@ export declare class Editor {
|
|||||||
/**
|
/**
|
||||||
* Sets the editor contents by setting its note.
|
* 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 getParameterByName(name: string, url: string): string | null;
|
||||||
export declare function isNullOrUndefined(value: any): boolean;
|
export declare function isNullOrUndefined(value: any): boolean;
|
||||||
export declare function getPlatformString(): string;
|
export declare function getPlatformString(): string;
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ import { ApplicationEvent } from 'snjs';
|
|||||||
import { WebApplication } from '@/ui_models/application';
|
import { WebApplication } from '@/ui_models/application';
|
||||||
export declare type CtrlState = Partial<Record<string, any>>;
|
export declare type CtrlState = Partial<Record<string, any>>;
|
||||||
export declare type CtrlProps = 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;
|
$timeout: ng.ITimeoutService;
|
||||||
/** Passed through templates */
|
/** Passed through templates */
|
||||||
application: WebApplication;
|
application: WebApplication;
|
||||||
props: CtrlProps;
|
state: S;
|
||||||
state: CtrlState;
|
|
||||||
private unsubApp;
|
private unsubApp;
|
||||||
private unsubState;
|
private unsubState;
|
||||||
private stateTimeout;
|
private stateTimeout?;
|
||||||
constructor($timeout: ng.ITimeoutService);
|
constructor($timeout: ng.ITimeoutService, props?: P);
|
||||||
$onInit(): void;
|
$onInit(): void;
|
||||||
deinit(): void;
|
deinit(): void;
|
||||||
$onDestroy(): void;
|
$onDestroy(): void;
|
||||||
@@ -20,8 +20,8 @@ export declare class PureViewCtrl {
|
|||||||
/** @private */
|
/** @private */
|
||||||
resetState(): Promise<void>;
|
resetState(): Promise<void>;
|
||||||
/** @override */
|
/** @override */
|
||||||
getInitialState(): {};
|
getInitialState(): S;
|
||||||
setState(state: CtrlState): Promise<unknown>;
|
setState(state: Partial<S>): Promise<unknown>;
|
||||||
updateUI(func: () => void): Promise<void>;
|
updateUI(func: () => void): Promise<void>;
|
||||||
initProps(props: CtrlProps): void;
|
initProps(props: CtrlProps): void;
|
||||||
addAppStateObserver(): void;
|
addAppStateObserver(): void;
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ declare class $SanitizeProvider {
|
|||||||
* @returns {boolean|$sanitizeProvider} Returns the currently configured value if called
|
* @returns {boolean|$sanitizeProvider} Returns the currently configured value if called
|
||||||
* without an argument or self for chaining otherwise.
|
* without an argument or self for chaining otherwise.
|
||||||
*/
|
*/
|
||||||
enableSvg: (enableSvg: any) => any;
|
enableSvg: (enableSvg: any) => boolean | any;
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name $sanitizeProvider#addValidElements
|
* @name $sanitizeProvider#addValidElements
|
||||||
@@ -196,7 +196,7 @@ declare class $SanitizeProvider {
|
|||||||
*
|
*
|
||||||
* @return {$sanitizeProvider} Returns self for chaining.
|
* @return {$sanitizeProvider} Returns self for chaining.
|
||||||
*/
|
*/
|
||||||
addValidElements: (elements: Object | string[]) => any;
|
addValidElements: (elements: Array<string> | Object) => any;
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name $sanitizeProvider#addValidAttrs
|
* @name $sanitizeProvider#addValidAttrs
|
||||||
@@ -226,7 +226,7 @@ declare class $SanitizeProvider {
|
|||||||
*
|
*
|
||||||
* @returns {$sanitizeProvider} Returns self for chaining.
|
* @returns {$sanitizeProvider} Returns self for chaining.
|
||||||
*/
|
*/
|
||||||
addValidAttrs: (attrs: string[]) => any;
|
addValidAttrs: (attrs: Array<string>) => any;
|
||||||
}
|
}
|
||||||
declare function sanitizeText(chars: any): string;
|
declare function sanitizeText(chars: any): string;
|
||||||
declare var $sanitizeMinErr: any;
|
declare var $sanitizeMinErr: any;
|
||||||
|
|||||||
Reference in New Issue
Block a user