WIP
This commit is contained in:
@@ -3,6 +3,10 @@
|
|||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import { configRoutes } from './routes';
|
import { configRoutes } from './routes';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Application
|
||||||
|
} from './application';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AppState
|
AppState
|
||||||
} from './state';
|
} from './state';
|
||||||
@@ -54,14 +58,14 @@ import {
|
|||||||
ArchiveManager,
|
ArchiveManager,
|
||||||
AuthManager,
|
AuthManager,
|
||||||
ComponentManager,
|
ComponentManager,
|
||||||
DBManager,
|
DatabaseManager,
|
||||||
DesktopManager,
|
DesktopManager,
|
||||||
HttpManager,
|
HttpManager,
|
||||||
KeyboardManager,
|
KeyboardManager,
|
||||||
MigrationManager,
|
MigrationManager,
|
||||||
ModelManager,
|
ModelManager,
|
||||||
NativeExtManager,
|
NativeExtManager,
|
||||||
PasscodeManager,
|
LockManager,
|
||||||
PrivilegesManager,
|
PrivilegesManager,
|
||||||
SessionHistory,
|
SessionHistory,
|
||||||
SingletonManager,
|
SingletonManager,
|
||||||
@@ -141,20 +145,21 @@ angular
|
|||||||
// Services
|
// Services
|
||||||
angular
|
angular
|
||||||
.module('app')
|
.module('app')
|
||||||
|
.service('application', Application)
|
||||||
.service('appState', AppState)
|
.service('appState', AppState)
|
||||||
.service('preferencesManager', PreferencesManager)
|
.service('preferencesManager', PreferencesManager)
|
||||||
.service('actionsManager', ActionsManager)
|
.service('actionsManager', ActionsManager)
|
||||||
.service('archiveManager', ArchiveManager)
|
.service('archiveManager', ArchiveManager)
|
||||||
.service('authManager', AuthManager)
|
.service('authManager', AuthManager)
|
||||||
.service('componentManager', ComponentManager)
|
.service('componentManager', ComponentManager)
|
||||||
.service('dbManager', DBManager)
|
.service('databaseManager', DatabaseManager)
|
||||||
.service('desktopManager', DesktopManager)
|
.service('desktopManager', DesktopManager)
|
||||||
.service('httpManager', HttpManager)
|
.service('httpManager', HttpManager)
|
||||||
.service('keyboardManager', KeyboardManager)
|
.service('keyboardManager', KeyboardManager)
|
||||||
.service('migrationManager', MigrationManager)
|
.service('migrationManager', MigrationManager)
|
||||||
.service('modelManager', ModelManager)
|
.service('modelManager', ModelManager)
|
||||||
.service('nativeExtManager', NativeExtManager)
|
.service('nativeExtManager', NativeExtManager)
|
||||||
.service('passcodeManager', PasscodeManager)
|
.service('lockManager', LockManager)
|
||||||
.service('privilegesManager', PrivilegesManager)
|
.service('privilegesManager', PrivilegesManager)
|
||||||
.service('sessionHistory', SessionHistory)
|
.service('sessionHistory', SessionHistory)
|
||||||
.service('singletonManager', SingletonManager)
|
.service('singletonManager', SingletonManager)
|
||||||
|
|||||||
55
app/assets/javascripts/application.js
Normal file
55
app/assets/javascripts/application.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import {
|
||||||
|
SNApplication,
|
||||||
|
SNAlertManager
|
||||||
|
Platforms,
|
||||||
|
Environments
|
||||||
|
} from 'snjs';
|
||||||
|
import angular from 'angular';
|
||||||
|
import { AlertManager } from '@/services/alertManager'
|
||||||
|
|
||||||
|
import { WebDeviceInterface } from '@/web_device_interface';
|
||||||
|
|
||||||
|
export class Application extends SNApplication {
|
||||||
|
constructor(
|
||||||
|
desktopManager
|
||||||
|
) {
|
||||||
|
const deviceInterface = new WebDeviceInterface();
|
||||||
|
super({
|
||||||
|
platform: Platforms.Web,
|
||||||
|
namespace: '',
|
||||||
|
host: window._default_sync_server,
|
||||||
|
deviceInterface: deviceInterface,
|
||||||
|
swapClasses: [
|
||||||
|
{
|
||||||
|
swap: SNAlertManager,
|
||||||
|
with: AlertManager
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
this.desktopManager = desktopManager;
|
||||||
|
this.overrideComponentManagerFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
overrideComponentManagerFunctions() {
|
||||||
|
function openModalComponent(component) {
|
||||||
|
var scope = this.$rootScope.$new(true);
|
||||||
|
scope.component = component;
|
||||||
|
var el = this.$compile("<component-modal component='component' class='sk-modal'></component-modal>")(scope);
|
||||||
|
angular.element(document.body).append(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
function presentPermissionsDialog(dialog) {
|
||||||
|
const scope = this.$rootScope.$new(true);
|
||||||
|
scope.permissionsString = dialog.permissionsString;
|
||||||
|
scope.component = dialog.component;
|
||||||
|
scope.callback = dialog.callback;
|
||||||
|
|
||||||
|
var el = this.$compile("<permissions-modal component='component' permissions-string='permissionsString' callback='callback' class='sk-modal'></permissions-modal>")(scope);
|
||||||
|
angular.element(document.body).append(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.componentManager.openModalComponent = openModalComponent;
|
||||||
|
this.componentManager.presentPermissionsDialog = presentPermissionsDialog;
|
||||||
|
this.componentManager.setDesktopManager(this.desktopManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import { SFModelManager } from 'snjs';
|
import {
|
||||||
|
ApplicationEvents,
|
||||||
|
isPayloadSourceRetrieved,
|
||||||
|
CONTENT_TYPE_NOTE,
|
||||||
|
CONTENT_TYPE_TAG,
|
||||||
|
CONTENT_TYPE_COMPONENT,
|
||||||
|
ProtectedActions
|
||||||
|
} from 'snjs';
|
||||||
import { isDesktopApplication } from '@/utils';
|
import { isDesktopApplication } from '@/utils';
|
||||||
import { KeyboardManager } from '@/services/keyboardManager';
|
import { KeyboardManager } from '@/services/keyboardManager';
|
||||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
|
||||||
import template from '%/editor.pug';
|
import template from '%/editor.pug';
|
||||||
import { PureCtrl } from '@Controllers';
|
import { PureCtrl } from '@Controllers';
|
||||||
import {
|
import {
|
||||||
@@ -53,32 +59,22 @@ class EditorCtrl extends PureCtrl {
|
|||||||
constructor(
|
constructor(
|
||||||
$timeout,
|
$timeout,
|
||||||
$rootScope,
|
$rootScope,
|
||||||
alertManager,
|
|
||||||
appState,
|
appState,
|
||||||
authManager,
|
application,
|
||||||
actionsManager,
|
actionsManager,
|
||||||
componentManager,
|
|
||||||
desktopManager,
|
desktopManager,
|
||||||
keyboardManager,
|
keyboardManager,
|
||||||
modelManager,
|
|
||||||
preferencesManager,
|
preferencesManager,
|
||||||
privilegesManager,
|
sessionHistory /** Unused below, required to load globally */
|
||||||
sessionHistory /** Unused below, required to load globally */,
|
|
||||||
syncManager,
|
|
||||||
) {
|
) {
|
||||||
super($timeout);
|
super($timeout);
|
||||||
this.$rootScope = $rootScope;
|
this.$rootScope = $rootScope;
|
||||||
this.alertManager = alertManager;
|
this.application = application;
|
||||||
this.appState = appState;
|
this.appState = appState;
|
||||||
this.actionsManager = actionsManager;
|
this.actionsManager = actionsManager;
|
||||||
this.authManager = authManager;
|
|
||||||
this.componentManager = componentManager;
|
|
||||||
this.desktopManager = desktopManager;
|
this.desktopManager = desktopManager;
|
||||||
this.keyboardManager = keyboardManager;
|
this.keyboardManager = keyboardManager;
|
||||||
this.modelManager = modelManager;
|
|
||||||
this.preferencesManager = preferencesManager;
|
this.preferencesManager = preferencesManager;
|
||||||
this.privilegesManager = privilegesManager;
|
|
||||||
this.syncManager = syncManager;
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
componentStack: [],
|
componentStack: [],
|
||||||
@@ -94,9 +90,9 @@ class EditorCtrl extends PureCtrl {
|
|||||||
this.rightResizeControl = {};
|
this.rightResizeControl = {};
|
||||||
|
|
||||||
this.addAppStateObserver();
|
this.addAppStateObserver();
|
||||||
this.addSyncEventHandler();
|
this.addAppEventObserver();
|
||||||
this.addSyncStatusObserver();
|
this.addSyncStatusObserver();
|
||||||
this.addMappingObservers();
|
this.streamItems();
|
||||||
this.registerComponentHandler();
|
this.registerComponentHandler();
|
||||||
this.registerKeyboardShortcuts();
|
this.registerKeyboardShortcuts();
|
||||||
|
|
||||||
@@ -119,6 +115,111 @@ class EditorCtrl extends PureCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
streamItems() {
|
||||||
|
this.application.streamItems({
|
||||||
|
contentType: CONTENT_TYPE_NOTE,
|
||||||
|
stream: async ({ items, source }) => {
|
||||||
|
if (!this.state.note) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.state.note.deleted || this.state.note.content.trashed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isPayloadSourceRetrieved(source)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const matchingNote = items.find((item) => {
|
||||||
|
return item.uuid === this.state.note.uuid;
|
||||||
|
});
|
||||||
|
if (!matchingNote) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.reloadTagsString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.application.streamItems({
|
||||||
|
contentType: CONTENT_TYPE_TAG,
|
||||||
|
stream: async ({ items, source }) => {
|
||||||
|
if (!this.state.note) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const tag of items) {
|
||||||
|
if (
|
||||||
|
!this.state.note.savedTagsString ||
|
||||||
|
tag.deleted ||
|
||||||
|
tag.hasRelationshipWithItem(this.state.note)
|
||||||
|
) {
|
||||||
|
this.reloadTagsString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.application.streamItems({
|
||||||
|
contentType: CONTENT_TYPE_COMPONENT,
|
||||||
|
stream: async ({ items, source }) => {
|
||||||
|
if (!this.state.note) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/** Reload componentStack in case new ones were added or removed */
|
||||||
|
this.reloadComponentStackArray();
|
||||||
|
/** Observe editor changes to see if the current note should update its editor */
|
||||||
|
const editors = items.filter(function (item) {
|
||||||
|
return item.isEditor();
|
||||||
|
});
|
||||||
|
if (editors.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/** Find the most recent editor for note */
|
||||||
|
const editor = this.editorForNote(this.state.note);
|
||||||
|
this.setState({
|
||||||
|
selectedEditor: editor
|
||||||
|
});
|
||||||
|
if (!editor) {
|
||||||
|
this.reloadFont();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addAppEventObserver() {
|
||||||
|
this.application.addEventObserver((eventName) => {
|
||||||
|
if (!this.state.note) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eventName === ApplicationEvents.HighLatencySync) {
|
||||||
|
this.setState({
|
||||||
|
syncTakingTooLong: true
|
||||||
|
});
|
||||||
|
} else if (eventName === ApplicationEvents.CompletedSync) {
|
||||||
|
this.setState({
|
||||||
|
syncTakingTooLong: false
|
||||||
|
});
|
||||||
|
if (this.state.note.dirty) {
|
||||||
|
/** if we're still dirty, don't change status, a sync is likely upcoming. */
|
||||||
|
} else {
|
||||||
|
const saved = this.state.note.updated_at > this.state.note.lastSyncBegan;
|
||||||
|
const isInErrorState = this.state.saveError;
|
||||||
|
if (isInErrorState || saved) {
|
||||||
|
this.showAllChangesSavedStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (eventName === ApplicationEvents.FailedSync) {
|
||||||
|
/**
|
||||||
|
* Only show error status in editor if the note is dirty.
|
||||||
|
* Otherwise, it means the originating sync came from somewhere else
|
||||||
|
* and we don't want to display an error here.
|
||||||
|
*/
|
||||||
|
if (this.state.note.dirty) {
|
||||||
|
this.showErrorStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async handleNoteSelectionChange(note, previousNote) {
|
async handleNoteSelectionChange(note, previousNote) {
|
||||||
this.setState({
|
this.setState({
|
||||||
note: this.appState.getSelectedNote(),
|
note: this.appState.getSelectedNote(),
|
||||||
@@ -164,124 +265,19 @@ class EditorCtrl extends PureCtrl {
|
|||||||
this.reloadComponentContext();
|
this.reloadComponentContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
addMappingObservers() {
|
|
||||||
this.modelManager.addItemSyncObserver(
|
|
||||||
'editor-note-observer',
|
|
||||||
'Note',
|
|
||||||
(allItems, validItems, deletedItems, source) => {
|
|
||||||
if (!this.state.note) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.state.note.deleted || this.state.note.content.trashed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!SFModelManager.isMappingSourceRetrieved(source)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const matchingNote = allItems.find((item) => {
|
|
||||||
return item.uuid === this.state.note.uuid;
|
|
||||||
});
|
|
||||||
if (!matchingNote) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.reloadTagsString();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.modelManager.addItemSyncObserver(
|
|
||||||
'editor-tag-observer',
|
|
||||||
'Tag',
|
|
||||||
(allItems, validItems, deletedItems, source) => {
|
|
||||||
if (!this.state.note) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const tag of allItems) {
|
|
||||||
if (
|
|
||||||
!this.state.note.savedTagsString ||
|
|
||||||
tag.deleted ||
|
|
||||||
tag.hasRelationshipWithItem(this.state.note)
|
|
||||||
) {
|
|
||||||
this.reloadTagsString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.modelManager.addItemSyncObserver(
|
|
||||||
'editor-component-observer',
|
|
||||||
'SN|Component',
|
|
||||||
(allItems, validItems, deletedItems, source) => {
|
|
||||||
if (!this.state.note) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/** Reload componentStack in case new ones were added or removed */
|
|
||||||
this.reloadComponentStackArray();
|
|
||||||
/** Observe editor changes to see if the current note should update its editor */
|
|
||||||
const editors = allItems.filter(function (item) {
|
|
||||||
return item.isEditor();
|
|
||||||
});
|
|
||||||
if (editors.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/** Find the most recent editor for note */
|
|
||||||
const editor = this.editorForNote(this.state.note);
|
|
||||||
this.setState({
|
|
||||||
selectedEditor: editor
|
|
||||||
});
|
|
||||||
if (!editor) {
|
|
||||||
this.reloadFont();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addSyncEventHandler() {
|
|
||||||
this.syncManager.addEventHandler((eventName, data) => {
|
|
||||||
if (!this.state.note) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (eventName === 'sync:taking-too-long') {
|
|
||||||
this.setState({
|
|
||||||
syncTakingTooLong: true
|
|
||||||
});
|
|
||||||
} else if (eventName === 'sync:completed') {
|
|
||||||
this.setState({
|
|
||||||
syncTakingTooLong: false
|
|
||||||
});
|
|
||||||
if (this.state.note.dirty) {
|
|
||||||
/** if we're still dirty, don't change status, a sync is likely upcoming. */
|
|
||||||
} else {
|
|
||||||
const savedItem = data.savedItems.find((item) => {
|
|
||||||
return item.uuid === this.state.note.uuid;
|
|
||||||
});
|
|
||||||
const isInErrorState = this.state.saveError;
|
|
||||||
if (isInErrorState || savedItem) {
|
|
||||||
this.showAllChangesSavedStatus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (eventName === 'sync:error') {
|
|
||||||
/**
|
|
||||||
* Only show error status in editor if the note is dirty.
|
|
||||||
* Otherwise, it means the originating sync came from somewhere else
|
|
||||||
* and we don't want to display an error here.
|
|
||||||
*/
|
|
||||||
if (this.state.note.dirty) {
|
|
||||||
this.showErrorStatus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addSyncStatusObserver() {
|
addSyncStatusObserver() {
|
||||||
this.syncStatusObserver = this.syncManager.
|
/** @todo */
|
||||||
registerSyncStatusObserver((status) => {
|
// this.syncStatusObserver = this.syncManager.
|
||||||
if (status.localError) {
|
// registerSyncStatusObserver((status) => {
|
||||||
this.$timeout(() => {
|
// if (status.localError) {
|
||||||
this.showErrorStatus({
|
// this.$timeout(() => {
|
||||||
message: "Offline Saving Issue",
|
// this.showErrorStatus({
|
||||||
desc: "Changes not saved"
|
// message: "Offline Saving Issue",
|
||||||
});
|
// desc: "Changes not saved"
|
||||||
}, 500);
|
// });
|
||||||
}
|
// }, 500);
|
||||||
});
|
// }
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
editorForNote(note) {
|
editorForNote(note) {
|
||||||
@@ -332,7 +328,7 @@ class EditorCtrl extends PureCtrl {
|
|||||||
APP_DATA_KEY_PREFERS_PLAIN_EDITOR,
|
APP_DATA_KEY_PREFERS_PLAIN_EDITOR,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
this.modelManager.setItemDirty(this.state.note);
|
this.application.setItemNeedsSync({ item: this.state.note });
|
||||||
}
|
}
|
||||||
this.associateComponentWithCurrentNote(editor);
|
this.associateComponentWithCurrentNote(editor);
|
||||||
} else {
|
} else {
|
||||||
@@ -342,7 +338,7 @@ class EditorCtrl extends PureCtrl {
|
|||||||
APP_DATA_KEY_PREFERS_PLAIN_EDITOR,
|
APP_DATA_KEY_PREFERS_PLAIN_EDITOR,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
this.modelManager.setItemDirty(this.state.note);
|
this.application.setItemNeedsSync({ item: this.state.note });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reloadFont();
|
this.reloadFont();
|
||||||
@@ -356,7 +352,7 @@ class EditorCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Dirtying can happen above */
|
/** Dirtying can happen above */
|
||||||
this.syncManager.sync();
|
this.application.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
hasAvailableExtensions() {
|
hasAvailableExtensions() {
|
||||||
@@ -383,13 +379,13 @@ class EditorCtrl extends PureCtrl {
|
|||||||
const note = this.state.note;
|
const note = this.state.note;
|
||||||
note.dummy = false;
|
note.dummy = false;
|
||||||
if (note.deleted) {
|
if (note.deleted) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: STRING_DELETED_NOTE
|
text: STRING_DELETED_NOTE
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.modelManager.findItem(note.uuid)) {
|
if (!this.application.findItem({ uuid: note.uuid })) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: STRING_INVALID_NOTE
|
text: STRING_INVALID_NOTE
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -405,28 +401,20 @@ class EditorCtrl extends PureCtrl {
|
|||||||
note.content.preview_plain = previewPlain;
|
note.content.preview_plain = previewPlain;
|
||||||
note.content.preview_html = null;
|
note.content.preview_html = null;
|
||||||
}
|
}
|
||||||
this.modelManager.setItemDirty(
|
this.application.setItemNeedsSync({
|
||||||
note,
|
item: note,
|
||||||
true,
|
updateUserModifiedDate: updateClientModified
|
||||||
updateClientModified
|
});
|
||||||
);
|
|
||||||
if (this.saveTimeout) {
|
if (this.saveTimeout) {
|
||||||
this.$timeout.cancel(this.saveTimeout);
|
this.$timeout.cancel(this.saveTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
const noDebounce = bypassDebouncer || this.authManager.offline();
|
const noDebounce = bypassDebouncer || this.application.noAccount();
|
||||||
const syncDebouceMs = noDebounce
|
const syncDebouceMs = noDebounce
|
||||||
? SAVE_TIMEOUT_NO_DEBOUNCE
|
? SAVE_TIMEOUT_NO_DEBOUNCE
|
||||||
: SAVE_TIMEOUT_DEBOUNCE;
|
: SAVE_TIMEOUT_DEBOUNCE;
|
||||||
this.saveTimeout = this.$timeout(() => {
|
this.saveTimeout = this.$timeout(() => {
|
||||||
this.syncManager.sync().then((response) => {
|
this.application.sync();
|
||||||
if (response && response.error && !this.didShowErrorAlert) {
|
|
||||||
this.didShowErrorAlert = true;
|
|
||||||
this.alertManager.alert({
|
|
||||||
text: STRING_GENERIC_SAVE_ERROR
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, syncDebouceMs);
|
}, syncDebouceMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,7 +431,7 @@ class EditorCtrl extends PureCtrl {
|
|||||||
syncTakingTooLong: false
|
syncTakingTooLong: false
|
||||||
});
|
});
|
||||||
let status = "All changes saved";
|
let status = "All changes saved";
|
||||||
if (this.authManager.offline()) {
|
if (this.application.noAccount()) {
|
||||||
status += " (offline)";
|
status += " (offline)";
|
||||||
}
|
}
|
||||||
this.setStatus(
|
this.setStatus(
|
||||||
@@ -542,14 +530,14 @@ class EditorCtrl extends PureCtrl {
|
|||||||
|
|
||||||
async deleteNote(permanently) {
|
async deleteNote(permanently) {
|
||||||
if (this.state.note.dummy) {
|
if (this.state.note.dummy) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: STRING_DELETE_PLACEHOLDER_ATTEMPT
|
text: STRING_DELETE_PLACEHOLDER_ATTEMPT
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const run = () => {
|
const run = () => {
|
||||||
if (this.state.note.locked) {
|
if (this.state.note.locked) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: STRING_DELETE_LOCKED_ATTEMPT
|
text: STRING_DELETE_LOCKED_ATTEMPT
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -561,7 +549,7 @@ class EditorCtrl extends PureCtrl {
|
|||||||
title: title,
|
title: title,
|
||||||
permanently: permanently
|
permanently: permanently
|
||||||
});
|
});
|
||||||
this.alertManager.confirm({
|
this.application.alertManager.confirm({
|
||||||
text: text,
|
text: text,
|
||||||
destructive: true,
|
destructive: true,
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
@@ -579,12 +567,12 @@ class EditorCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const requiresPrivilege = await this.privilegesManager.actionRequiresPrivilege(
|
const requiresPrivilege = await this.application.privilegesManager.actionRequiresPrivilege(
|
||||||
PrivilegesManager.ActionDeleteNote
|
ProtectedActions.DeleteNote
|
||||||
);
|
);
|
||||||
if (requiresPrivilege) {
|
if (requiresPrivilege) {
|
||||||
this.privilegesManager.presentPrivilegesModal(
|
this.godService.presentPrivilegesModal(
|
||||||
PrivilegesManager.ActionDeleteNote,
|
ProtectedActions.DeleteNote,
|
||||||
() => {
|
() => {
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
@@ -595,17 +583,17 @@ class EditorCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
performNoteDeletion(note) {
|
performNoteDeletion(note) {
|
||||||
this.modelManager.setItemToBeDeleted(note);
|
this.application.deleteItem({ item: note });
|
||||||
if (note === this.state.note) {
|
if (note === this.state.note) {
|
||||||
this.setState({
|
this.setState({
|
||||||
note: null
|
note: null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (note.dummy) {
|
if (note.dummy) {
|
||||||
this.modelManager.removeItemLocally(note);
|
this.application.deleteItemLocally({ item: note });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.syncManager.sync();
|
this.application.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreTrashedNote() {
|
restoreTrashedNote() {
|
||||||
@@ -622,17 +610,17 @@ class EditorCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTrashCount() {
|
getTrashCount() {
|
||||||
return this.modelManager.trashedItems().length;
|
return this.application.getTrashedItems().length;
|
||||||
}
|
}
|
||||||
|
|
||||||
emptyTrash() {
|
emptyTrash() {
|
||||||
const count = this.getTrashCount();
|
const count = this.getTrashCount();
|
||||||
this.alertManager.confirm({
|
this.application.alertManager.confirm({
|
||||||
text: StringEmptyTrash({ count }),
|
text: StringEmptyTrash({ count }),
|
||||||
destructive: true,
|
destructive: true,
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
this.modelManager.emptyTrash();
|
this.application.emptyTrash();
|
||||||
this.syncManager.sync();
|
this.application.sync();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -666,12 +654,12 @@ class EditorCtrl extends PureCtrl {
|
|||||||
dontUpdatePreviews: true
|
dontUpdatePreviews: true
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Show privilegesManager if protection is not yet set up */
|
/** Show privileges manager if protection is not yet set up */
|
||||||
this.privilegesManager.actionHasPrivilegesConfigured(
|
this.application.privilegesManager.actionHasPrivilegesConfigured(
|
||||||
PrivilegesManager.ActionViewProtectedNotes
|
ProtectedActions.ViewProtectedNotes
|
||||||
).then((configured) => {
|
).then((configured) => {
|
||||||
if (!configured) {
|
if (!configured) {
|
||||||
this.privilegesManager.presentPrivilegesManagementModal();
|
this.godService.presentPrivilegesManagementModal();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -709,7 +697,7 @@ class EditorCtrl extends PureCtrl {
|
|||||||
return currentTag.title;
|
return currentTag.title;
|
||||||
});
|
});
|
||||||
strings.push(tag.title);
|
strings.push(tag.title);
|
||||||
this.saveTags({ strings: strings});
|
this.saveTags({ strings: strings });
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTag(tag) {
|
removeTag(tag) {
|
||||||
@@ -721,7 +709,7 @@ class EditorCtrl extends PureCtrl {
|
|||||||
this.saveTags({ strings: strings });
|
this.saveTags({ strings: strings });
|
||||||
}
|
}
|
||||||
|
|
||||||
saveTags({strings} = {}) {
|
saveTags({ strings } = {}) {
|
||||||
if (!strings && this.state.mutable.tagsString === this.state.note.tagsString()) {
|
if (!strings && this.state.mutable.tagsString === this.state.note.tagsString()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -743,7 +731,7 @@ class EditorCtrl extends PureCtrl {
|
|||||||
for (const tagToRemove of toRemove) {
|
for (const tagToRemove of toRemove) {
|
||||||
tagToRemove.removeItemAsRelationship(this.state.note);
|
tagToRemove.removeItemAsRelationship(this.state.note);
|
||||||
}
|
}
|
||||||
this.modelManager.setItemsDirty(toRemove);
|
this.application.setItemsNeedsSync({ items: toRemove });
|
||||||
const tags = [];
|
const tags = [];
|
||||||
for (const tagString of strings) {
|
for (const tagString of strings) {
|
||||||
const existingRelationship = _.find(
|
const existingRelationship = _.find(
|
||||||
@@ -752,15 +740,14 @@ class EditorCtrl extends PureCtrl {
|
|||||||
);
|
);
|
||||||
if (!existingRelationship) {
|
if (!existingRelationship) {
|
||||||
tags.push(
|
tags.push(
|
||||||
this.modelManager.findOrCreateTagByTitle(tagString)
|
this.application.findOrCreateTag({ title: tagString })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const tag of tags) {
|
for (const tag of tags) {
|
||||||
tag.addItemAsRelationship(this.state.note);
|
tag.addItemAsRelationship(this.state.note);
|
||||||
}
|
}
|
||||||
this.modelManager.setItemsDirty(tags);
|
this.application.saveItems({ items: tags });
|
||||||
this.syncManager.sync();
|
|
||||||
this.reloadTagsString();
|
this.reloadTagsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -977,12 +964,12 @@ class EditorCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
else if (action === 'associate-item') {
|
else if (action === 'associate-item') {
|
||||||
if (data.item.content_type === 'Tag') {
|
if (data.item.content_type === 'Tag') {
|
||||||
const tag = this.modelManager.findItem(data.item.uuid);
|
const tag = this.application.findItem({ uuid: data.item.uuid });
|
||||||
this.addTag(tag);
|
this.addTag(tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (action === 'deassociate-item') {
|
else if (action === 'deassociate-item') {
|
||||||
const tag = this.modelManager.findItem(data.item.uuid);
|
const tag = this.application.findItem({ uuid: data.item.uuid });
|
||||||
this.removeTag(tag);
|
this.removeTag(tag);
|
||||||
}
|
}
|
||||||
else if (action === 'save-items') {
|
else if (action === 'save-items') {
|
||||||
@@ -1049,8 +1036,7 @@ class EditorCtrl extends PureCtrl {
|
|||||||
component.disassociatedItemIds.push(this.state.note.uuid);
|
component.disassociatedItemIds.push(this.state.note.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.modelManager.setItemDirty(component);
|
this.application.saveItem({ item: component });
|
||||||
this.syncManager.sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
associateComponentWithCurrentNote(component) {
|
associateComponentWithCurrentNote(component) {
|
||||||
@@ -1063,8 +1049,7 @@ class EditorCtrl extends PureCtrl {
|
|||||||
component.associatedItemIds.push(this.state.note.uuid);
|
component.associatedItemIds.push(this.state.note.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.modelManager.setItemDirty(component);
|
this.application.saveItem({ item: component });
|
||||||
this.syncManager.sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerKeyboardShortcuts() {
|
registerKeyboardShortcuts() {
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
|
||||||
import { dateToLocalizedString } from '@/utils';
|
import { dateToLocalizedString } from '@/utils';
|
||||||
|
import {
|
||||||
|
ApplicationEvents,
|
||||||
|
TIMING_STRATEGY_FORCE_SPAWN_NEW,
|
||||||
|
ProtectedActions
|
||||||
|
} from 'snjs';
|
||||||
import template from '%/footer.pug';
|
import template from '%/footer.pug';
|
||||||
import {
|
import {
|
||||||
APP_STATE_EVENT_EDITOR_FOCUSED,
|
APP_STATE_EVENT_EDITOR_FOCUSED,
|
||||||
@@ -18,29 +22,19 @@ class FooterCtrl {
|
|||||||
constructor(
|
constructor(
|
||||||
$rootScope,
|
$rootScope,
|
||||||
$timeout,
|
$timeout,
|
||||||
alertManager,
|
|
||||||
appState,
|
appState,
|
||||||
authManager,
|
application,
|
||||||
componentManager,
|
|
||||||
modelManager,
|
|
||||||
nativeExtManager,
|
nativeExtManager,
|
||||||
passcodeManager,
|
|
||||||
privilegesManager,
|
|
||||||
statusManager,
|
statusManager,
|
||||||
syncManager,
|
godService
|
||||||
) {
|
) {
|
||||||
this.$rootScope = $rootScope;
|
this.$rootScope = $rootScope;
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
this.alertManager = alertManager;
|
this.application = application;
|
||||||
this.appState = appState;
|
this.appState = appState;
|
||||||
this.authManager = authManager;
|
|
||||||
this.componentManager = componentManager;
|
|
||||||
this.modelManager = modelManager;
|
|
||||||
this.nativeExtManager = nativeExtManager;
|
this.nativeExtManager = nativeExtManager;
|
||||||
this.passcodeManager = passcodeManager;
|
|
||||||
this.privilegesManager = privilegesManager;
|
|
||||||
this.statusManager = statusManager;
|
this.statusManager = statusManager;
|
||||||
this.syncManager = syncManager;
|
this.godService = godService;
|
||||||
|
|
||||||
this.rooms = [];
|
this.rooms = [];
|
||||||
this.themesWithIcons = [];
|
this.themesWithIcons = [];
|
||||||
@@ -48,13 +42,13 @@ class FooterCtrl {
|
|||||||
|
|
||||||
this.addAppStateObserver();
|
this.addAppStateObserver();
|
||||||
this.updateOfflineStatus();
|
this.updateOfflineStatus();
|
||||||
this.addSyncEventHandler();
|
this.addAppEventObserver();
|
||||||
this.findErrors();
|
this.findErrors();
|
||||||
this.registerMappingObservers();
|
this.streamItems();
|
||||||
this.registerComponentHandler();
|
this.registerComponentHandler();
|
||||||
this.addRootScopeListeners();
|
this.addRootScopeListeners();
|
||||||
|
|
||||||
this.authManager.checkForSecurityUpdate().then((available) => {
|
this.godService.checkForSecurityUpdate().then((available) => {
|
||||||
this.securityUpdateAvailable = available;
|
this.securityUpdateAvailable = available;
|
||||||
});
|
});
|
||||||
this.statusManager.addStatusObserver((string) => {
|
this.statusManager.addStatusObserver((string) => {
|
||||||
@@ -66,7 +60,7 @@ class FooterCtrl {
|
|||||||
|
|
||||||
addRootScopeListeners() {
|
addRootScopeListeners() {
|
||||||
this.$rootScope.$on("security-update-status-changed", () => {
|
this.$rootScope.$on("security-update-status-changed", () => {
|
||||||
this.securityUpdateAvailable = this.authManager.securityUpdateAvailable;
|
this.securityUpdateAvailable = this.godService.securityUpdateAvailable;
|
||||||
});
|
});
|
||||||
this.$rootScope.$on("reload-ext-data", () => {
|
this.$rootScope.$on("reload-ext-data", () => {
|
||||||
this.reloadExtendedData();
|
this.reloadExtendedData();
|
||||||
@@ -108,35 +102,34 @@ class FooterCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addSyncEventHandler() {
|
addAppEventObserver() {
|
||||||
this.syncManager.addEventHandler((syncEvent, data) => {
|
this.application.addEventHandler((eventName) => {
|
||||||
this.$timeout(() => {
|
if (eventName === ApplicationEvents.LoadedLocalData) {
|
||||||
if(syncEvent === "local-data-loaded") {
|
if(this.offline && this.application.getNoteCount() === 0) {
|
||||||
if(this.offline && this.modelManager.noteCount() === 0) {
|
this.showAccountMenu = true;
|
||||||
this.showAccountMenu = true;
|
|
||||||
}
|
|
||||||
} else if(syncEvent === "enter-out-of-sync") {
|
|
||||||
this.outOfSync = true;
|
|
||||||
} else if(syncEvent === "exit-out-of-sync") {
|
|
||||||
this.outOfSync = false;
|
|
||||||
} else if(syncEvent === 'sync:completed') {
|
|
||||||
this.syncUpdated();
|
|
||||||
this.findErrors();
|
|
||||||
this.updateOfflineStatus();
|
|
||||||
} else if(syncEvent === 'sync:error') {
|
|
||||||
this.findErrors();
|
|
||||||
this.updateOfflineStatus();
|
|
||||||
}
|
}
|
||||||
});
|
} else if (eventName === ApplicationEvents.EnteredOutOfSync) {
|
||||||
|
this.outOfSync = true;
|
||||||
|
} else if (eventName === ApplicationEvents.ExitedOutOfSync) {
|
||||||
|
this.outOfSync = false;
|
||||||
|
} else if (eventName === ApplicationEvents.CompletedSync) {
|
||||||
|
this.syncUpdated();
|
||||||
|
this.findErrors();
|
||||||
|
this.updateOfflineStatus();
|
||||||
|
} else if (eventName === ApplicationEvents.FailedSync) {
|
||||||
|
this.findErrors();
|
||||||
|
this.updateOfflineStatus();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
registerMappingObservers() {
|
streamItems() {
|
||||||
this.modelManager.addItemSyncObserver(
|
this.application.streamItems({
|
||||||
'room-bar',
|
contentType: CONTENT_TYPE_COMPONENT,
|
||||||
'SN|Component',
|
stream: async () => {
|
||||||
(allItems, validItems, deletedItems, source) => {
|
this.rooms = this.application.getItems({
|
||||||
this.rooms = this.modelManager.components.filter((candidate) => {
|
contentType: CONTENT_TYPE_COMPONENT
|
||||||
|
}).filter((candidate) => {
|
||||||
return candidate.area === 'rooms' && !candidate.deleted;
|
return candidate.area === 'rooms' && !candidate.deleted;
|
||||||
});
|
});
|
||||||
if(this.queueExtReload) {
|
if(this.queueExtReload) {
|
||||||
@@ -144,14 +137,14 @@ class FooterCtrl {
|
|||||||
this.reloadExtendedData();
|
this.reloadExtendedData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
this.modelManager.addItemSyncObserver(
|
this.application.streamItems({
|
||||||
'footer-bar-themes',
|
contentType: 'SN|Theme',
|
||||||
'SN|Theme',
|
stream: async () => {
|
||||||
(allItems, validItems, deletedItems, source) => {
|
const themes = this.application.getDisplayableItems({
|
||||||
const themes = this.modelManager.validItemsForContentType('SN|Theme')
|
contentType: CONTENT_TYPE_THEME
|
||||||
.filter((candidate) => {
|
}).filter((candidate) => {
|
||||||
return (
|
return (
|
||||||
!candidate.deleted &&
|
!candidate.deleted &&
|
||||||
candidate.content.package_info &&
|
candidate.content.package_info &&
|
||||||
@@ -170,7 +163,7 @@ class FooterCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerComponentHandler() {
|
registerComponentHandler() {
|
||||||
this.componentManager.registerHandler({
|
this.application.componentManager.registerHandler({
|
||||||
identifier: "roomBar",
|
identifier: "roomBar",
|
||||||
areas: ["rooms", "modal"],
|
areas: ["rooms", "modal"],
|
||||||
activationHandler: (component) => {},
|
activationHandler: (component) => {},
|
||||||
@@ -215,19 +208,19 @@ class FooterCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getUser() {
|
getUser() {
|
||||||
return this.authManager.user;
|
return this.application.getUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOfflineStatus() {
|
updateOfflineStatus() {
|
||||||
this.offline = this.authManager.offline();
|
this.offline = this.application.noUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
openSecurityUpdate() {
|
openSecurityUpdate() {
|
||||||
this.authManager.presentPasswordWizard('upgrade-security');
|
this.godService.presentPasswordWizard('upgrade-security');
|
||||||
}
|
}
|
||||||
|
|
||||||
findErrors() {
|
findErrors() {
|
||||||
this.error = this.syncManager.syncStatus.error;
|
this.error = this.application.getSyncStatus().error;
|
||||||
}
|
}
|
||||||
|
|
||||||
accountMenuPressed() {
|
accountMenuPressed() {
|
||||||
@@ -244,7 +237,7 @@ class FooterCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hasPasscode() {
|
hasPasscode() {
|
||||||
return this.passcodeManager.hasPasscode();
|
return this.application.hasPasscode();
|
||||||
}
|
}
|
||||||
|
|
||||||
lockApp() {
|
lockApp() {
|
||||||
@@ -253,15 +246,15 @@ class FooterCtrl {
|
|||||||
|
|
||||||
refreshData() {
|
refreshData() {
|
||||||
this.isRefreshing = true;
|
this.isRefreshing = true;
|
||||||
this.syncManager.sync({
|
this.application.sync({
|
||||||
force: true,
|
timingStrategy: TIMING_STRATEGY_FORCE_SPAWN_NEW,
|
||||||
performIntegrityCheck: true
|
checkIntegrity: true
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
this.$timeout(() => {
|
this.$timeout(() => {
|
||||||
this.isRefreshing = false;
|
this.isRefreshing = false;
|
||||||
}, 200);
|
}, 200);
|
||||||
if(response && response.error) {
|
if(response && response.error) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: STRING_GENERIC_SYNC_ERROR
|
text: STRING_GENERIC_SYNC_ERROR
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -280,7 +273,7 @@ class FooterCtrl {
|
|||||||
|
|
||||||
clickedNewUpdateAnnouncement() {
|
clickedNewUpdateAnnouncement() {
|
||||||
this.newUpdateAvailable = false;
|
this.newUpdateAvailable = false;
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: STRING_NEW_UPDATE_READY
|
text: STRING_NEW_UPDATE_READY
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -324,7 +317,7 @@ class FooterCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectShortcut(shortcut) {
|
selectShortcut(shortcut) {
|
||||||
this.componentManager.toggleComponent(shortcut.component);
|
this.application.componentManager.toggleComponent(shortcut.component);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRoomDismiss(room) {
|
onRoomDismiss(room) {
|
||||||
@@ -345,12 +338,12 @@ class FooterCtrl {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if(!room.showRoom) {
|
if(!room.showRoom) {
|
||||||
const requiresPrivilege = await this.privilegesManager.actionRequiresPrivilege(
|
const requiresPrivilege = await this.application.privilegesManager.actionRequiresPrivilege(
|
||||||
PrivilegesManager.ActionManageExtensions
|
ProtectedActions.ManageExtensions
|
||||||
);
|
);
|
||||||
if(requiresPrivilege) {
|
if(requiresPrivilege) {
|
||||||
this.privilegesManager.presentPrivilegesModal(
|
this.godService.presentPrivilegesModal(
|
||||||
PrivilegesManager.ActionManageExtensions,
|
ProtectedActions.ManageExtensions,
|
||||||
run
|
run
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -362,7 +355,7 @@ class FooterCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clickOutsideAccountMenu() {
|
clickOutsideAccountMenu() {
|
||||||
if(this.privilegesManager.authenticationInProgress()) {
|
if(this.application.privilegesManager.authenticationInProgress()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.showAccountMenu = false;
|
this.showAccountMenu = false;
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import template from '%/lock-screen.pug';
|
import template from '%/lock-screen.pug';
|
||||||
|
import {
|
||||||
|
APP_STATE_EVENT_WINDOW_DID_FOCUS
|
||||||
|
} from '@/state';
|
||||||
|
|
||||||
const ELEMENT_ID_PASSCODE_INPUT = 'passcode-input';
|
const ELEMENT_ID_PASSCODE_INPUT = 'passcode-input';
|
||||||
|
|
||||||
@@ -8,15 +11,14 @@ class LockScreenCtrl {
|
|||||||
constructor(
|
constructor(
|
||||||
$scope,
|
$scope,
|
||||||
alertManager,
|
alertManager,
|
||||||
authManager,
|
application,
|
||||||
passcodeManager,
|
appState
|
||||||
) {
|
) {
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
this.alertManager = alertManager;
|
this.alertManager = alertManager;
|
||||||
this.authManager = authManager;
|
this.application = application;
|
||||||
this.passcodeManager = passcodeManager;
|
this.appState = appState;
|
||||||
this.formData = {};
|
this.formData = {};
|
||||||
|
|
||||||
this.addVisibilityObserver();
|
this.addVisibilityObserver();
|
||||||
this.addDestroyHandler();
|
this.addDestroyHandler();
|
||||||
}
|
}
|
||||||
@@ -29,16 +31,13 @@ class LockScreenCtrl {
|
|||||||
|
|
||||||
addDestroyHandler() {
|
addDestroyHandler() {
|
||||||
this.$scope.$on('$destroy', () => {
|
this.$scope.$on('$destroy', () => {
|
||||||
this.passcodeManager.removeVisibilityObserver(
|
this.unregisterObserver();
|
||||||
this.visibilityObserver
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addVisibilityObserver() {
|
addVisibilityObserver() {
|
||||||
this.visibilityObserver = this.passcodeManager
|
this.unregisterObserver = this.appState.addObserver((eventName, data) => {
|
||||||
.addVisibilityObserver((visible) => {
|
if (eventName === APP_STATE_EVENT_WINDOW_DID_FOCUS) {
|
||||||
if(visible) {
|
|
||||||
const input = this.passcodeInput;
|
const input = this.passcodeInput;
|
||||||
if(input) {
|
if(input) {
|
||||||
input.focus();
|
input.focus();
|
||||||
@@ -47,7 +46,7 @@ class LockScreenCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
submitPasscodeForm($event) {
|
async submitPasscodeForm($event) {
|
||||||
if(
|
if(
|
||||||
!this.formData.passcode ||
|
!this.formData.passcode ||
|
||||||
this.formData.passcode.length === 0
|
this.formData.passcode.length === 0
|
||||||
@@ -55,21 +54,15 @@ class LockScreenCtrl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.passcodeInput.blur();
|
this.passcodeInput.blur();
|
||||||
this.passcodeManager.unlock(
|
const success = await this.onValue()(this.formData.passcode);
|
||||||
this.formData.passcode,
|
if(!success) {
|
||||||
(success) => {
|
this.alertManager.alert({
|
||||||
if(!success) {
|
text: "Invalid passcode. Please try again.",
|
||||||
this.alertManager.alert({
|
onClose: () => {
|
||||||
text: "Invalid passcode. Please try again.",
|
this.passcodeInput.focus();
|
||||||
onClose: () => {
|
|
||||||
this.passcodeInput.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.onSuccess()();
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forgotPasscode() {
|
forgotPasscode() {
|
||||||
@@ -80,10 +73,9 @@ class LockScreenCtrl {
|
|||||||
this.alertManager.confirm({
|
this.alertManager.confirm({
|
||||||
text: "Are you sure you want to clear all local data?",
|
text: "Are you sure you want to clear all local data?",
|
||||||
destructive: true,
|
destructive: true,
|
||||||
onConfirm: () => {
|
onConfirm: async () => {
|
||||||
this.authManager.signout(true).then(() => {
|
await this.application.signOut();
|
||||||
window.location.reload();
|
await this.application.restart();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -97,7 +89,7 @@ export class LockScreen {
|
|||||||
this.controllerAs = 'ctrl';
|
this.controllerAs = 'ctrl';
|
||||||
this.bindToController = true;
|
this.bindToController = true;
|
||||||
this.scope = {
|
this.scope = {
|
||||||
onSuccess: '&',
|
onValue: '&',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import template from '%/notes.pug';
|
import template from '%/notes.pug';
|
||||||
import { SFAuthManager } from 'snjs';
|
import { ApplicationEvents, CONTENT_TYPE_NOTE, CONTENT_TYPE_TAG } from 'snjs';
|
||||||
import { KeyboardManager } from '@/services/keyboardManager';
|
import { KeyboardManager } from '@/services/keyboardManager';
|
||||||
import { PureCtrl } from '@Controllers';
|
import { PureCtrl } from '@Controllers';
|
||||||
import {
|
import {
|
||||||
@@ -48,25 +48,19 @@ class NotesCtrl extends PureCtrl {
|
|||||||
constructor(
|
constructor(
|
||||||
$timeout,
|
$timeout,
|
||||||
$rootScope,
|
$rootScope,
|
||||||
|
application,
|
||||||
appState,
|
appState,
|
||||||
authManager,
|
|
||||||
desktopManager,
|
desktopManager,
|
||||||
keyboardManager,
|
keyboardManager,
|
||||||
modelManager,
|
|
||||||
preferencesManager,
|
preferencesManager,
|
||||||
privilegesManager,
|
|
||||||
syncManager,
|
|
||||||
) {
|
) {
|
||||||
super($timeout);
|
super($timeout);
|
||||||
this.$rootScope = $rootScope;
|
this.$rootScope = $rootScope;
|
||||||
|
this.application = application;
|
||||||
this.appState = appState;
|
this.appState = appState;
|
||||||
this.authManager = authManager;
|
|
||||||
this.desktopManager = desktopManager;
|
this.desktopManager = desktopManager;
|
||||||
this.keyboardManager = keyboardManager;
|
this.keyboardManager = keyboardManager;
|
||||||
this.modelManager = modelManager;
|
|
||||||
this.preferencesManager = preferencesManager;
|
this.preferencesManager = preferencesManager;
|
||||||
this.privilegesManager = privilegesManager;
|
|
||||||
this.syncManager = syncManager;
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
notes: [],
|
notes: [],
|
||||||
@@ -90,9 +84,8 @@ class NotesCtrl extends PureCtrl {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.addAppStateObserver();
|
this.addAppStateObserver();
|
||||||
this.addSignInObserver();
|
this.addAppEventObserver();
|
||||||
this.addSyncEventHandler();
|
this.streamNotesAndTags();
|
||||||
this.addMappingObserver();
|
|
||||||
this.reloadPreferences();
|
this.reloadPreferences();
|
||||||
this.resetPagination();
|
this.resetPagination();
|
||||||
this.registerKeyboardShortcuts();
|
this.registerKeyboardShortcuts();
|
||||||
@@ -116,12 +109,12 @@ class NotesCtrl extends PureCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addSignInObserver() {
|
addAppEventObserver() {
|
||||||
this.authManager.addEventHandler((event) => {
|
this.application.addEventObserver((eventName) => {
|
||||||
if (event === SFAuthManager.DidSignInEvent) {
|
if (eventName === ApplicationEvents.SignedIn) {
|
||||||
/** Delete dummy note if applicable */
|
/** Delete dummy note if applicable */
|
||||||
if (this.state.selectedNote && this.state.selectedNote.dummy) {
|
if (this.state.selectedNote && this.state.selectedNote.dummy) {
|
||||||
this.modelManager.removeItemLocally(this.state.selectedNote);
|
this.application.removeItemLocally({ item: this.state.selectedNote });
|
||||||
this.selectNote(null).then(() => {
|
this.selectNote(null).then(() => {
|
||||||
this.reloadNotes();
|
this.reloadNotes();
|
||||||
});
|
});
|
||||||
@@ -132,17 +125,11 @@ class NotesCtrl extends PureCtrl {
|
|||||||
*/
|
*/
|
||||||
this.createDummyOnSynCompletionIfNoNotes = true;
|
this.createDummyOnSynCompletionIfNoNotes = true;
|
||||||
}
|
}
|
||||||
}
|
} else if (eventName === ApplicationEvents.LoadedLocalData) {
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addSyncEventHandler() {
|
|
||||||
this.syncManager.addEventHandler((syncEvent, data) => {
|
|
||||||
if (syncEvent === 'local-data-loaded') {
|
|
||||||
if (this.state.notes.length === 0) {
|
if (this.state.notes.length === 0) {
|
||||||
this.createNewNote();
|
this.createNewNote();
|
||||||
}
|
}
|
||||||
} else if (syncEvent === 'sync:completed') {
|
} else if(eventName === ApplicationEvents.CompletedSync) {
|
||||||
if (this.createDummyOnSynCompletionIfNoNotes && this.state.notes.length === 0) {
|
if (this.createDummyOnSynCompletionIfNoNotes && this.state.notes.length === 0) {
|
||||||
this.createDummyOnSynCompletionIfNoNotes = false;
|
this.createDummyOnSynCompletionIfNoNotes = false;
|
||||||
this.createNewNote();
|
this.createNewNote();
|
||||||
@@ -151,11 +138,10 @@ class NotesCtrl extends PureCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addMappingObserver() {
|
streamNotesAndTags() {
|
||||||
this.modelManager.addItemSyncObserver(
|
this.application.streamItems({
|
||||||
'note-list',
|
contentType: [CONTENT_TYPE_NOTE, CONTENT_TYPE_TAG],
|
||||||
'*',
|
stream: async ({ items }) => {
|
||||||
async (allItems, validItems, deletedItems, source, sourceKey) => {
|
|
||||||
await this.reloadNotes();
|
await this.reloadNotes();
|
||||||
const selectedNote = this.state.selectedNote;
|
const selectedNote = this.state.selectedNote;
|
||||||
if (selectedNote) {
|
if (selectedNote) {
|
||||||
@@ -169,18 +155,19 @@ class NotesCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Note has changed values, reset its flags */
|
/** Note has changed values, reset its flags */
|
||||||
const notes = allItems.filter((item) => item.content_type === 'Note');
|
const notes = items.filter((item) => item.content_type === CONTENT_TYPE_NOTE);
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
this.loadFlagsForNote(note);
|
this.loadFlagsForNote(note);
|
||||||
note.cachedCreatedAtString = note.createdAtString();
|
note.cachedCreatedAtString = note.createdAtString();
|
||||||
note.cachedUpdatedAtString = note.updatedAtString();
|
note.cachedUpdatedAtString = note.updatedAtString();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleTagChange(tag, previousTag) {
|
async handleTagChange(tag, previousTag) {
|
||||||
if (this.state.selectedNote && this.state.selectedNote.dummy) {
|
if (this.state.selectedNote && this.state.selectedNote.dummy) {
|
||||||
this.modelManager.removeItemLocally(this.state.selectedNote);
|
this.application.removeItemLocally({item: this.state.selectedNote});
|
||||||
if (previousTag) {
|
if (previousTag) {
|
||||||
_.remove(previousTag.notes, this.state.selectedNote);
|
_.remove(previousTag.notes, this.state.selectedNote);
|
||||||
}
|
}
|
||||||
@@ -201,7 +188,7 @@ class NotesCtrl extends PureCtrl {
|
|||||||
|
|
||||||
if (this.state.notes.length > 0) {
|
if (this.state.notes.length > 0) {
|
||||||
this.selectFirstNote();
|
this.selectFirstNote();
|
||||||
} else if (this.syncManager.initialDataLoaded()) {
|
} else if (this.application.isDatabaseLoaded()) {
|
||||||
if (!tag.isSmartTag() || tag.content.isAllTag) {
|
if (!tag.isSmartTag() || tag.content.isAllTag) {
|
||||||
this.createNewNote();
|
this.createNewNote();
|
||||||
} else if (
|
} else if (
|
||||||
@@ -279,7 +266,7 @@ class NotesCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
const previousNote = this.state.selectedNote;
|
const previousNote = this.state.selectedNote;
|
||||||
if (previousNote && previousNote.dummy) {
|
if (previousNote && previousNote.dummy) {
|
||||||
this.modelManager.removeItemLocally(previousNote);
|
this.application.removeItemLocally({previousNote});
|
||||||
this.removeNoteFromList(previousNote);
|
this.removeNoteFromList(previousNote);
|
||||||
}
|
}
|
||||||
await this.setState({
|
await this.setState({
|
||||||
@@ -292,8 +279,7 @@ class NotesCtrl extends PureCtrl {
|
|||||||
this.selectedIndex = Math.max(0, this.displayableNotes().indexOf(note));
|
this.selectedIndex = Math.max(0, this.displayableNotes().indexOf(note));
|
||||||
if (note.content.conflict_of) {
|
if (note.content.conflict_of) {
|
||||||
note.content.conflict_of = null;
|
note.content.conflict_of = null;
|
||||||
this.modelManager.setItemDirty(note);
|
this.application.saveItem({item: note});
|
||||||
this.syncManager.sync();
|
|
||||||
}
|
}
|
||||||
if (this.isFiltering()) {
|
if (this.isFiltering()) {
|
||||||
this.desktopManager.searchText(this.state.noteFilter.text);
|
this.desktopManager.searchText(this.state.noteFilter.text);
|
||||||
@@ -536,8 +522,8 @@ class NotesCtrl extends PureCtrl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const title = "Note" + (this.state.notes ? (" " + (this.state.notes.length + 1)) : "");
|
const title = "Note" + (this.state.notes ? (" " + (this.state.notes.length + 1)) : "");
|
||||||
const newNote = this.modelManager.createItem({
|
const newNote = this.application.createItem({
|
||||||
content_type: 'Note',
|
contentType: CONTENT_TYPE_NOTE,
|
||||||
content: {
|
content: {
|
||||||
text: '',
|
text: '',
|
||||||
title: title
|
title: title
|
||||||
@@ -545,19 +531,18 @@ class NotesCtrl extends PureCtrl {
|
|||||||
});
|
});
|
||||||
newNote.client_updated_at = new Date();
|
newNote.client_updated_at = new Date();
|
||||||
newNote.dummy = true;
|
newNote.dummy = true;
|
||||||
this.modelManager.addItem(newNote);
|
this.application.setItemNeedsSync({item: newNote});
|
||||||
this.modelManager.setItemDirty(newNote);
|
|
||||||
const selectedTag = this.appState.getSelectedTag();
|
const selectedTag = this.appState.getSelectedTag();
|
||||||
if (!selectedTag.isSmartTag()) {
|
if (!selectedTag.isSmartTag()) {
|
||||||
selectedTag.addItemAsRelationship(newNote);
|
selectedTag.addItemAsRelationship(newNote);
|
||||||
this.modelManager.setItemDirty(selectedTag);
|
this.application.setItemNeedsSync({ item: selectedTag });
|
||||||
}
|
}
|
||||||
this.selectNote(newNote);
|
this.selectNote(newNote);
|
||||||
}
|
}
|
||||||
|
|
||||||
isFiltering() {
|
isFiltering() {
|
||||||
return this.state.noteFilter.text &&
|
return this.state.noteFilter.text &&
|
||||||
this.state.noteFilter.text.length > 0;
|
this.state.noteFilter.text.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setNoteFilterText(text) {
|
async setNoteFilterText(text) {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { SFAuthManager } from 'snjs';
|
import { Challenges } from 'snjs';
|
||||||
import { getPlatformString } from '@/utils';
|
import { getPlatformString } from '@/utils';
|
||||||
import template from '%/root.pug';
|
import template from '%/root.pug';
|
||||||
import {
|
import {
|
||||||
APP_STATE_EVENT_PANEL_RESIZED
|
APP_STATE_EVENT_PANEL_RESIZED,
|
||||||
|
APP_STATE_EVENT_WINDOW_DID_FOCUS
|
||||||
} from '@/state';
|
} from '@/state';
|
||||||
import {
|
import {
|
||||||
PANEL_NAME_NOTES,
|
PANEL_NAME_NOTES,
|
||||||
@@ -15,257 +16,188 @@ import {
|
|||||||
StringSyncException
|
StringSyncException
|
||||||
} from '@/strings';
|
} from '@/strings';
|
||||||
|
|
||||||
/** How often to automatically sync, in milliseconds */
|
class RootCtrl extends PureCtrl {
|
||||||
const AUTO_SYNC_INTERVAL = 30000;
|
|
||||||
|
|
||||||
class RootCtrl {
|
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
$location,
|
$location,
|
||||||
$rootScope,
|
$rootScope,
|
||||||
$scope,
|
|
||||||
$timeout,
|
$timeout,
|
||||||
alertManager,
|
application,
|
||||||
appState,
|
appState,
|
||||||
authManager,
|
databaseManager,
|
||||||
dbManager,
|
lockManager,
|
||||||
modelManager,
|
|
||||||
passcodeManager,
|
|
||||||
preferencesManager,
|
preferencesManager,
|
||||||
themeManager /** Unused below, required to load globally */,
|
themeManager /** Unused below, required to load globally */,
|
||||||
statusManager,
|
statusManager,
|
||||||
storageManager,
|
|
||||||
syncManager,
|
|
||||||
) {
|
) {
|
||||||
this.$rootScope = $rootScope;
|
super($timeout);
|
||||||
this.$scope = $scope;
|
|
||||||
this.$location = $location;
|
this.$location = $location;
|
||||||
|
this.$rootScope = $rootScope;
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
this.dbManager = dbManager;
|
this.application = application;
|
||||||
this.syncManager = syncManager;
|
|
||||||
this.statusManager = statusManager;
|
|
||||||
this.storageManager = storageManager;
|
|
||||||
this.appState = appState;
|
this.appState = appState;
|
||||||
this.authManager = authManager;
|
this.databaseManager = databaseManager;
|
||||||
this.modelManager = modelManager;
|
this.lockManager = lockManager;
|
||||||
this.alertManager = alertManager;
|
|
||||||
this.preferencesManager = preferencesManager;
|
this.preferencesManager = preferencesManager;
|
||||||
this.passcodeManager = passcodeManager;
|
this.statusManager = statusManager;
|
||||||
|
this.platformString = getPlatformString();
|
||||||
|
this.state = {
|
||||||
|
needsUnlock: false,
|
||||||
|
appClass: ''
|
||||||
|
};
|
||||||
|
|
||||||
this.defineRootScopeFunctions();
|
this.loadApplication();
|
||||||
this.handleAutoSignInFromParams();
|
this.handleAutoSignInFromParams();
|
||||||
this.initializeStorageManager();
|
|
||||||
this.addAppStateObserver();
|
this.addAppStateObserver();
|
||||||
this.addDragDropHandlers();
|
this.addDragDropHandlers();
|
||||||
this.defaultLoad();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defineRootScopeFunctions() {
|
async loadApplication() {
|
||||||
this.$rootScope.lockApplication = () => {
|
this.application.prepareForLaunch({
|
||||||
/** Reloading wipes current objects from memory */
|
callbacks: {
|
||||||
window.location.reload();
|
authChallengeResponses: async (challenges) => {
|
||||||
};
|
if (challenges.includes(Challenges.LocalPasscode)) {
|
||||||
|
this.setState({ needsUnlock: true });
|
||||||
this.$rootScope.safeApply = (fn) => {
|
}
|
||||||
const phase = this.$scope.$root.$$phase;
|
}
|
||||||
if(phase === '$apply' || phase === '$digest') {
|
|
||||||
this.$scope.$eval(fn);
|
|
||||||
} else {
|
|
||||||
this.$scope.$apply(fn);
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
await this.application.launch();
|
||||||
|
this.setState({ needsUnlock: false });
|
||||||
|
await this.openDatabase();
|
||||||
|
this.preferencesManager.load();
|
||||||
|
this.addSyncStatusObserver();
|
||||||
|
this.addSyncEventHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultLoad() {
|
onUpdateAvailable() {
|
||||||
this.$scope.platform = getPlatformString();
|
this.$rootScope.$broadcast('new-update-available');
|
||||||
|
};
|
||||||
if(this.passcodeManager.isLocked()) {
|
|
||||||
this.$scope.needsUnlock = true;
|
|
||||||
} else {
|
|
||||||
this.loadAfterUnlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$scope.onSuccessfulUnlock = () => {
|
|
||||||
this.$timeout(() => {
|
|
||||||
this.$scope.needsUnlock = false;
|
|
||||||
this.loadAfterUnlock();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$scope.onUpdateAvailable = () => {
|
|
||||||
this.$rootScope.$broadcast('new-update-available');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
initializeStorageManager() {
|
|
||||||
this.storageManager.initialize(
|
|
||||||
this.passcodeManager.hasPasscode(),
|
|
||||||
this.authManager.isEphemeralSession()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
addAppStateObserver() {
|
addAppStateObserver() {
|
||||||
this.appState.addObserver((eventName, data) => {
|
this.appState.addObserver(async (eventName, data) => {
|
||||||
if(eventName === APP_STATE_EVENT_PANEL_RESIZED) {
|
if (eventName === APP_STATE_EVENT_PANEL_RESIZED) {
|
||||||
if(data.panel === PANEL_NAME_NOTES) {
|
if (data.panel === PANEL_NAME_NOTES) {
|
||||||
this.notesCollapsed = data.collapsed;
|
this.notesCollapsed = data.collapsed;
|
||||||
}
|
}
|
||||||
if(data.panel === PANEL_NAME_TAGS) {
|
if (data.panel === PANEL_NAME_TAGS) {
|
||||||
this.tagsCollapsed = data.collapsed;
|
this.tagsCollapsed = data.collapsed;
|
||||||
}
|
}
|
||||||
let appClass = "";
|
let appClass = "";
|
||||||
if(this.notesCollapsed) { appClass += "collapsed-notes"; }
|
if (this.notesCollapsed) { appClass += "collapsed-notes"; }
|
||||||
if(this.tagsCollapsed) { appClass += " collapsed-tags"; }
|
if (this.tagsCollapsed) { appClass += " collapsed-tags"; }
|
||||||
this.$scope.appClass = appClass;
|
this.setState({ appClass });
|
||||||
|
} else if (eventName === APP_STATE_EVENT_WINDOW_DID_FOCUS) {
|
||||||
|
if (!(await this.application.isPasscodeLocked())) {
|
||||||
|
this.application.sync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadAfterUnlock() {
|
async openDatabase() {
|
||||||
this.openDatabase();
|
this.databaseManager.setLocked(false);
|
||||||
this.authManager.loadInitialData();
|
this.databaseManager.openDatabase({
|
||||||
this.preferencesManager.load();
|
|
||||||
this.addSyncStatusObserver();
|
|
||||||
this.configureKeyRequestHandler();
|
|
||||||
this.addSyncEventHandler();
|
|
||||||
this.addSignOutObserver();
|
|
||||||
this.loadLocalData();
|
|
||||||
}
|
|
||||||
|
|
||||||
openDatabase() {
|
|
||||||
this.dbManager.setLocked(false);
|
|
||||||
this.dbManager.openDatabase({
|
|
||||||
onUpgradeNeeded: () => {
|
onUpgradeNeeded: () => {
|
||||||
/**
|
/**
|
||||||
* New database, delete syncToken so that items
|
* New database/database wiped, delete syncToken so that items
|
||||||
* can be refetched entirely from server
|
* can be refetched entirely from server
|
||||||
*/
|
*/
|
||||||
this.syncManager.clearSyncToken();
|
this.application.syncManager.clearSyncPositionTokens();
|
||||||
this.syncManager.sync();
|
this.application.sync();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addSyncStatusObserver() {
|
// addSyncStatusObserver() {
|
||||||
this.syncStatusObserver = this.syncManager.registerSyncStatusObserver((status) => {
|
// this.syncStatusObserver = this.syncManager.registerSyncStatusObserver((status) => {
|
||||||
if(status.retrievedCount > 20) {
|
// if (status.retrievedCount > 20) {
|
||||||
const text = `Downloading ${status.retrievedCount} items. Keep app open.`;
|
// const text = `Downloading ${status.retrievedCount} items. Keep app open.`;
|
||||||
this.syncStatus = this.statusManager.replaceStatusWithString(
|
// this.syncStatus = this.statusManager.replaceStatusWithString(
|
||||||
this.syncStatus,
|
// this.syncStatus,
|
||||||
text
|
// text
|
||||||
);
|
// );
|
||||||
this.showingDownloadStatus = true;
|
// this.showingDownloadStatus = true;
|
||||||
} else if(this.showingDownloadStatus) {
|
// } else if (this.showingDownloadStatus) {
|
||||||
this.showingDownloadStatus = false;
|
// this.showingDownloadStatus = false;
|
||||||
const text = "Download Complete.";
|
// const text = "Download Complete.";
|
||||||
this.syncStatus = this.statusManager.replaceStatusWithString(
|
// this.syncStatus = this.statusManager.replaceStatusWithString(
|
||||||
this.syncStatus,
|
// this.syncStatus,
|
||||||
text
|
// text
|
||||||
);
|
// );
|
||||||
setTimeout(() => {
|
// setTimeout(() => {
|
||||||
this.syncStatus = this.statusManager.removeStatus(this.syncStatus);
|
// this.syncStatus = this.statusManager.removeStatus(this.syncStatus);
|
||||||
}, 2000);
|
// }, 2000);
|
||||||
} else if(status.total > 20) {
|
// } else if (status.total > 20) {
|
||||||
this.uploadSyncStatus = this.statusManager.replaceStatusWithString(
|
// this.uploadSyncStatus = this.statusManager.replaceStatusWithString(
|
||||||
this.uploadSyncStatus,
|
// this.uploadSyncStatus,
|
||||||
`Syncing ${status.current}/${status.total} items...`
|
// `Syncing ${status.current}/${status.total} items...`
|
||||||
);
|
// );
|
||||||
} else if(this.uploadSyncStatus) {
|
// } else if (this.uploadSyncStatus) {
|
||||||
this.uploadSyncStatus = this.statusManager.removeStatus(
|
// this.uploadSyncStatus = this.statusManager.removeStatus(
|
||||||
this.uploadSyncStatus
|
// this.uploadSyncStatus
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
configureKeyRequestHandler() {
|
// addSyncEventHandler() {
|
||||||
this.syncManager.setKeyRequestHandler(async () => {
|
// let lastShownDate;
|
||||||
const offline = this.authManager.offline();
|
// this.syncManager.addEventHandler((syncEvent, data) => {
|
||||||
const authParams = (
|
// this.$rootScope.$broadcast(
|
||||||
offline
|
// syncEvent,
|
||||||
? this.passcodeManager.passcodeAuthParams()
|
// data || {}
|
||||||
: await this.authManager.getAuthParams()
|
// );
|
||||||
);
|
// if (syncEvent === 'sync-session-invalid') {
|
||||||
const keys = offline
|
// /** Don't show repeatedly; at most 30 seconds in between */
|
||||||
? this.passcodeManager.keys()
|
// const SHOW_INTERVAL = 30;
|
||||||
: await this.authManager.keys();
|
// const lastShownSeconds = (new Date() - lastShownDate) / 1000;
|
||||||
return {
|
// if (!lastShownDate || lastShownSeconds > SHOW_INTERVAL) {
|
||||||
keys: keys,
|
// lastShownDate = new Date();
|
||||||
offline: offline,
|
// setTimeout(() => {
|
||||||
auth_params: authParams
|
// this.alertManager.alert({
|
||||||
};
|
// text: STRING_SESSION_EXPIRED
|
||||||
});
|
// });
|
||||||
}
|
// }, 500);
|
||||||
|
// }
|
||||||
|
// } else if (syncEvent === 'sync-exception') {
|
||||||
|
// this.alertManager.alert({
|
||||||
|
// text: StringSyncException(data)
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
addSyncEventHandler() {
|
// loadLocalData() {
|
||||||
let lastShownDate;
|
// const encryptionEnabled = this.application.getUser || this.application.hasPasscode();
|
||||||
this.syncManager.addEventHandler((syncEvent, data) => {
|
// this.syncStatus = this.statusManager.addStatusFromString(
|
||||||
this.$rootScope.$broadcast(
|
// encryptionEnabled ? "Decrypting items..." : "Loading items..."
|
||||||
syncEvent,
|
// );
|
||||||
data || {}
|
// const incrementalCallback = (current, total) => {
|
||||||
);
|
// const notesString = `${current}/${total} items...`;
|
||||||
if(syncEvent === 'sync-session-invalid') {
|
// const status = encryptionEnabled
|
||||||
/** Don't show repeatedly; at most 30 seconds in between */
|
// ? `Decrypting ${notesString}`
|
||||||
const SHOW_INTERVAL = 30;
|
// : `Loading ${notesString}`;
|
||||||
const lastShownSeconds = (new Date() - lastShownDate) / 1000;
|
// this.syncStatus = this.statusManager.replaceStatusWithString(
|
||||||
if(!lastShownDate || lastShownSeconds > SHOW_INTERVAL) {
|
// this.syncStatus,
|
||||||
lastShownDate = new Date();
|
// status
|
||||||
setTimeout(() => {
|
// );
|
||||||
this.alertManager.alert({
|
// };
|
||||||
text: STRING_SESSION_EXPIRED
|
// this.syncManager.loadLocalItems({ incrementalCallback }).then(() => {
|
||||||
});
|
// this.$timeout(() => {
|
||||||
}, 500);
|
// this.$rootScope.$broadcast("initial-data-loaded");
|
||||||
}
|
// this.syncStatus = this.statusManager.replaceStatusWithString(
|
||||||
} else if(syncEvent === 'sync-exception') {
|
// this.syncStatus,
|
||||||
this.alertManager.alert({
|
// "Syncing..."
|
||||||
text: StringSyncException(data)
|
// );
|
||||||
});
|
// this.syncManager.sync({
|
||||||
}
|
// checkIntegrity: true
|
||||||
});
|
// }).then(() => {
|
||||||
}
|
// this.syncStatus = this.statusManager.removeStatus(this.syncStatus);
|
||||||
|
// });
|
||||||
loadLocalData() {
|
// });
|
||||||
const encryptionEnabled = this.authManager.user || this.passcodeManager.hasPasscode();
|
// });
|
||||||
this.syncStatus = this.statusManager.addStatusFromString(
|
// }
|
||||||
encryptionEnabled ? "Decrypting items..." : "Loading items..."
|
|
||||||
);
|
|
||||||
const incrementalCallback = (current, total) => {
|
|
||||||
const notesString = `${current}/${total} items...`;
|
|
||||||
const status = encryptionEnabled
|
|
||||||
? `Decrypting ${notesString}`
|
|
||||||
: `Loading ${notesString}`;
|
|
||||||
this.syncStatus = this.statusManager.replaceStatusWithString(
|
|
||||||
this.syncStatus,
|
|
||||||
status
|
|
||||||
);
|
|
||||||
};
|
|
||||||
this.syncManager.loadLocalItems({incrementalCallback}).then(() => {
|
|
||||||
this.$timeout(() => {
|
|
||||||
this.$rootScope.$broadcast("initial-data-loaded");
|
|
||||||
this.syncStatus = this.statusManager.replaceStatusWithString(
|
|
||||||
this.syncStatus,
|
|
||||||
"Syncing..."
|
|
||||||
);
|
|
||||||
this.syncManager.sync({
|
|
||||||
performIntegrityCheck: true
|
|
||||||
}).then(() => {
|
|
||||||
this.syncStatus = this.statusManager.removeStatus(this.syncStatus);
|
|
||||||
});
|
|
||||||
setInterval(() => {
|
|
||||||
this.syncManager.sync();
|
|
||||||
}, AUTO_SYNC_INTERVAL);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addSignOutObserver() {
|
|
||||||
this.authManager.addEventHandler((event) => {
|
|
||||||
if(event === SFAuthManager.DidSignOutEvent) {
|
|
||||||
this.modelManager.handleSignout();
|
|
||||||
this.syncManager.handleSignout();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addDragDropHandlers() {
|
addDragDropHandlers() {
|
||||||
/**
|
/**
|
||||||
@@ -280,9 +212,9 @@ class RootCtrl {
|
|||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
window.addEventListener('drop', (event) => {
|
window.addEventListener('drop', (event) => {
|
||||||
if(event.dataTransfer.files.length > 0) {
|
if (event.dataTransfer.files.length > 0) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: STRING_DEFAULT_FILE_ERROR
|
text: STRING_DEFAULT_FILE_ERROR
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -294,39 +226,33 @@ class RootCtrl {
|
|||||||
return this.$location.search()[key];
|
return this.$location.search()[key];
|
||||||
};
|
};
|
||||||
|
|
||||||
const autoSignInFromParams = async () => {
|
const autoSignInFromParams = async () => {
|
||||||
const server = urlParam('server');
|
const server = urlParam('server');
|
||||||
const email = urlParam('email');
|
const email = urlParam('email');
|
||||||
const pw = urlParam('pw');
|
const pw = urlParam('pw');
|
||||||
if(!this.authManager.offline()) {
|
if (!this.application.getUser()) {
|
||||||
if(
|
if (
|
||||||
await this.syncManager.getServerURL() === server
|
await this.application.getHost() === server
|
||||||
&& this.authManager.user.email === email
|
&& this.application.getUser().email === email
|
||||||
) {
|
) {
|
||||||
/** Already signed in, return */
|
/** Already signed in, return */
|
||||||
// eslint-disable-next-line no-useless-return
|
// eslint-disable-next-line no-useless-return
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
/** Sign out */
|
/** Sign out */
|
||||||
this.authManager.signout(true).then(() => {
|
await this.application.signOut();
|
||||||
window.location.reload();
|
await this.application.restart();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.authManager.login(
|
await this.application.setHost(server);
|
||||||
server,
|
this.application.signIn({
|
||||||
email,
|
email: email,
|
||||||
pw,
|
password: pw,
|
||||||
false,
|
|
||||||
false,
|
|
||||||
{}
|
|
||||||
).then((response) => {
|
|
||||||
window.location.reload();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if(urlParam('server')) {
|
if (urlParam('server')) {
|
||||||
autoSignInFromParams();
|
autoSignInFromParams();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,5 +262,8 @@ export class Root {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.template = template;
|
this.template = template;
|
||||||
this.controller = RootCtrl;
|
this.controller = RootCtrl;
|
||||||
|
this.replace = true;
|
||||||
|
this.controllerAs = 'self';
|
||||||
|
this.bindToController = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { SNNote, SNSmartTag } from 'snjs';
|
import { SNNote, SNSmartTag, CONTENT_TYPE_TAG, CONTENT_TYPE_SMART_TAG } from 'snjs';
|
||||||
import template from '%/tags.pug';
|
import template from '%/tags.pug';
|
||||||
import {
|
import {
|
||||||
APP_STATE_EVENT_PREFERENCES_CHANGED,
|
APP_STATE_EVENT_PREFERENCES_CHANGED,
|
||||||
@@ -14,29 +14,22 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
constructor(
|
constructor(
|
||||||
$rootScope,
|
$rootScope,
|
||||||
$timeout,
|
$timeout,
|
||||||
alertManager,
|
application,
|
||||||
appState,
|
appState,
|
||||||
componentManager,
|
preferencesManager
|
||||||
modelManager,
|
|
||||||
preferencesManager,
|
|
||||||
syncManager,
|
|
||||||
) {
|
) {
|
||||||
super($timeout);
|
super($timeout);
|
||||||
this.$rootScope = $rootScope;
|
this.$rootScope = $rootScope;
|
||||||
this.alertManager = alertManager;
|
this.application = application;
|
||||||
this.appState = appState;
|
this.appState = appState;
|
||||||
this.componentManager = componentManager;
|
|
||||||
this.modelManager = modelManager;
|
|
||||||
this.preferencesManager = preferencesManager;
|
this.preferencesManager = preferencesManager;
|
||||||
this.syncManager = syncManager;
|
|
||||||
this.panelController = {};
|
this.panelController = {};
|
||||||
this.addSyncEventHandler();
|
this.beginStreamingItems();
|
||||||
this.addAppStateObserver();
|
this.addAppStateObserver();
|
||||||
this.addMappingObserver();
|
|
||||||
this.loadPreferences();
|
this.loadPreferences();
|
||||||
this.registerComponentHandler();
|
this.registerComponentHandler();
|
||||||
this.state = {
|
this.state = {
|
||||||
smartTags: this.modelManager.getSmartTags(),
|
smartTags: this.application.getSmartTags(),
|
||||||
noteCounts: {}
|
noteCounts: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -45,18 +38,24 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
this.selectTag(this.state.smartTags[0]);
|
this.selectTag(this.state.smartTags[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSyncEventHandler() {
|
beginStreamingItems() {
|
||||||
this.syncManager.addEventHandler(async (syncEvent, data) => {
|
this.application.streamItems({
|
||||||
if (
|
contentType: CONTENT_TYPE_TAG,
|
||||||
syncEvent === 'local-data-loaded' ||
|
stream: async ({items}) => {
|
||||||
syncEvent === 'sync:completed' ||
|
|
||||||
syncEvent === 'local-data-incremental-load'
|
|
||||||
) {
|
|
||||||
await this.setState({
|
await this.setState({
|
||||||
tags: this.modelManager.tags,
|
tags: this.application.getItems({contentType: CONTENT_TYPE_TAG}),
|
||||||
smartTags: this.modelManager.getSmartTags()
|
smartTags: this.application.getItems({ contentType: CONTENT_TYPE_SMART_TAG }),
|
||||||
});
|
});
|
||||||
this.reloadNoteCounts();
|
this.reloadNoteCounts();
|
||||||
|
if (this.state.selectedTag) {
|
||||||
|
/** If the selected tag has been deleted, revert to All view. */
|
||||||
|
const selectedTag = items.find((tag) => {
|
||||||
|
return tag.uuid === this.state.selectedTag.uuid;
|
||||||
|
});
|
||||||
|
if (selectedTag && selectedTag.deleted) {
|
||||||
|
this.selectTag(this.state.smartTags[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -73,27 +72,6 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addMappingObserver() {
|
|
||||||
this.modelManager.addItemSyncObserver(
|
|
||||||
'tags-list-tags',
|
|
||||||
'Tag',
|
|
||||||
(allItems, validItems, deletedItems, source, sourceKey) => {
|
|
||||||
this.reloadNoteCounts();
|
|
||||||
|
|
||||||
if (!this.state.selectedTag) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/** If the selected tag has been deleted, revert to All view. */
|
|
||||||
const selectedTag = allItems.find((tag) => {
|
|
||||||
return tag.uuid === this.state.selectedTag.uuid;
|
|
||||||
});
|
|
||||||
if (selectedTag && selectedTag.deleted) {
|
|
||||||
this.selectTag(this.state.smartTags[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadNoteCounts() {
|
reloadNoteCounts() {
|
||||||
let allTags = [];
|
let allTags = [];
|
||||||
if (this.state.tags) {
|
if (this.state.tags) {
|
||||||
@@ -140,7 +118,7 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerComponentHandler() {
|
registerComponentHandler() {
|
||||||
this.componentManager.registerHandler({
|
this.application.componentManager.registerHandler({
|
||||||
identifier: 'tags',
|
identifier: 'tags',
|
||||||
areas: ['tags-list'],
|
areas: ['tags-list'],
|
||||||
activationHandler: (component) => {
|
activationHandler: (component) => {
|
||||||
@@ -152,7 +130,7 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
actionHandler: (component, action, data) => {
|
actionHandler: (component, action, data) => {
|
||||||
if (action === 'select-item') {
|
if (action === 'select-item') {
|
||||||
if (data.item.content_type === 'Tag') {
|
if (data.item.content_type === 'Tag') {
|
||||||
const tag = this.modelManager.findItem(data.item.uuid);
|
const tag = this.application.findItem({uuid: data.item.uuid});
|
||||||
if (tag) {
|
if (tag) {
|
||||||
this.selectTag(tag);
|
this.selectTag(tag);
|
||||||
}
|
}
|
||||||
@@ -171,14 +149,15 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
if (tag.isSmartTag()) {
|
if (tag.isSmartTag()) {
|
||||||
Object.defineProperty(tag, 'notes', {
|
Object.defineProperty(tag, 'notes', {
|
||||||
get: () => {
|
get: () => {
|
||||||
return this.modelManager.notesMatchingSmartTag(tag);
|
return this.application.getNotesMatchingSmartTag({
|
||||||
|
smartTag: tag
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (tag.content.conflict_of) {
|
if (tag.content.conflict_of) {
|
||||||
tag.content.conflict_of = null;
|
tag.content.conflict_of = null;
|
||||||
this.modelManager.setItemDirty(tag);
|
this.application.saveItem({item: tag});
|
||||||
this.syncManager.sync();
|
|
||||||
}
|
}
|
||||||
this.appState.setSelectedTag(tag);
|
this.appState.setSelectedTag(tag);
|
||||||
}
|
}
|
||||||
@@ -187,8 +166,8 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
if (this.state.editingTag) {
|
if (this.state.editingTag) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newTag = this.modelManager.createItem({
|
const newTag = this.application.createItem({
|
||||||
content_type: 'Tag'
|
contentType: CONTENT_TYPE_TAG
|
||||||
});
|
});
|
||||||
this.setState({
|
this.setState({
|
||||||
previousTag: this.state.selectedTag,
|
previousTag: this.state.selectedTag,
|
||||||
@@ -196,7 +175,9 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
editingTag: newTag,
|
editingTag: newTag,
|
||||||
newTag: newTag
|
newTag: newTag
|
||||||
});
|
});
|
||||||
this.modelManager.addItem(newTag);
|
/** @todo Should not be accessing internal function */
|
||||||
|
/** Rely on local state instead of adding to global state */
|
||||||
|
this.application.modelManager.insertItems({items: [newTag]});
|
||||||
}
|
}
|
||||||
|
|
||||||
tagTitleDidChange(tag) {
|
tagTitleDidChange(tag) {
|
||||||
@@ -215,7 +196,9 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
tag.title = this.editingOriginalName;
|
tag.title = this.editingOriginalName;
|
||||||
this.editingOriginalName = null;
|
this.editingOriginalName = null;
|
||||||
} else if(this.state.newTag) {
|
} else if(this.state.newTag) {
|
||||||
this.modelManager.removeItemLocally(tag);
|
/** @todo Should not be accessing internal function */
|
||||||
|
/** Rely on local state instead of adding to global state */
|
||||||
|
this.application.modelManager.removeItemLocally(tag);
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedTag: this.state.previousTag
|
selectedTag: this.state.previousTag
|
||||||
});
|
});
|
||||||
@@ -226,20 +209,20 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
|
|
||||||
this.editingOriginalName = null;
|
this.editingOriginalName = null;
|
||||||
|
|
||||||
const matchingTag = this.modelManager.findTag(tag.title);
|
const matchingTag = this.application.findTag({title: tag.title});
|
||||||
const alreadyExists = matchingTag && matchingTag !== tag;
|
const alreadyExists = matchingTag && matchingTag !== tag;
|
||||||
if (this.state.newTag === tag && alreadyExists) {
|
if (this.state.newTag === tag && alreadyExists) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: "A tag with this name already exists."
|
text: "A tag with this name already exists."
|
||||||
});
|
});
|
||||||
this.modelManager.removeItemLocally(tag);
|
/** @todo Should not be accessing internal function */
|
||||||
|
/** Rely on local state instead of adding to global state */
|
||||||
|
this.application.modelManager.removeItemLocally(tag);
|
||||||
this.setState({ newTag: null });
|
this.setState({ newTag: null });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.modelManager.setItemDirty(tag);
|
this.application.saveItem({item: tag});
|
||||||
this.syncManager.sync();
|
|
||||||
this.modelManager.resortTag(tag);
|
|
||||||
this.selectTag(tag);
|
this.selectTag(tag);
|
||||||
this.setState({
|
this.setState({
|
||||||
newTag: null
|
newTag: null
|
||||||
@@ -260,12 +243,11 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeTag(tag) {
|
removeTag(tag) {
|
||||||
this.alertManager.confirm({
|
this.application.alertManager.confirm({
|
||||||
text: STRING_DELETE_TAG,
|
text: STRING_DELETE_TAG,
|
||||||
destructive: true,
|
destructive: true,
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
this.modelManager.setItemToBeDeleted(tag);
|
this.application.deleteItem({item: tag});
|
||||||
this.syncManager.sync();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { isDesktopApplication, isNullOrUndefined } from '@/utils';
|
import { isDesktopApplication, isNullOrUndefined } from '@/utils';
|
||||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
|
||||||
import template from '%/directives/account-menu.pug';
|
import template from '%/directives/account-menu.pug';
|
||||||
import { protocolManager } from 'snjs';
|
import { ProtectedActions } from 'snjs';
|
||||||
import { PureCtrl } from '@Controllers';
|
import { PureCtrl } from '@Controllers';
|
||||||
import {
|
import {
|
||||||
STRING_ACCOUNT_MENU_UNCHECK_MERGE,
|
STRING_ACCOUNT_MENU_UNCHECK_MERGE,
|
||||||
@@ -33,56 +32,38 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
$scope,
|
$scope,
|
||||||
$rootScope,
|
$rootScope,
|
||||||
$timeout,
|
$timeout,
|
||||||
alertManager,
|
|
||||||
archiveManager,
|
archiveManager,
|
||||||
appVersion,
|
appVersion,
|
||||||
authManager,
|
godService,
|
||||||
modelManager,
|
lockManager,
|
||||||
passcodeManager,
|
application
|
||||||
privilegesManager,
|
|
||||||
storageManager,
|
|
||||||
syncManager,
|
|
||||||
) {
|
) {
|
||||||
super($timeout);
|
super($timeout);
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
this.$rootScope = $rootScope;
|
this.$rootScope = $rootScope;
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
this.alertManager = alertManager;
|
|
||||||
this.archiveManager = archiveManager;
|
this.archiveManager = archiveManager;
|
||||||
this.authManager = authManager;
|
this.godService = godService;
|
||||||
this.modelManager = modelManager;
|
this.lockManager = lockManager;
|
||||||
this.passcodeManager = passcodeManager;
|
this.application = application;
|
||||||
this.privilegesManager = privilegesManager;
|
|
||||||
this.storageManager = storageManager;
|
|
||||||
this.syncManager = syncManager;
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
appVersion: 'v' + (window.electronAppVersion || appVersion),
|
appVersion: 'v' + (window.electronAppVersion || appVersion),
|
||||||
user: this.authManager.user,
|
user: this.application.getUser(),
|
||||||
canAddPasscode: !this.authManager.isEphemeralSession(),
|
canAddPasscode: !this.application.isEphemeralSession(),
|
||||||
passcodeAutoLockOptions: this.passcodeManager.getAutoLockIntervalOptions(),
|
passcodeAutoLockOptions: this.lockManager.getAutoLockIntervalOptions(),
|
||||||
formData: {
|
formData: {
|
||||||
mergeLocal: true,
|
mergeLocal: true,
|
||||||
ephemeral: false
|
ephemeral: false
|
||||||
},
|
},
|
||||||
mutable: {
|
mutable: {}
|
||||||
backupEncrypted: this.encryptedBackupsAvailable()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.syncStatus = this.syncManager.syncStatus;
|
this.syncStatus = this.application.getSyncStatus();
|
||||||
this.syncManager.getServerURL().then((url) => {
|
this.loadHost();
|
||||||
this.setState({
|
this.checkForSecurityUpdate();
|
||||||
server: url,
|
|
||||||
formData: { ...this.state.formData, url: url }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.authManager.checkForSecurityUpdate().then((available) => {
|
|
||||||
this.setState({
|
|
||||||
securityUpdateAvailable: available
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.reloadAutoLockInterval();
|
this.reloadAutoLockInterval();
|
||||||
|
this.loadBackupsAvailability();
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
@@ -97,15 +78,51 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptedBackupsAvailable() {
|
async loadHost() {
|
||||||
return !isNullOrUndefined(this.authManager.user) || this.passcodeManager.hasPasscode();
|
const host = await this.application.getHost();
|
||||||
|
this.setState({
|
||||||
|
server: host,
|
||||||
|
formData: {
|
||||||
|
...this.state.formData,
|
||||||
|
url: host
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkForSecurityUpdate() {
|
||||||
|
const available = await this.godService.checkForSecurityUpdate();
|
||||||
|
this.setState({
|
||||||
|
securityUpdateAvailable: available
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadBackupsAvailability() {
|
||||||
|
const hasUser = !isNullOrUndefined(await this.application.getUser());
|
||||||
|
const hasPasscode = await this.application.hasPasscode();
|
||||||
|
const encryptedAvailable = hasUser || hasPasscode;
|
||||||
|
|
||||||
|
function encryptionStatusString() {
|
||||||
|
if (hasUser) {
|
||||||
|
return STRING_E2E_ENABLED;
|
||||||
|
} else if (hasPasscode) {
|
||||||
|
return STRING_LOCAL_ENC_ENABLED;
|
||||||
|
} else {
|
||||||
|
return STRING_ENC_NOT_ENABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
encryptionStatusString: encryptionStatusString(),
|
||||||
|
encryptionEnabled: encryptedAvailable,
|
||||||
|
mutable: {
|
||||||
|
...this.state.mutable,
|
||||||
|
backupEncrypted: encryptedAvailable
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
submitMfaForm() {
|
submitMfaForm() {
|
||||||
const params = {
|
this.login();
|
||||||
[this.state.formData.mfa.payload.mfa_key]: this.state.formData.userMfaCode
|
|
||||||
};
|
|
||||||
this.login(params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blurAuthFields() {
|
blurAuthFields() {
|
||||||
@@ -114,9 +131,9 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
ELEMENT_NAME_AUTH_PASSWORD,
|
ELEMENT_NAME_AUTH_PASSWORD,
|
||||||
ELEMENT_NAME_AUTH_PASSWORD_CONF
|
ELEMENT_NAME_AUTH_PASSWORD_CONF
|
||||||
];
|
];
|
||||||
for(const name of names) {
|
for (const name of names) {
|
||||||
const element = document.getElementsByName(name)[0];
|
const element = document.getElementsByName(name)[0];
|
||||||
if(element) {
|
if (element) {
|
||||||
element.blur();
|
element.blur();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,29 +160,25 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(extraParams) {
|
async login() {
|
||||||
/** Prevent a timed sync from occuring while signing in. */
|
|
||||||
this.syncManager.lockSyncing();
|
|
||||||
await this.setFormDataState({
|
await this.setFormDataState({
|
||||||
status: STRING_GENERATING_LOGIN_KEYS,
|
status: STRING_GENERATING_LOGIN_KEYS,
|
||||||
authenticating: true
|
authenticating: true
|
||||||
});
|
});
|
||||||
const response = await this.authManager.login(
|
const response = await this.application.signIn({
|
||||||
this.state.formData.url,
|
email: this.state.formData.email,
|
||||||
this.state.formData.email,
|
password: this.state.formData.user_password,
|
||||||
this.state.formData.user_password,
|
strict: this.state.formData.strictSignin,
|
||||||
this.state.formData.ephemeral,
|
ephemeral: this.state.formData.ephemeral,
|
||||||
this.state.formData.strictSignin,
|
mfaKeyPath: this.state.formData.mfa.payload.mfa_key,
|
||||||
extraParams
|
mfaCode: this.state.formData.userMfaCode,
|
||||||
);
|
mergeLocal: this.state.formData.mergeLocal
|
||||||
|
});
|
||||||
const hasError = !response || response.error;
|
const hasError = !response || response.error;
|
||||||
if (!hasError) {
|
if (!hasError) {
|
||||||
await this.onAuthSuccess();
|
await this.onAuthSuccess();
|
||||||
this.syncManager.unlockSyncing();
|
|
||||||
this.syncManager.sync({ performIntegrityCheck: true });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.syncManager.unlockSyncing();
|
|
||||||
await this.setFormDataState({
|
await this.setFormDataState({
|
||||||
status: null,
|
status: null,
|
||||||
});
|
});
|
||||||
@@ -174,7 +187,7 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
: { message: "An unknown error occured." };
|
: { message: "An unknown error occured." };
|
||||||
|
|
||||||
if (error.tag === 'mfa-required' || error.tag === 'mfa-invalid') {
|
if (error.tag === 'mfa-required' || error.tag === 'mfa-invalid') {
|
||||||
await this.setFormDataState({
|
await this.setFormDataState({
|
||||||
showLogin: false,
|
showLogin: false,
|
||||||
mfa: error
|
mfa: error
|
||||||
});
|
});
|
||||||
@@ -184,7 +197,7 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
mfa: null
|
mfa: null
|
||||||
});
|
});
|
||||||
if (error.message) {
|
if (error.message) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: error.message
|
text: error.message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -197,22 +210,22 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
async register() {
|
async register() {
|
||||||
const confirmation = this.state.formData.password_conf;
|
const confirmation = this.state.formData.password_conf;
|
||||||
if (confirmation !== this.state.formData.user_password) {
|
if (confirmation !== this.state.formData.user_password) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: STRING_NON_MATCHING_PASSWORDS
|
text: STRING_NON_MATCHING_PASSWORDS
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.setFormDataState({
|
await this.setFormDataState({
|
||||||
confirmPassword: false,
|
confirmPassword: false,
|
||||||
status: STRING_GENERATING_REGISTER_KEYS,
|
status: STRING_GENERATING_REGISTER_KEYS,
|
||||||
authenticating: true
|
authenticating: true
|
||||||
});
|
});
|
||||||
const response = await this.authManager.register(
|
const response = await this.application.register({
|
||||||
this.state.formData.url,
|
email: this.state.formData.email,
|
||||||
this.state.formData.email,
|
password: this.state.formData.user_password,
|
||||||
this.state.formData.user_password,
|
ephemeral: this.state.formData.ephemeral,
|
||||||
this.state.formData.ephemeral
|
mergeLocal: this.state.formData.mergeLocal
|
||||||
);
|
});
|
||||||
if (!response || response.error) {
|
if (!response || response.error) {
|
||||||
await this.setFormDataState({
|
await this.setFormDataState({
|
||||||
status: null
|
status: null
|
||||||
@@ -223,18 +236,18 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
await this.setFormDataState({
|
await this.setFormDataState({
|
||||||
authenticating: false
|
authenticating: false
|
||||||
});
|
});
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: error.message
|
text: error.message
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await this.onAuthSuccess();
|
await this.onAuthSuccess();
|
||||||
this.syncManager.sync();
|
this.application.sync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeLocalChanged() {
|
mergeLocalChanged() {
|
||||||
if (!this.state.formData.mergeLocal) {
|
if (!this.state.formData.mergeLocal) {
|
||||||
this.alertManager.confirm({
|
this.application.alertManager.confirm({
|
||||||
text: STRING_ACCOUNT_MENU_UNCHECK_MERGE,
|
text: STRING_ACCOUNT_MENU_UNCHECK_MERGE,
|
||||||
destructive: true,
|
destructive: true,
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
@@ -249,34 +262,30 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
async onAuthSuccess() {
|
async onAuthSuccess() {
|
||||||
if (this.state.formData.mergeLocal) {
|
if (this.state.formData.mergeLocal) {
|
||||||
this.$rootScope.$broadcast('major-data-change');
|
this.$rootScope.$broadcast('major-data-change');
|
||||||
await this.clearDatabaseAndRewriteAllItems({ alternateUuids: true });
|
await this.rewriteDatabase({ alternateUuids: true });
|
||||||
} else {
|
|
||||||
this.modelManager.removeAllItemsFromMemory();
|
|
||||||
await this.storageManager.clearAllModels();
|
|
||||||
}
|
}
|
||||||
await this.setFormDataState({
|
await this.setFormDataState({
|
||||||
authenticating: false
|
authenticating: false
|
||||||
});
|
});
|
||||||
this.syncManager.refreshErroredItems();
|
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
openPasswordWizard(type) {
|
openPasswordWizard(type) {
|
||||||
this.close();
|
this.close();
|
||||||
this.authManager.presentPasswordWizard(type);
|
this.godService.presentPasswordWizard(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
async openPrivilegesModal() {
|
async openPrivilegesModal() {
|
||||||
this.close();
|
this.close();
|
||||||
const run = () => {
|
const run = () => {
|
||||||
this.privilegesManager.presentPrivilegesManagementModal();
|
this.godService.presentPrivilegesManagementModal();
|
||||||
};
|
};
|
||||||
const needsPrivilege = await this.privilegesManager.actionRequiresPrivilege(
|
const needsPrivilege = await this.application.privilegesManager.actionRequiresPrivilege(
|
||||||
PrivilegesManager.ActionManagePrivileges
|
ProtectedActions.ManagePrivileges
|
||||||
);
|
);
|
||||||
if (needsPrivilege) {
|
if (needsPrivilege) {
|
||||||
this.privilegesManager.presentPrivilegesModal(
|
this.godService.presentPrivilegesModal(
|
||||||
PrivilegesManager.ActionManagePrivileges,
|
ProtectedActions.ManagePrivileges,
|
||||||
() => {
|
() => {
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
@@ -288,21 +297,21 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows IndexedDB unencrypted logs to be deleted
|
* Allows IndexedDB unencrypted logs to be deleted
|
||||||
* `clearAllModels` will remove data from backing store,
|
* `clearAllPayloads` will remove data from backing store,
|
||||||
* but not from working memory See:
|
* but not from working memory See:
|
||||||
* https://github.com/standardnotes/desktop/issues/131
|
* https://github.com/standardnotes/desktop/issues/131
|
||||||
*/
|
*/
|
||||||
async clearDatabaseAndRewriteAllItems({ alternateUuids } = {}) {
|
async rewriteDatabase({ alternateUuids } = {}) {
|
||||||
await this.storageManager.clearAllModels();
|
await this.application.destroyDatabase();
|
||||||
await this.syncManager.markAllItemsDirtyAndSaveOffline(alternateUuids);
|
await this.application.markAllItemsAsNeedingSync({ alternateUuids });
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyLocalData() {
|
destroyLocalData() {
|
||||||
this.alertManager.confirm({
|
this.application.alertManager.confirm({
|
||||||
text: STRING_SIGN_OUT_CONFIRMATION,
|
text: STRING_SIGN_OUT_CONFIRMATION,
|
||||||
destructive: true,
|
destructive: true,
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
await this.authManager.signout(true);
|
await this.application.signOut();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -323,7 +332,7 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
const data = JSON.parse(e.target.result);
|
const data = JSON.parse(e.target.result);
|
||||||
resolve(data);
|
resolve(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: STRING_INVALID_IMPORT_FILE
|
text: STRING_INVALID_IMPORT_FILE
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -360,12 +369,12 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
await this.performImport(data, null);
|
await this.performImport(data, null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const needsPrivilege = await this.privilegesManager.actionRequiresPrivilege(
|
const needsPrivilege = await this.application.privilegesManager.actionRequiresPrivilege(
|
||||||
PrivilegesManager.ActionManageBackups
|
ProtectedActions.ManageBackups
|
||||||
);
|
);
|
||||||
if (needsPrivilege) {
|
if (needsPrivilege) {
|
||||||
this.privilegesManager.presentPrivilegesModal(
|
this.godService.presentPrivilegesModal(
|
||||||
PrivilegesManager.ActionManageBackups,
|
ProtectedActions.ManageBackups,
|
||||||
run
|
run
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -386,47 +395,22 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
});
|
});
|
||||||
if (errorCount > 0) {
|
if (errorCount > 0) {
|
||||||
const message = StringImportError({ errorCount: errorCount });
|
const message = StringImportError({ errorCount: errorCount });
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: message
|
text: message
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: STRING_IMPORT_SUCCESS
|
text: STRING_IMPORT_SUCCESS
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async importJSONData(data, password) {
|
async importJSONData(data, password) {
|
||||||
let errorCount = 0;
|
const { affectedItems, errorCount } = await this.application.importData({
|
||||||
if (data.auth_params) {
|
data: data.items,
|
||||||
const keys = await protocolManager.computeEncryptionKeysForUser(
|
password: password
|
||||||
password,
|
});
|
||||||
data.auth_params
|
for (const item of affectedItems) {
|
||||||
);
|
|
||||||
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
|
* Don't want to activate any components during import process in
|
||||||
* case of exceptions breaking up the import proccess
|
* case of exceptions breaking up the import proccess
|
||||||
@@ -435,8 +419,6 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
item.active = false;
|
item.active = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.syncManager.sync();
|
|
||||||
return errorCount;
|
return errorCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,10 +427,12 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
notesAndTagsCount() {
|
notesAndTagsCount() {
|
||||||
return this.modelManager.allItemsMatchingTypes([
|
return this.application.getItems({
|
||||||
'Note',
|
contentType: [
|
||||||
'Tag'
|
'Note',
|
||||||
]).length;
|
'Tag'
|
||||||
|
]
|
||||||
|
}).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptionStatusForNotes() {
|
encryptionStatusForNotes() {
|
||||||
@@ -456,32 +440,8 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
return length + "/" + length + " notes and tags encrypted";
|
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() {
|
async reloadAutoLockInterval() {
|
||||||
const interval = await this.passcodeManager.getAutoLockInterval();
|
const interval = await this.lockManager.getAutoLockInterval();
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedAutoLockInterval: interval
|
selectedAutoLockInterval: interval
|
||||||
});
|
});
|
||||||
@@ -489,15 +449,15 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
|
|
||||||
async selectAutoLockInterval(interval) {
|
async selectAutoLockInterval(interval) {
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
await this.passcodeManager.setAutoLockInterval(interval);
|
await this.lockManager.setAutoLockInterval(interval);
|
||||||
this.reloadAutoLockInterval();
|
this.reloadAutoLockInterval();
|
||||||
};
|
};
|
||||||
const needsPrivilege = await this.privilegesManager.actionRequiresPrivilege(
|
const needsPrivilege = await this.application.privilegesManager.actionRequiresPrivilege(
|
||||||
PrivilegesManager.ActionManagePasscode
|
ProtectedActions.ManagePasscode
|
||||||
);
|
);
|
||||||
if (needsPrivilege) {
|
if (needsPrivilege) {
|
||||||
this.privilegesManager.presentPrivilegesModal(
|
this.godService.presentPrivilegesModal(
|
||||||
PrivilegesManager.ActionManagePasscode,
|
ProtectedActions.ManagePasscode,
|
||||||
() => {
|
() => {
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
@@ -508,7 +468,7 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hasPasscode() {
|
hasPasscode() {
|
||||||
return this.passcodeManager.hasPasscode();
|
return this.application.hasPasscode();
|
||||||
}
|
}
|
||||||
|
|
||||||
addPasscodeClicked() {
|
addPasscodeClicked() {
|
||||||
@@ -520,23 +480,23 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
submitPasscodeForm() {
|
submitPasscodeForm() {
|
||||||
const passcode = this.state.formData.passcode;
|
const passcode = this.state.formData.passcode;
|
||||||
if (passcode !== this.state.formData.confirmPasscode) {
|
if (passcode !== this.state.formData.confirmPasscode) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: STRING_NON_MATCHING_PASSCODES
|
text: STRING_NON_MATCHING_PASSCODES
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const func = this.state.formData.changingPasscode
|
const func = this.state.formData.changingPasscode
|
||||||
? this.passcodeManager.changePasscode.bind(this.passcodeManager)
|
? this.application.changePasscode.bind(this.application)
|
||||||
: this.passcodeManager.setPasscode.bind(this.passcodeManager);
|
: this.application.setPasscode.bind(this.application);
|
||||||
func(passcode, async () => {
|
func(passcode, async () => {
|
||||||
await this.setFormDataState({
|
await this.setFormDataState({
|
||||||
passcode: null,
|
passcode: null,
|
||||||
confirmPasscode: null,
|
confirmPasscode: null,
|
||||||
showPasscodeForm: false
|
showPasscodeForm: false
|
||||||
});
|
});
|
||||||
if (await this.authManager.offline()) {
|
if (isNullOrUndefined(await this.application.getUser())) {
|
||||||
this.$rootScope.$broadcast('major-data-change');
|
this.$rootScope.$broadcast('major-data-change');
|
||||||
this.clearDatabaseAndRewriteAllItems();
|
this.rewriteDatabase();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -546,12 +506,12 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
this.state.formData.changingPasscode = true;
|
this.state.formData.changingPasscode = true;
|
||||||
this.addPasscodeClicked();
|
this.addPasscodeClicked();
|
||||||
};
|
};
|
||||||
const needsPrivilege = await this.privilegesManager.actionRequiresPrivilege(
|
const needsPrivilege = await this.application.privilegesManager.actionRequiresPrivilege(
|
||||||
PrivilegesManager.ActionManagePasscode
|
ProtectedActions.ManagePasscode
|
||||||
);
|
);
|
||||||
if (needsPrivilege) {
|
if (needsPrivilege) {
|
||||||
this.privilegesManager.presentPrivilegesModal(
|
this.godService.presentPrivilegesModal(
|
||||||
PrivilegesManager.ActionManagePasscode,
|
ProtectedActions.ManagePasscode,
|
||||||
run
|
run
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -560,34 +520,24 @@ class AccountMenuCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removePasscodePressed() {
|
async removePasscodePressed() {
|
||||||
const run = () => {
|
const run = async () => {
|
||||||
const signedIn = !this.authManager.offline();
|
const signedIn = !isNullOrUndefined(await this.application.getUser());
|
||||||
let message = STRING_REMOVE_PASSCODE_CONFIRMATION;
|
let message = STRING_REMOVE_PASSCODE_CONFIRMATION;
|
||||||
if (!signedIn) {
|
if (!signedIn) {
|
||||||
message += STRING_REMOVE_PASSCODE_OFFLINE_ADDENDUM;
|
message += STRING_REMOVE_PASSCODE_OFFLINE_ADDENDUM;
|
||||||
}
|
}
|
||||||
this.alertManager.confirm({
|
this.application.alertManager.confirm({
|
||||||
text: message,
|
text: message,
|
||||||
destructive: true,
|
destructive: true,
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
this.passcodeManager.clearPasscode();
|
this.application.removePasscode();
|
||||||
if (this.authManager.offline()) {
|
|
||||||
this.syncManager.markAllItemsDirtyAndSaveOffline();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const needsPrivilege = await this.privilegesManager.actionRequiresPrivilege(
|
this.godService.presentPrivilegesModal(
|
||||||
PrivilegesManager.ActionManagePasscode
|
ProtectedActions.ManagePasscode,
|
||||||
|
run
|
||||||
);
|
);
|
||||||
if (needsPrivilege) {
|
|
||||||
this.privilegesManager.presentPrivilegesModal(
|
|
||||||
PrivilegesManager.ActionManagePasscode,
|
|
||||||
run
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
run();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isDesktopApplication() {
|
isDesktopApplication() {
|
||||||
|
|||||||
@@ -14,15 +14,15 @@ class ComponentViewCtrl {
|
|||||||
$scope,
|
$scope,
|
||||||
$rootScope,
|
$rootScope,
|
||||||
$timeout,
|
$timeout,
|
||||||
componentManager,
|
application,
|
||||||
desktopManager,
|
desktopManager,
|
||||||
themeManager
|
themeManager
|
||||||
) {
|
) {
|
||||||
this.$rootScope = $rootScope;
|
this.$rootScope = $rootScope;
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
|
this.application = application;
|
||||||
this.themeManager = themeManager;
|
this.themeManager = themeManager;
|
||||||
this.desktopManager = desktopManager;
|
this.desktopManager = desktopManager;
|
||||||
this.componentManager = componentManager;
|
|
||||||
this.componentValid = true;
|
this.componentValid = true;
|
||||||
|
|
||||||
$scope.$watch('ctrl.component', (component, prevComponent) => {
|
$scope.$watch('ctrl.component', (component, prevComponent) => {
|
||||||
@@ -52,7 +52,7 @@ class ComponentViewCtrl {
|
|||||||
|
|
||||||
registerComponentHandlers() {
|
registerComponentHandlers() {
|
||||||
this.themeHandlerIdentifier = 'component-view-' + Math.random();
|
this.themeHandlerIdentifier = 'component-view-' + Math.random();
|
||||||
this.componentManager.registerHandler({
|
this.application.componentManager.registerHandler({
|
||||||
identifier: this.themeHandlerIdentifier,
|
identifier: this.themeHandlerIdentifier,
|
||||||
areas: ['themes'],
|
areas: ['themes'],
|
||||||
activationHandler: (component) => {
|
activationHandler: (component) => {
|
||||||
@@ -61,7 +61,7 @@ class ComponentViewCtrl {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.identifier = 'component-view-' + Math.random();
|
this.identifier = 'component-view-' + Math.random();
|
||||||
this.componentManager.registerHandler({
|
this.application.componentManager.registerHandler({
|
||||||
identifier: this.identifier,
|
identifier: this.identifier,
|
||||||
areas: [this.component.area],
|
areas: [this.component.area],
|
||||||
activationHandler: (component) => {
|
activationHandler: (component) => {
|
||||||
@@ -74,7 +74,7 @@ class ComponentViewCtrl {
|
|||||||
},
|
},
|
||||||
actionHandler: (component, action, data) => {
|
actionHandler: (component, action, data) => {
|
||||||
if(action === 'set-size') {
|
if(action === 'set-size') {
|
||||||
this.componentManager.handleSetSizeEvent(component, data);
|
this.application.componentManager.handleSetSizeEvent(component, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -91,7 +91,7 @@ class ComponentViewCtrl {
|
|||||||
|
|
||||||
async reloadComponent() {
|
async reloadComponent() {
|
||||||
this.componentValid = false;
|
this.componentValid = false;
|
||||||
await this.componentManager.reloadComponent(this.component);
|
await this.application.componentManager.reloadComponent(this.component);
|
||||||
this.reloadStatus();
|
this.reloadStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ class ComponentViewCtrl {
|
|||||||
}
|
}
|
||||||
if(this.componentValid !== previouslyValid) {
|
if(this.componentValid !== previouslyValid) {
|
||||||
if(this.componentValid) {
|
if(this.componentValid) {
|
||||||
this.componentManager.reloadComponent(component, true);
|
this.application.componentManager.reloadComponent(component, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(this.expired && doManualReload) {
|
if(this.expired && doManualReload) {
|
||||||
@@ -140,7 +140,7 @@ class ComponentViewCtrl {
|
|||||||
if(!this.component.active) {
|
if(!this.component.active) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const iframe = this.componentManager.iframeForComponent(
|
const iframe = this.application.componentManager.iframeForComponent(
|
||||||
this.component
|
this.component
|
||||||
);
|
);
|
||||||
if(!iframe) {
|
if(!iframe) {
|
||||||
@@ -186,7 +186,7 @@ class ComponentViewCtrl {
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
this.$timeout.cancel(this.loadTimeout);
|
this.$timeout.cancel(this.loadTimeout);
|
||||||
await this.componentManager.registerComponentWindow(
|
await this.application.componentManager.registerComponentWindow(
|
||||||
this.component,
|
this.component,
|
||||||
iframe.contentWindow
|
iframe.contentWindow
|
||||||
);
|
);
|
||||||
@@ -201,13 +201,13 @@ class ComponentViewCtrl {
|
|||||||
componentValueDidSet(component, prevComponent) {
|
componentValueDidSet(component, prevComponent) {
|
||||||
const dontSync = true;
|
const dontSync = true;
|
||||||
if(prevComponent && component !== prevComponent) {
|
if(prevComponent && component !== prevComponent) {
|
||||||
this.componentManager.deactivateComponent(
|
this.application.componentManager.deactivateComponent(
|
||||||
prevComponent,
|
prevComponent,
|
||||||
dontSync
|
dontSync
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if(component) {
|
if(component) {
|
||||||
this.componentManager.activateComponent(
|
this.application.componentManager.activateComponent(
|
||||||
component,
|
component,
|
||||||
dontSync
|
dontSync
|
||||||
);
|
);
|
||||||
@@ -239,17 +239,17 @@ class ComponentViewCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getUrl() {
|
getUrl() {
|
||||||
const url = this.componentManager.urlForComponent(this.component);
|
const url = this.application.componentManager.urlForComponent(this.component);
|
||||||
this.component.runningLocally = (url === this.component.local_url);
|
this.component.runningLocally = (url === this.component.local_url);
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.componentManager.deregisterHandler(this.themeHandlerIdentifier);
|
this.application.componentManager.deregisterHandler(this.themeHandlerIdentifier);
|
||||||
this.componentManager.deregisterHandler(this.identifier);
|
this.application.componentManager.deregisterHandler(this.identifier);
|
||||||
if(this.component && !this.manualDealloc) {
|
if(this.component && !this.manualDealloc) {
|
||||||
const dontSync = true;
|
const dontSync = true;
|
||||||
this.componentManager.deactivateComponent(this.component, dontSync);
|
this.application.componentManager.deactivateComponent(this.component, dontSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.desktopManager.deregisterUpdateObserver(this.updateObserver);
|
this.desktopManager.deregisterUpdateObserver(this.updateObserver);
|
||||||
|
|||||||
@@ -4,16 +4,12 @@ class ConflictResolutionCtrl {
|
|||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
$element,
|
$element,
|
||||||
alertManager,
|
|
||||||
archiveManager,
|
archiveManager,
|
||||||
modelManager,
|
application
|
||||||
syncManager
|
|
||||||
) {
|
) {
|
||||||
this.$element = $element;
|
this.$element = $element;
|
||||||
this.alertManager = alertManager;
|
this.application = application;
|
||||||
this.archiveManager = archiveManager;
|
this.archiveManager = archiveManager;
|
||||||
this.modelManager = modelManager;
|
|
||||||
this.syncManager = syncManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
@@ -31,35 +27,31 @@ class ConflictResolutionCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
keepItem1() {
|
keepItem1() {
|
||||||
this.alertManager.confirm({
|
this.application.alertManager.confirm({
|
||||||
text: `Are you sure you want to delete the item on the right?`,
|
text: `Are you sure you want to delete the item on the right?`,
|
||||||
destructive: true,
|
destructive: true,
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
this.modelManager.setItemToBeDeleted(this.item2);
|
this.application.deleteItem({item: this.item2});
|
||||||
this.syncManager.sync().then(() => {
|
this.triggerCallback();
|
||||||
this.applyCallback();
|
|
||||||
});
|
|
||||||
this.dismiss();
|
this.dismiss();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
keepItem2() {
|
keepItem2() {
|
||||||
this.alertManager.confirm({
|
this.application.alertManager.confirm({
|
||||||
text: `Are you sure you want to delete the item on the left?`,
|
text: `Are you sure you want to delete the item on the left?`,
|
||||||
destructive: true,
|
destructive: true,
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
this.modelManager.setItemToBeDeleted(this.item1);
|
this.application.deleteItem({item: this.item1});
|
||||||
this.syncManager.sync().then(() => {
|
this.triggerCallback();
|
||||||
this.applyCallback();
|
|
||||||
});
|
|
||||||
this.dismiss();
|
this.dismiss();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
keepBoth() {
|
keepBoth() {
|
||||||
this.applyCallback();
|
this.triggerCallback();
|
||||||
this.dismiss();
|
this.dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +62,7 @@ class ConflictResolutionCtrl {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyCallback() {
|
triggerCallback() {
|
||||||
this.callback && this.callback();
|
this.callback && this.callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,22 +6,17 @@ class EditorMenuCtrl extends PureCtrl {
|
|||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
$timeout,
|
$timeout,
|
||||||
componentManager,
|
application
|
||||||
modelManager,
|
|
||||||
syncManager,
|
|
||||||
) {
|
) {
|
||||||
super($timeout);
|
super($timeout);
|
||||||
this.$timeout = $timeout;
|
this.application = application;
|
||||||
this.componentManager = componentManager;
|
|
||||||
this.modelManager = modelManager;
|
|
||||||
this.syncManager = syncManager;
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isDesktop: isDesktopApplication()
|
isDesktop: isDesktopApplication()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
const editors = this.componentManager.componentsForArea('editor-editor')
|
const editors = this.application.componentManager.componentsForArea('editor-editor')
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
|
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
|
||||||
});
|
});
|
||||||
@@ -36,8 +31,7 @@ class EditorMenuCtrl extends PureCtrl {
|
|||||||
if(component) {
|
if(component) {
|
||||||
if(component.content.conflict_of) {
|
if(component.content.conflict_of) {
|
||||||
component.content.conflict_of = null;
|
component.content.conflict_of = null;
|
||||||
this.modelManager.setItemDirty(component, true);
|
this.application.saveItem({item: component});
|
||||||
this.syncManager.sync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$timeout(() => {
|
this.$timeout(() => {
|
||||||
@@ -58,16 +52,15 @@ class EditorMenuCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
makeEditorDefault(component) {
|
makeEditorDefault(component) {
|
||||||
const currentDefault = this.componentManager
|
const currentDefault = this.application.componentManager
|
||||||
.componentsForArea('editor-editor')
|
.componentsForArea('editor-editor')
|
||||||
.filter((e) => e.isDefaultEditor())[0];
|
.filter((e) => e.isDefaultEditor())[0];
|
||||||
if(currentDefault) {
|
if(currentDefault) {
|
||||||
currentDefault.setAppDataItem('defaultEditor', false);
|
currentDefault.setAppDataItem('defaultEditor', false);
|
||||||
this.modelManager.setItemDirty(currentDefault);
|
this.application.setItemsNeedsSync({item: currentDefault});
|
||||||
}
|
}
|
||||||
component.setAppDataItem('defaultEditor', true);
|
component.setAppDataItem('defaultEditor', true);
|
||||||
this.modelManager.setItemDirty(component);
|
this.application.saveItem({ item: component });
|
||||||
this.syncManager.sync();
|
|
||||||
this.setState({
|
this.setState({
|
||||||
defaultEditor: component
|
defaultEditor: component
|
||||||
});
|
});
|
||||||
@@ -75,8 +68,7 @@ class EditorMenuCtrl extends PureCtrl {
|
|||||||
|
|
||||||
removeEditorDefault(component) {
|
removeEditorDefault(component) {
|
||||||
component.setAppDataItem('defaultEditor', false);
|
component.setAppDataItem('defaultEditor', false);
|
||||||
this.modelManager.setItemDirty(component);
|
this.application.saveItem({ item: component });
|
||||||
this.syncManager.sync();
|
|
||||||
this.setState({
|
this.setState({
|
||||||
defaultEditor: null
|
defaultEditor: null
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { protocolManager } from 'snjs';
|
|
||||||
import template from '%/directives/password-wizard.pug';
|
import template from '%/directives/password-wizard.pug';
|
||||||
import { STRING_FAILED_PASSWORD_CHANGE } from '@/strings';
|
import { STRING_FAILED_PASSWORD_CHANGE } from '@/strings';
|
||||||
|
import { isNullOrUndefined } from '../../utils';
|
||||||
|
|
||||||
const DEFAULT_CONTINUE_TITLE = "Continue";
|
const DEFAULT_CONTINUE_TITLE = "Continue";
|
||||||
const Steps = {
|
const Steps = {
|
||||||
@@ -18,25 +18,17 @@ class PasswordWizardCtrl {
|
|||||||
$element,
|
$element,
|
||||||
$scope,
|
$scope,
|
||||||
$timeout,
|
$timeout,
|
||||||
alertManager,
|
archiveManager
|
||||||
archiveManager,
|
|
||||||
authManager,
|
|
||||||
modelManager,
|
|
||||||
syncManager,
|
|
||||||
) {
|
) {
|
||||||
this.$element = $element;
|
this.$element = $element;
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
this.alertManager = alertManager;
|
|
||||||
this.archiveManager = archiveManager;
|
this.archiveManager = archiveManager;
|
||||||
this.authManager = authManager;
|
|
||||||
this.modelManager = modelManager;
|
|
||||||
this.syncManager = syncManager;
|
|
||||||
this.registerWindowUnloadStopper();
|
this.registerWindowUnloadStopper();
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
this.syncStatus = this.syncManager.syncStatus;
|
this.syncStatus = this.application.getSyncStatus();
|
||||||
this.formData = {};
|
this.formData = {};
|
||||||
this.configureDefaults();
|
this.configureDefaults();
|
||||||
}
|
}
|
||||||
@@ -139,20 +131,11 @@ class PasswordWizardCtrl {
|
|||||||
this.formData.status = "Unable to process your password. Please try again.";
|
this.formData.status = "Unable to process your password. Please try again.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.formData.status = "Encrypting and syncing data with new keys...";
|
this.lockContinue = false;
|
||||||
|
if (this.changePassword) {
|
||||||
const syncSuccess = await this.resyncData();
|
this.formData.status = "Successfully changed password.";
|
||||||
this.formData.statusError = !syncSuccess;
|
} else if (this.securityUpdate) {
|
||||||
this.formData.processing = !syncSuccess;
|
this.formData.status = "Successfully performed security update.";
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,28 +143,28 @@ class PasswordWizardCtrl {
|
|||||||
const currentPassword = this.formData.currentPassword;
|
const currentPassword = this.formData.currentPassword;
|
||||||
const newPass = this.securityUpdate ? currentPassword : this.formData.newPassword;
|
const newPass = this.securityUpdate ? currentPassword : this.formData.newPassword;
|
||||||
if (!currentPassword || currentPassword.length === 0) {
|
if (!currentPassword || currentPassword.length === 0) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: "Please enter your current password."
|
text: "Please enter your current password."
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.changePassword) {
|
if (this.changePassword) {
|
||||||
if (!newPass || newPass.length === 0) {
|
if (!newPass || newPass.length === 0) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: "Please enter a new password."
|
text: "Please enter a new password."
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (newPass !== this.formData.newPasswordConfirmation) {
|
if (newPass !== this.formData.newPasswordConfirmation) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: "Your new password does not match its confirmation."
|
text: "Your new password does not match its confirmation."
|
||||||
});
|
});
|
||||||
this.formData.status = null;
|
this.formData.status = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this.authManager.user.email) {
|
if (!this.application.getUser().email) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: "We don't have your email stored. Please log out then log back in to fix this issue."
|
text: "We don't have your email stored. Please log out then log back in to fix this issue."
|
||||||
});
|
});
|
||||||
this.formData.status = null;
|
this.formData.status = null;
|
||||||
@@ -189,61 +172,31 @@ class PasswordWizardCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Validate current password */
|
/** Validate current password */
|
||||||
const authParams = await this.authManager.getAuthParams();
|
const key = await this.application.validateAccountPassword({
|
||||||
const password = this.formData.currentPassword;
|
password: this.formData.currentPassword
|
||||||
const keys = await protocolManager.computeEncryptionKeysForUser(
|
});
|
||||||
password,
|
if (key) {
|
||||||
authParams
|
this.currentServerPassword = key.serverPassword;
|
||||||
);
|
|
||||||
const success = keys.mk === (await this.authManager.keys()).mk;
|
|
||||||
if (success) {
|
|
||||||
this.currentServerPw = keys.pw;
|
|
||||||
} else {
|
} else {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: "The current password you entered is not correct. Please try again."
|
text: "The current password you entered is not correct. Please try again."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return success;
|
return !isNullOrUndefined(key);
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
async processPasswordChange() {
|
||||||
const newUserPassword = this.securityUpdate
|
const newPassword = this.securityUpdate
|
||||||
? this.formData.currentPassword
|
? this.formData.currentPassword
|
||||||
: this.formData.newPassword;
|
: this.formData.newPassword;
|
||||||
const currentServerPw = this.currentServerPw;
|
|
||||||
const results = await protocolManager.generateInitialKeysAndAuthParamsForUser(
|
const response = await this.application.changePassword({
|
||||||
this.authManager.user.email,
|
email: this.application.getUser().email,
|
||||||
newUserPassword
|
currentPassword: this.formData.currentPassword,
|
||||||
);
|
newPassword: newPassword
|
||||||
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) {
|
if (response.error) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: response.error.message
|
text: 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."
|
||||||
@@ -260,7 +213,7 @@ class PasswordWizardCtrl {
|
|||||||
|
|
||||||
dismiss() {
|
dismiss() {
|
||||||
if (this.lockContinue) {
|
if (this.lockContinue) {
|
||||||
this.alertManager.alert({
|
this.application.alertManager.alert({
|
||||||
text: "Cannot close window until pending tasks are complete."
|
text: "Cannot close window until pending tasks are complete."
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,22 +5,24 @@ class PrivilegesAuthModalCtrl {
|
|||||||
constructor(
|
constructor(
|
||||||
$element,
|
$element,
|
||||||
$timeout,
|
$timeout,
|
||||||
privilegesManager,
|
application
|
||||||
) {
|
) {
|
||||||
this.$element = $element;
|
this.$element = $element;
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
this.privilegesManager = privilegesManager;
|
this.application = application;
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
this.authParameters = {};
|
this.authParameters = {};
|
||||||
this.sessionLengthOptions = this.privilegesManager.getSessionLengthOptions();
|
this.sessionLengthOptions = this.application.privilegesManager.getSessionLengthOptions();
|
||||||
this.privilegesManager.getSelectedSessionLength().then((length) => {
|
this.application.privilegesManager.getSelectedSessionLength()
|
||||||
|
.then((length) => {
|
||||||
this.$timeout(() => {
|
this.$timeout(() => {
|
||||||
this.selectedSessionLength = length;
|
this.selectedSessionLength = length;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.privilegesManager.netCredentialsForAction(this.action).then((credentials) => {
|
this.application.privilegesManager.netCredentialsForAction(this.action)
|
||||||
|
.then((credentials) => {
|
||||||
this.$timeout(() => {
|
this.$timeout(() => {
|
||||||
this.requiredCredentials = credentials.sort();
|
this.requiredCredentials = credentials.sort();
|
||||||
});
|
});
|
||||||
@@ -32,7 +34,7 @@ class PrivilegesAuthModalCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
promptForCredential(credential) {
|
promptForCredential(credential) {
|
||||||
return this.privilegesManager.displayInfoForCredential(credential).prompt;
|
return this.application.privilegesManager.displayInfoForCredential(credential).prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
@@ -65,13 +67,13 @@ class PrivilegesAuthModalCtrl {
|
|||||||
if (!this.validate()) {
|
if (!this.validate()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = await this.privilegesManager.authenticateAction(
|
const result = await this.application.privilegesManager.authenticateAction(
|
||||||
this.action,
|
this.action,
|
||||||
this.authParameters
|
this.authParameters
|
||||||
);
|
);
|
||||||
this.$timeout(() => {
|
this.$timeout(() => {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.privilegesManager.setSessionLength(this.selectedSessionLength);
|
this.application.privilegesManager.setSessionLength(this.selectedSessionLength);
|
||||||
this.onSuccess();
|
this.onSuccess();
|
||||||
this.dismiss();
|
this.dismiss();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -6,20 +6,18 @@ class PrivilegesManagementModalCtrl {
|
|||||||
constructor(
|
constructor(
|
||||||
$timeout,
|
$timeout,
|
||||||
$element,
|
$element,
|
||||||
privilegesManager,
|
application
|
||||||
authManager,
|
|
||||||
passcodeManager,
|
|
||||||
) {
|
) {
|
||||||
this.$element = $element;
|
this.$element = $element;
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
this.privilegesManager = privilegesManager;
|
this.application = application;
|
||||||
this.hasPasscode = passcodeManager.hasPasscode();
|
this.hasPasscode = application.hasPasscode();
|
||||||
this.hasAccount = !authManager.offline();
|
this.hasAccount = !application.noUser();
|
||||||
this.reloadPrivileges();
|
this.reloadPrivileges();
|
||||||
}
|
}
|
||||||
|
|
||||||
displayInfoForCredential(credential) {
|
displayInfoForCredential(credential) {
|
||||||
const info = this.privilegesManager.displayInfoForCredential(credential);
|
const info = this.application.privilegesManager.displayInfoForCredential(credential);
|
||||||
if (credential === PrivilegesManager.CredentialLocalPasscode) {
|
if (credential === PrivilegesManager.CredentialLocalPasscode) {
|
||||||
info.availability = this.hasPasscode;
|
info.availability = this.hasPasscode;
|
||||||
} else if (credential === PrivilegesManager.CredentialAccountPassword) {
|
} else if (credential === PrivilegesManager.CredentialAccountPassword) {
|
||||||
@@ -31,7 +29,7 @@ class PrivilegesManagementModalCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
displayInfoForAction(action) {
|
displayInfoForAction(action) {
|
||||||
return this.privilegesManager.displayInfoForAction(action).label;
|
return this.application.privilegesManager.displayInfoForAction(action).label;
|
||||||
}
|
}
|
||||||
|
|
||||||
isCredentialRequiredForAction(action, credential) {
|
isCredentialRequiredForAction(action, credential) {
|
||||||
@@ -42,21 +40,21 @@ class PrivilegesManagementModalCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async clearSession() {
|
async clearSession() {
|
||||||
await this.privilegesManager.clearSession();
|
await this.application.privilegesManager.clearSession();
|
||||||
this.reloadPrivileges();
|
this.reloadPrivileges();
|
||||||
}
|
}
|
||||||
|
|
||||||
async reloadPrivileges() {
|
async reloadPrivileges() {
|
||||||
this.availableActions = this.privilegesManager.getAvailableActions();
|
this.availableActions = this.application.privilegesManager.getAvailableActions();
|
||||||
this.availableCredentials = this.privilegesManager.getAvailableCredentials();
|
this.availableCredentials = this.application.privilegesManager.getAvailableCredentials();
|
||||||
const sessionEndDate = await this.privilegesManager.getSessionExpirey();
|
const sessionEndDate = await this.application.privilegesManager.getSessionExpirey();
|
||||||
this.sessionExpirey = sessionEndDate.toLocaleString();
|
this.sessionExpirey = sessionEndDate.toLocaleString();
|
||||||
this.sessionExpired = new Date() >= sessionEndDate;
|
this.sessionExpired = new Date() >= sessionEndDate;
|
||||||
this.credentialDisplayInfo = {};
|
this.credentialDisplayInfo = {};
|
||||||
for (const cred of this.availableCredentials) {
|
for (const cred of this.availableCredentials) {
|
||||||
this.credentialDisplayInfo[cred] = this.displayInfoForCredential(cred);
|
this.credentialDisplayInfo[cred] = this.displayInfoForCredential(cred);
|
||||||
}
|
}
|
||||||
const privs = await this.privilegesManager.getPrivileges();
|
const privs = await this.application.privilegesManager.getPrivileges();
|
||||||
this.$timeout(() => {
|
this.$timeout(() => {
|
||||||
this.privileges = privs;
|
this.privileges = privs;
|
||||||
});
|
});
|
||||||
@@ -64,7 +62,7 @@ class PrivilegesManagementModalCtrl {
|
|||||||
|
|
||||||
checkboxValueChanged(action, credential) {
|
checkboxValueChanged(action, credential) {
|
||||||
this.privileges.toggleCredentialForAction(action, credential);
|
this.privileges.toggleCredentialForAction(action, credential);
|
||||||
this.privilegesManager.savePrivileges();
|
this.application.privilegesManager.savePrivileges();
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
|
|||||||
@@ -7,23 +7,17 @@ class RevisionPreviewModalCtrl {
|
|||||||
$element,
|
$element,
|
||||||
$scope,
|
$scope,
|
||||||
$timeout,
|
$timeout,
|
||||||
alertManager,
|
|
||||||
componentManager,
|
|
||||||
modelManager,
|
|
||||||
syncManager,
|
syncManager,
|
||||||
) {
|
) {
|
||||||
this.$element = $element;
|
this.$element = $element;
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
this.alertManager = alertManager;
|
|
||||||
this.componentManager = componentManager;
|
|
||||||
this.modelManager = modelManager;
|
|
||||||
this.syncManager = syncManager;
|
this.syncManager = syncManager;
|
||||||
this.createNote();
|
this.createNote();
|
||||||
this.configureEditor();
|
this.configureEditor();
|
||||||
$scope.$on('$destroy', () => {
|
$scope.$on('$destroy', () => {
|
||||||
if (this.identifier) {
|
if (this.identifier) {
|
||||||
this.componentManager.deregisterHandler(this.identifier);
|
this.application.componentManager.deregisterHandler(this.identifier);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -41,7 +35,7 @@ class RevisionPreviewModalCtrl {
|
|||||||
* for note as not to save changes to original, if editor makes changes.
|
* for note as not to save changes to original, if editor makes changes.
|
||||||
*/
|
*/
|
||||||
this.note.uuid = this.uuid;
|
this.note.uuid = this.uuid;
|
||||||
const editorForNote = this.componentManager.editorForNote(this.note);
|
const editorForNote = this.application.componentManager.editorForNote(this.note);
|
||||||
this.note.uuid = protocolManager.crypto.generateUUIDSync();
|
this.note.uuid = protocolManager.crypto.generateUUIDSync();
|
||||||
if (editorForNote) {
|
if (editorForNote) {
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +49,7 @@ class RevisionPreviewModalCtrl {
|
|||||||
editorCopy.readonly = true;
|
editorCopy.readonly = true;
|
||||||
editorCopy.lockReadonly = true;
|
editorCopy.lockReadonly = true;
|
||||||
this.identifier = editorCopy.uuid;
|
this.identifier = editorCopy.uuid;
|
||||||
this.componentManager.registerHandler({
|
this.application.componentManager.registerHandler({
|
||||||
identifier: this.identifier,
|
identifier: this.identifier,
|
||||||
areas: ['editor-editor'],
|
areas: ['editor-editor'],
|
||||||
contextRequestHandler: (component) => {
|
contextRequestHandler: (component) => {
|
||||||
@@ -75,21 +69,21 @@ class RevisionPreviewModalCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
restore(asCopy) {
|
restore(asCopy) {
|
||||||
const run = () => {
|
const run = async () => {
|
||||||
let item;
|
let item;
|
||||||
if (asCopy) {
|
if (asCopy) {
|
||||||
const contentCopy = Object.assign({}, this.content);
|
const contentCopy = Object.assign({}, this.content);
|
||||||
if (contentCopy.title) {
|
if (contentCopy.title) {
|
||||||
contentCopy.title += " (copy)";
|
contentCopy.title += " (copy)";
|
||||||
}
|
}
|
||||||
item = this.modelManager.createItem({
|
item = await this.application.createItem({
|
||||||
content_type: 'Note',
|
contentType: 'Note',
|
||||||
content: contentCopy
|
content: contentCopy,
|
||||||
|
needsSync: true
|
||||||
});
|
});
|
||||||
this.modelManager.addItem(item);
|
|
||||||
} else {
|
} else {
|
||||||
const uuid = this.uuid;
|
const uuid = this.uuid;
|
||||||
item = this.modelManager.findItem(uuid);
|
item = this.application.findItem({uuid: uuid});
|
||||||
item.content = Object.assign({}, this.content);
|
item.content = Object.assign({}, this.content);
|
||||||
this.modelManager.mapResponseItemsToLocalModels(
|
this.modelManager.mapResponseItemsToLocalModels(
|
||||||
[item],
|
[item],
|
||||||
@@ -102,7 +96,7 @@ class RevisionPreviewModalCtrl {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!asCopy) {
|
if (!asCopy) {
|
||||||
this.alertManager.confirm({
|
this.application.alertManager.confirm({
|
||||||
text: "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?",
|
||||||
destructive: true,
|
destructive: true,
|
||||||
onConfirm: run
|
onConfirm: run
|
||||||
|
|||||||
@@ -12,11 +12,6 @@ import '../../../vendor/assets/javascripts/zip/inflate';
|
|||||||
import '../../../vendor/assets/javascripts/zip/zip';
|
import '../../../vendor/assets/javascripts/zip/zip';
|
||||||
import '../../../vendor/assets/javascripts/zip/z-worker';
|
import '../../../vendor/assets/javascripts/zip/z-worker';
|
||||||
|
|
||||||
import { SFItem } from 'snjs';
|
|
||||||
|
|
||||||
// Set the app domain before starting the app
|
|
||||||
SFItem.AppDomain = 'org.standardnotes.sn';
|
|
||||||
|
|
||||||
// entry point
|
// entry point
|
||||||
// eslint-disable-next-line import/first
|
// eslint-disable-next-line import/first
|
||||||
import './app';
|
import './app';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { SFAlertManager } from 'snjs';
|
import { SNAlertManager } from 'snjs';
|
||||||
import { SKAlert } from 'sn-stylekit';
|
import { SKAlert } from 'sn-stylekit';
|
||||||
|
|
||||||
export class AlertManager extends SFAlertManager {
|
export class AlertManager extends SNAlertManager {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor($timeout) {
|
constructor($timeout) {
|
||||||
super();
|
super();
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { PrivilegesManager } from '@/services/privilegesManager';
|
|||||||
|
|
||||||
export class ArchiveManager {
|
export class ArchiveManager {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(passcodeManager, authManager, modelManager, privilegesManager) {
|
constructor(lockManager, authManager, modelManager, privilegesManager) {
|
||||||
this.passcodeManager = passcodeManager;
|
this.lockManager = lockManager;
|
||||||
this.authManager = authManager;
|
this.authManager = authManager;
|
||||||
this.modelManager = modelManager;
|
this.modelManager = modelManager;
|
||||||
this.privilegesManager = privilegesManager;
|
this.privilegesManager = privilegesManager;
|
||||||
@@ -22,9 +22,9 @@ export class ArchiveManager {
|
|||||||
// download in Standard Notes format
|
// download in Standard Notes format
|
||||||
let keys, authParams;
|
let keys, authParams;
|
||||||
if(encrypted) {
|
if(encrypted) {
|
||||||
if(this.authManager.offline() && this.passcodeManager.hasPasscode()) {
|
if(this.authManager.offline() && this.lockManager.hasPasscode()) {
|
||||||
keys = this.passcodeManager.keys();
|
keys = this.lockManager.keys();
|
||||||
authParams = this.passcodeManager.passcodeAuthParams();
|
authParams = this.lockManager.passcodeAuthParams();
|
||||||
} else {
|
} else {
|
||||||
keys = await this.authManager.keys();
|
keys = await this.authManager.keys();
|
||||||
authParams = await this.authManager.getAuthParams();
|
authParams = await this.authManager.getAuthParams();
|
||||||
@@ -42,7 +42,7 @@ export class ArchiveManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if(await this.privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageBackups)) {
|
if(await this.privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageBackups)) {
|
||||||
this.privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManageBackups, () => {
|
this.godService.presentPrivilegesModal(PrivilegesManager.ActionManageBackups, () => {
|
||||||
run();
|
run();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
import angular from 'angular';
|
|
||||||
import { StorageManager } from './storageManager';
|
|
||||||
import { protocolManager, SFAuthManager } from 'snjs';
|
|
||||||
|
|
||||||
export class AuthManager extends SFAuthManager {
|
|
||||||
/* @ngInject */
|
|
||||||
constructor(
|
|
||||||
modelManager,
|
|
||||||
singletonManager,
|
|
||||||
storageManager,
|
|
||||||
dbManager,
|
|
||||||
httpManager,
|
|
||||||
$rootScope,
|
|
||||||
$timeout,
|
|
||||||
$compile
|
|
||||||
) {
|
|
||||||
super(storageManager, httpManager, null, $timeout);
|
|
||||||
this.$rootScope = $rootScope;
|
|
||||||
this.$compile = $compile;
|
|
||||||
this.modelManager = modelManager;
|
|
||||||
this.singletonManager = singletonManager;
|
|
||||||
this.storageManager = storageManager;
|
|
||||||
this.dbManager = dbManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadInitialData() {
|
|
||||||
const userData = this.storageManager.getItemSync("user");
|
|
||||||
if(userData) {
|
|
||||||
this.user = JSON.parse(userData);
|
|
||||||
} else {
|
|
||||||
// legacy, check for uuid
|
|
||||||
const idData = this.storageManager.getItemSync("uuid");
|
|
||||||
if(idData) {
|
|
||||||
this.user = {uuid: idData};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.checkForSecurityUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
offline() {
|
|
||||||
return !this.user;
|
|
||||||
}
|
|
||||||
|
|
||||||
isEphemeralSession() {
|
|
||||||
if(this.ephemeral == null || this.ephemeral == undefined) {
|
|
||||||
this.ephemeral = JSON.parse(this.storageManager.getItemSync("ephemeral", StorageManager.Fixed));
|
|
||||||
}
|
|
||||||
return this.ephemeral;
|
|
||||||
}
|
|
||||||
|
|
||||||
setEphemeral(ephemeral) {
|
|
||||||
this.ephemeral = ephemeral;
|
|
||||||
if(ephemeral) {
|
|
||||||
this.storageManager.setModelStorageMode(StorageManager.Ephemeral);
|
|
||||||
this.storageManager.setItemsMode(StorageManager.Ephemeral);
|
|
||||||
} else {
|
|
||||||
this.storageManager.setModelStorageMode(StorageManager.Fixed);
|
|
||||||
this.storageManager.setItemsMode(this.storageManager.bestStorageMode());
|
|
||||||
this.storageManager.setItem("ephemeral", JSON.stringify(false), StorageManager.Fixed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAuthParamsForEmail(url, email, extraParams) {
|
|
||||||
return super.getAuthParamsForEmail(url, email, extraParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
async login(url, email, password, ephemeral, strictSignin, extraParams) {
|
|
||||||
return super.login(url, email, password, strictSignin, extraParams).then((response) => {
|
|
||||||
if(!response.error) {
|
|
||||||
this.setEphemeral(ephemeral);
|
|
||||||
this.checkForSecurityUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async register(url, email, password, ephemeral) {
|
|
||||||
return super.register(url, email, password).then((response) => {
|
|
||||||
if(!response.error) {
|
|
||||||
this.setEphemeral(ephemeral);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async changePassword(url, email, current_server_pw, newKeys, newAuthParams) {
|
|
||||||
return super.changePassword(url, email, current_server_pw, newKeys, newAuthParams).then((response) => {
|
|
||||||
if(!response.error) {
|
|
||||||
this.checkForSecurityUpdate();
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleAuthResponse(response, email, url, authParams, keys) {
|
|
||||||
try {
|
|
||||||
await super.handleAuthResponse(response, email, url, authParams, keys);
|
|
||||||
this.user = response.user;
|
|
||||||
this.storageManager.setItem("user", JSON.stringify(response.user));
|
|
||||||
} catch (e) {
|
|
||||||
this.dbManager.displayOfflineAlert();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async verifyAccountPassword(password) {
|
|
||||||
const authParams = await this.getAuthParams();
|
|
||||||
const keys = await protocolManager.computeEncryptionKeysForUser(password, authParams);
|
|
||||||
const success = keys.mk === (await this.keys()).mk;
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkForSecurityUpdate() {
|
|
||||||
if(this.offline()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const latest = protocolManager.version();
|
|
||||||
const updateAvailable = await this.protocolVersion() !== latest;
|
|
||||||
if(updateAvailable !== this.securityUpdateAvailable) {
|
|
||||||
this.securityUpdateAvailable = updateAvailable;
|
|
||||||
this.$rootScope.$broadcast("security-update-status-changed");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.securityUpdateAvailable;
|
|
||||||
}
|
|
||||||
|
|
||||||
presentPasswordWizard(type) {
|
|
||||||
var scope = this.$rootScope.$new(true);
|
|
||||||
scope.type = type;
|
|
||||||
var el = this.$compile( "<password-wizard type='type'></password-wizard>" )(scope);
|
|
||||||
angular.element(document.body).append(el);
|
|
||||||
}
|
|
||||||
|
|
||||||
signOut() {
|
|
||||||
super.signout();
|
|
||||||
this.user = null;
|
|
||||||
this._authParams = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import angular from 'angular';
|
|
||||||
import { SNComponentManager, SFAlertManager } from 'snjs';
|
|
||||||
import { isDesktopApplication, getPlatformString } from '@/utils';
|
|
||||||
|
|
||||||
export class ComponentManager extends SNComponentManager {
|
|
||||||
/* @ngInject */
|
|
||||||
constructor(
|
|
||||||
modelManager,
|
|
||||||
syncManager,
|
|
||||||
desktopManager,
|
|
||||||
nativeExtManager,
|
|
||||||
$rootScope,
|
|
||||||
$timeout,
|
|
||||||
$compile
|
|
||||||
) {
|
|
||||||
super({
|
|
||||||
modelManager,
|
|
||||||
syncManager,
|
|
||||||
desktopManager,
|
|
||||||
nativeExtManager,
|
|
||||||
alertManager: new SFAlertManager(),
|
|
||||||
$uiRunner: $rootScope.safeApply,
|
|
||||||
$timeout: $timeout,
|
|
||||||
environment: isDesktopApplication() ? "desktop" : "web",
|
|
||||||
platform: getPlatformString()
|
|
||||||
});
|
|
||||||
|
|
||||||
// this.loggingEnabled = true;
|
|
||||||
|
|
||||||
this.$compile = $compile;
|
|
||||||
this.$rootScope = $rootScope;
|
|
||||||
}
|
|
||||||
|
|
||||||
openModalComponent(component) {
|
|
||||||
var scope = this.$rootScope.$new(true);
|
|
||||||
scope.component = component;
|
|
||||||
var el = this.$compile( "<component-modal component='component' class='sk-modal'></component-modal>" )(scope);
|
|
||||||
angular.element(document.body).append(el);
|
|
||||||
}
|
|
||||||
|
|
||||||
presentPermissionsDialog(dialog) {
|
|
||||||
const scope = this.$rootScope.$new(true);
|
|
||||||
scope.permissionsString = dialog.permissionsString;
|
|
||||||
scope.component = dialog.component;
|
|
||||||
scope.callback = dialog.callback;
|
|
||||||
|
|
||||||
var el = this.$compile( "<permissions-modal component='component' permissions-string='permissionsString' callback='callback' class='sk-modal'></permissions-modal>" )(scope);
|
|
||||||
angular.element(document.body).append(el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export class DBManager {
|
export class DatabaseManager {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(alertManager) {
|
constructor(alertManager) {
|
||||||
this.locked = true;
|
this.locked = true;
|
||||||
@@ -15,10 +15,10 @@ export class DesktopManager {
|
|||||||
modelManager,
|
modelManager,
|
||||||
syncManager,
|
syncManager,
|
||||||
authManager,
|
authManager,
|
||||||
passcodeManager,
|
lockManager,
|
||||||
appState
|
appState
|
||||||
) {
|
) {
|
||||||
this.passcodeManager = passcodeManager;
|
this.lockManager = lockManager;
|
||||||
this.modelManager = modelManager;
|
this.modelManager = modelManager;
|
||||||
this.authManager = authManager;
|
this.authManager = authManager;
|
||||||
this.syncManager = syncManager;
|
this.syncManager = syncManager;
|
||||||
@@ -202,9 +202,9 @@ export class DesktopManager {
|
|||||||
|
|
||||||
async desktop_requestBackupFile(callback) {
|
async desktop_requestBackupFile(callback) {
|
||||||
let keys, authParams;
|
let keys, authParams;
|
||||||
if(this.authManager.offline() && this.passcodeManager.hasPasscode()) {
|
if(this.authManager.offline() && this.lockManager.hasPasscode()) {
|
||||||
keys = this.passcodeManager.keys();
|
keys = this.lockManager.keys();
|
||||||
authParams = this.passcodeManager.passcodeAuthParams();
|
authParams = this.lockManager.passcodeAuthParams();
|
||||||
} else {
|
} else {
|
||||||
keys = await this.authManager.keys();
|
keys = await this.authManager.keys();
|
||||||
authParams = await this.authManager.getAuthParams();
|
authParams = await this.authManager.getAuthParams();
|
||||||
|
|||||||
@@ -1,44 +1,36 @@
|
|||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import { SFPrivilegesManager } from 'snjs';
|
|
||||||
|
|
||||||
export class PrivilegesManager extends SFPrivilegesManager {
|
export class GodService {
|
||||||
|
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
passcodeManager,
|
|
||||||
authManager,
|
|
||||||
syncManager,
|
|
||||||
singletonManager,
|
|
||||||
modelManager,
|
|
||||||
storageManager,
|
|
||||||
$rootScope,
|
$rootScope,
|
||||||
$compile
|
$compile
|
||||||
) {
|
) {
|
||||||
super(modelManager, syncManager, singletonManager);
|
|
||||||
|
|
||||||
this.$rootScope = $rootScope;
|
this.$rootScope = $rootScope;
|
||||||
this.$compile = $compile;
|
this.$compile = $compile;
|
||||||
|
}
|
||||||
|
|
||||||
this.setDelegate({
|
async checkForSecurityUpdate() {
|
||||||
isOffline: async () => {
|
if (this.offline()) {
|
||||||
return authManager.offline();
|
return false;
|
||||||
},
|
}
|
||||||
hasLocalPasscode: async () => {
|
|
||||||
return passcodeManager.hasPasscode();
|
const latest = protocolManager.version();
|
||||||
},
|
const updateAvailable = await this.protocolVersion() !== latest;
|
||||||
saveToStorage: async (key, value) => {
|
if (updateAvailable !== this.securityUpdateAvailable) {
|
||||||
return storageManager.setItem(key, value, storageManager.bestStorageMode());
|
this.securityUpdateAvailable = updateAvailable;
|
||||||
},
|
this.$rootScope.$broadcast("security-update-status-changed");
|
||||||
getFromStorage: async (key) => {
|
}
|
||||||
return storageManager.getItem(key, storageManager.bestStorageMode());
|
|
||||||
},
|
return this.securityUpdateAvailable;
|
||||||
verifyAccountPassword: async (password) => {
|
}
|
||||||
return authManager.verifyAccountPassword(password);
|
|
||||||
},
|
presentPasswordWizard(type) {
|
||||||
verifyLocalPasscode: async (passcode) => {
|
var scope = this.$rootScope.$new(true);
|
||||||
return passcodeManager.verifyPasscode(passcode);
|
scope.type = type;
|
||||||
},
|
var el = this.$compile("<password-wizard type='type'></password-wizard>")(scope);
|
||||||
});
|
angular.element(document.body).append(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
async presentPrivilegesModal(action, onSuccess, onCancel) {
|
async presentPrivilegesModal(action, onSuccess, onCancel) {
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { SFHttpManager } from 'snjs';
|
|
||||||
|
|
||||||
export class HttpManager extends SFHttpManager {
|
|
||||||
/* @ngInject */
|
|
||||||
constructor(storageManager, $timeout) {
|
|
||||||
// calling callbacks in a $timeout allows UI to update
|
|
||||||
super($timeout);
|
|
||||||
|
|
||||||
this.setJWTRequestHandler(async () => {
|
|
||||||
return storageManager.getItem('jwt');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,14 +2,14 @@ export { ActionsManager } from './actionsManager';
|
|||||||
export { ArchiveManager } from './archiveManager';
|
export { ArchiveManager } from './archiveManager';
|
||||||
export { AuthManager } from './authManager';
|
export { AuthManager } from './authManager';
|
||||||
export { ComponentManager } from './componentManager';
|
export { ComponentManager } from './componentManager';
|
||||||
export { DBManager } from './dbManager';
|
export { DatabaseManager } from './databaseManager';
|
||||||
export { DesktopManager } from './desktopManager';
|
export { DesktopManager } from './desktopManager';
|
||||||
export { HttpManager } from './httpManager';
|
export { HttpManager } from './httpManager';
|
||||||
export { KeyboardManager } from './keyboardManager';
|
export { KeyboardManager } from './keyboardManager';
|
||||||
export { MigrationManager } from './migrationManager';
|
export { MigrationManager } from './migrationManager';
|
||||||
export { ModelManager } from './modelManager';
|
export { ModelManager } from './modelManager';
|
||||||
export { NativeExtManager } from './nativeExtManager';
|
export { NativeExtManager } from './nativeExtManager';
|
||||||
export { PasscodeManager } from './passcodeManager';
|
export { LockManager } from './lockManager';
|
||||||
export { PrivilegesManager } from './privilegesManager';
|
export { PrivilegesManager } from './privilegesManager';
|
||||||
export { SessionHistory } from './sessionHistory';
|
export { SessionHistory } from './sessionHistory';
|
||||||
export { SingletonManager } from './singletonManager';
|
export { SingletonManager } from './singletonManager';
|
||||||
|
|||||||
143
app/assets/javascripts/services/lockManager.js
Normal file
143
app/assets/javascripts/services/lockManager.js
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import { isDesktopApplication } from '@/utils';
|
||||||
|
import {
|
||||||
|
APP_STATE_EVENT_WINDOW_DID_BLUR,
|
||||||
|
APP_STATE_EVENT_WINDOW_DID_FOCUS
|
||||||
|
} from '../state';
|
||||||
|
|
||||||
|
const MILLISECONDS_PER_SECOND = 1000;
|
||||||
|
const FOCUS_POLL_INTERVAL = 1 * MILLISECONDS_PER_SECOND;
|
||||||
|
const LOCK_INTERVAL_NONE = 0;
|
||||||
|
const LOCK_INTERVAL_IMMEDIATE = 1;
|
||||||
|
const LOCK_INTERVAL_ONE_MINUTE = 60 * MILLISECONDS_PER_SECOND;
|
||||||
|
const LOCK_INTERVAL_FIVE_MINUTES = 300 * MILLISECONDS_PER_SECOND;
|
||||||
|
const LOCK_INTERVAL_ONE_HOUR= 3600 * MILLISECONDS_PER_SECOND;
|
||||||
|
|
||||||
|
const STORAGE_KEY_AUTOLOCK_INTERVAL = "AutoLockIntervalKey";
|
||||||
|
|
||||||
|
export class LockManager {
|
||||||
|
/* @ngInject */
|
||||||
|
constructor($rootScope, application, appState) {
|
||||||
|
this.$rootScope = $rootScope;
|
||||||
|
this.application = application;
|
||||||
|
this.appState = appState;
|
||||||
|
this.observeVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
observeVisibility() {
|
||||||
|
this.appState.addObserver((eventName, data) => {
|
||||||
|
if(eventName === APP_STATE_EVENT_WINDOW_DID_BLUR) {
|
||||||
|
this.documentVisibilityChanged(false);
|
||||||
|
} else if(eventName === APP_STATE_EVENT_WINDOW_DID_FOCUS) {
|
||||||
|
this.documentVisibilityChanged(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!isDesktopApplication()) {
|
||||||
|
this.beginWebFocusPolling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setAutoLockInterval(interval) {
|
||||||
|
return this.application.setValue(
|
||||||
|
STORAGE_KEY_AUTOLOCK_INTERVAL,
|
||||||
|
JSON.stringify(interval),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAutoLockInterval() {
|
||||||
|
const interval = await this.application.getValue(
|
||||||
|
STORAGE_KEY_AUTOLOCK_INTERVAL,
|
||||||
|
);
|
||||||
|
if(interval) {
|
||||||
|
return JSON.parse(interval);
|
||||||
|
} else {
|
||||||
|
return LOCK_INTERVAL_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify document is in focus every so often as visibilitychange event is
|
||||||
|
* not triggered on a typical window blur event but rather on tab changes.
|
||||||
|
*/
|
||||||
|
beginWebFocusPolling() {
|
||||||
|
this.pollFocusTimeout = setInterval(() => {
|
||||||
|
const hasFocus = document.hasFocus();
|
||||||
|
if(hasFocus && this.lastFocusState === 'hidden') {
|
||||||
|
this.documentVisibilityChanged(true);
|
||||||
|
} else if(!hasFocus && this.lastFocusState === 'visible') {
|
||||||
|
this.documentVisibilityChanged(false);
|
||||||
|
}
|
||||||
|
/* Save this to compare against next time around */
|
||||||
|
this.lastFocusState = hasFocus ? 'visible' : 'hidden';
|
||||||
|
}, FOCUS_POLL_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAutoLockIntervalOptions() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
value: LOCK_INTERVAL_NONE,
|
||||||
|
label: "Off"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LOCK_INTERVAL_IMMEDIATE,
|
||||||
|
label: "Immediately"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LOCK_INTERVAL_ONE_MINUTE,
|
||||||
|
label: "1m"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LOCK_INTERVAL_FIVE_MINUTES,
|
||||||
|
label: "5m"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: LOCK_INTERVAL_ONE_HOUR,
|
||||||
|
label: "1h"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
documentVisibilityChanged(visible) {
|
||||||
|
if(visible) {
|
||||||
|
if(
|
||||||
|
!this.isLocked() &&
|
||||||
|
this.lockAfterDate &&
|
||||||
|
new Date() > this.lockAfterDate
|
||||||
|
) {
|
||||||
|
this.application.passcodeLock();
|
||||||
|
}
|
||||||
|
this.cancelAutoLockTimer();
|
||||||
|
} else {
|
||||||
|
this.beginAutoLockTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async beginAutoLockTimer() {
|
||||||
|
var interval = await this.getAutoLockInterval();
|
||||||
|
if(interval === LOCK_INTERVAL_NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Use a timeout if possible, but if the computer is put to sleep, timeouts won't
|
||||||
|
* work. Need to set a date as backup. this.lockAfterDate does not need to be
|
||||||
|
* persisted, as living in memory is sufficient. If memory is cleared, then the
|
||||||
|
* application will lock anyway.
|
||||||
|
*/
|
||||||
|
const addToNow = (seconds) => {
|
||||||
|
const date = new Date();
|
||||||
|
date.setSeconds(date.getSeconds() + seconds);
|
||||||
|
return date;
|
||||||
|
};
|
||||||
|
this.lockAfterDate = addToNow(interval / MILLISECONDS_PER_SECOND);
|
||||||
|
this.lockTimeout = setTimeout(() => {
|
||||||
|
this.cancelAutoLockTimer();
|
||||||
|
this.application.passcodeLock();
|
||||||
|
this.lockAfterDate = null;
|
||||||
|
}, interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelAutoLockTimer() {
|
||||||
|
clearTimeout(this.lockTimeout);
|
||||||
|
this.lockAfterDate = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
import { isDesktopApplication } from '@/utils';
|
|
||||||
import { SFMigrationManager } from 'snjs';
|
|
||||||
import { ComponentManager } from '@/services/componentManager';
|
|
||||||
|
|
||||||
export class MigrationManager extends SFMigrationManager {
|
|
||||||
|
|
||||||
/* @ngInject */
|
|
||||||
constructor(
|
|
||||||
modelManager,
|
|
||||||
syncManager,
|
|
||||||
componentManager,
|
|
||||||
storageManager,
|
|
||||||
statusManager,
|
|
||||||
authManager,
|
|
||||||
desktopManager
|
|
||||||
) {
|
|
||||||
super(modelManager, syncManager, storageManager, authManager);
|
|
||||||
this.componentManager = componentManager;
|
|
||||||
this.statusManager = statusManager;
|
|
||||||
this.desktopManager = desktopManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
registeredMigrations() {
|
|
||||||
return [
|
|
||||||
this.editorToComponentMigration(),
|
|
||||||
this.componentUrlToHostedUrl(),
|
|
||||||
this.removeTagReferencesFromNotes()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Migrate SN|Editor to SN|Component. Editors are deprecated as of November 2017. Editors using old APIs must
|
|
||||||
convert to using the new component API.
|
|
||||||
*/
|
|
||||||
|
|
||||||
editorToComponentMigration() {
|
|
||||||
return {
|
|
||||||
name: "editor-to-component",
|
|
||||||
content_type: "SN|Editor",
|
|
||||||
handler: async (editors) => {
|
|
||||||
// Convert editors to components
|
|
||||||
for(var editor of editors) {
|
|
||||||
// If there's already a component for this url, then skip this editor
|
|
||||||
if(editor.url && !this.componentManager.componentForUrl(editor.url)) {
|
|
||||||
var component = this.modelManager.createItem({
|
|
||||||
content_type: "SN|Component",
|
|
||||||
content: {
|
|
||||||
url: editor.url,
|
|
||||||
name: editor.name,
|
|
||||||
area: "editor-editor"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
component.setAppDataItem("data", editor.data);
|
|
||||||
this.modelManager.addItem(component);
|
|
||||||
this.modelManager.setItemDirty(component, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const editor of editors) {
|
|
||||||
this.modelManager.setItemToBeDeleted(editor);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.syncManager.sync();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Migrate component.url fields to component.hosted_url. This involves rewriting any note data that relied on the
|
|
||||||
component.url value to store clientData, such as the CodeEditor, which stores the programming language for the note
|
|
||||||
in the note's clientData[component.url]. We want to rewrite any matching items to transfer that clientData into
|
|
||||||
clientData[component.uuid].
|
|
||||||
|
|
||||||
April 3, 2019 note: it seems this migration is mis-named. The first part of the description doesn't match what the code is actually doing.
|
|
||||||
It has nothing to do with url/hosted_url relationship and more to do with just mapping client data from the note's hosted_url to its uuid
|
|
||||||
|
|
||||||
Created: July 6, 2018
|
|
||||||
*/
|
|
||||||
componentUrlToHostedUrl() {
|
|
||||||
return {
|
|
||||||
name: "component-url-to-hosted-url",
|
|
||||||
content_type: "SN|Component",
|
|
||||||
handler: async (components) => {
|
|
||||||
let hasChanges = false;
|
|
||||||
const notes = this.modelManager.validItemsForContentType("Note");
|
|
||||||
for(const note of notes) {
|
|
||||||
for(const component of components) {
|
|
||||||
const clientData = note.getDomainDataItem(component.hosted_url, ComponentManager.ClientDataDomain);
|
|
||||||
if(clientData) {
|
|
||||||
note.setDomainDataItem(component.uuid, clientData, ComponentManager.ClientDataDomain);
|
|
||||||
note.setDomainDataItem(component.hosted_url, null, ComponentManager.ClientDataDomain);
|
|
||||||
this.modelManager.setItemDirty(note, true);
|
|
||||||
hasChanges = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(hasChanges) {
|
|
||||||
this.syncManager.sync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Migrate notes which have relationships on tags to migrate those relationships to the tags themselves.
|
|
||||||
That is, notes.content.references should not include any mention of tags.
|
|
||||||
This will apply to notes created before the schema change. Now, only tags reference notes.
|
|
||||||
Created: April 3, 2019
|
|
||||||
*/
|
|
||||||
removeTagReferencesFromNotes() {
|
|
||||||
return {
|
|
||||||
name: "remove-tag-references-from-notes",
|
|
||||||
content_type: "Note",
|
|
||||||
handler: async (notes) => {
|
|
||||||
|
|
||||||
const needsSync = false;
|
|
||||||
let status = this.statusManager.addStatusFromString("Optimizing data...");
|
|
||||||
let dirtyCount = 0;
|
|
||||||
|
|
||||||
for(const note of notes) {
|
|
||||||
if(!note.content) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const references = note.content.references;
|
|
||||||
// Remove any tag references, and transfer them to the tag if neccessary.
|
|
||||||
const newReferences = [];
|
|
||||||
|
|
||||||
for(const reference of references) {
|
|
||||||
if(reference.content_type != "Tag") {
|
|
||||||
newReferences.push(reference);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// is Tag content_type, we will not be adding this to newReferences
|
|
||||||
const tag = this.modelManager.findItem(reference.uuid);
|
|
||||||
if(tag && !tag.hasRelationshipWithItem(note)) {
|
|
||||||
tag.addItemAsRelationship(note);
|
|
||||||
this.modelManager.setItemDirty(tag, true);
|
|
||||||
dirtyCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(newReferences.length != references.length) {
|
|
||||||
note.content.references = newReferences;
|
|
||||||
this.modelManager.setItemDirty(note, true);
|
|
||||||
dirtyCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(dirtyCount > 0) {
|
|
||||||
if(isDesktopApplication()) {
|
|
||||||
this.desktopManager.saveBackup();
|
|
||||||
}
|
|
||||||
|
|
||||||
status = this.statusManager.replaceStatusWithString(status, `${dirtyCount} items optimized.`);
|
|
||||||
await this.syncManager.sync();
|
|
||||||
|
|
||||||
status = this.statusManager.replaceStatusWithString(status, `Optimization complete.`);
|
|
||||||
setTimeout(() => {
|
|
||||||
this.statusManager.removeStatus(status);
|
|
||||||
}, 2000);
|
|
||||||
} else {
|
|
||||||
this.statusManager.removeStatus(status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { SFModelManager, SNSmartTag, SFPredicate } from 'snjs';
|
|
||||||
|
|
||||||
export class ModelManager extends SFModelManager {
|
|
||||||
/* @ngInject */
|
|
||||||
constructor(storageManager, $timeout) {
|
|
||||||
super($timeout);
|
|
||||||
this.notes = [];
|
|
||||||
this.tags = [];
|
|
||||||
this.components = [];
|
|
||||||
|
|
||||||
this.storageManager = storageManager;
|
|
||||||
|
|
||||||
this.buildSystemSmartTags();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSignout() {
|
|
||||||
super.handleSignout();
|
|
||||||
this.notes.length = 0;
|
|
||||||
this.tags.length = 0;
|
|
||||||
this.components.length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
noteCount() {
|
|
||||||
return this.notes.filter((n) => !n.dummy).length;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeAllItemsFromMemory() {
|
|
||||||
for(var item of this.items) {
|
|
||||||
item.deleted = true;
|
|
||||||
}
|
|
||||||
this.notifySyncObserversOfModels(this.items);
|
|
||||||
this.handleSignout();
|
|
||||||
}
|
|
||||||
|
|
||||||
findTag(title) {
|
|
||||||
return _.find(this.tags, { title: title });
|
|
||||||
}
|
|
||||||
|
|
||||||
findOrCreateTagByTitle(title) {
|
|
||||||
let tag = this.findTag(title);
|
|
||||||
if(!tag) {
|
|
||||||
tag = this.createItem({content_type: "Tag", content: {title: title}});
|
|
||||||
this.addItem(tag);
|
|
||||||
this.setItemDirty(tag, true);
|
|
||||||
}
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
addItems(items, globalOnly = false) {
|
|
||||||
super.addItems(items, globalOnly);
|
|
||||||
|
|
||||||
items.forEach((item) => {
|
|
||||||
// In some cases, you just want to add the item to this.items, and not to the individual arrays
|
|
||||||
// This applies when you want to keep an item syncable, but not display it via the individual arrays
|
|
||||||
if(!globalOnly) {
|
|
||||||
if(item.content_type == "Tag") {
|
|
||||||
if(!_.find(this.tags, {uuid: item.uuid})) {
|
|
||||||
this.tags.splice(_.sortedIndexBy(this.tags, item, function(item){
|
|
||||||
if (item.title) return item.title.toLowerCase();
|
|
||||||
else return '';
|
|
||||||
}), 0, item);
|
|
||||||
}
|
|
||||||
} else if(item.content_type == "Note") {
|
|
||||||
if(!_.find(this.notes, {uuid: item.uuid})) {
|
|
||||||
this.notes.unshift(item);
|
|
||||||
}
|
|
||||||
} else if(item.content_type == "SN|Component") {
|
|
||||||
if(!_.find(this.components, {uuid: item.uuid})) {
|
|
||||||
this.components.unshift(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
resortTag(tag) {
|
|
||||||
_.pull(this.tags, tag);
|
|
||||||
this.tags.splice(_.sortedIndexBy(this.tags, tag, function(tag){
|
|
||||||
if (tag.title) return tag.title.toLowerCase();
|
|
||||||
else return '';
|
|
||||||
}), 0, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
setItemToBeDeleted(item) {
|
|
||||||
super.setItemToBeDeleted(item);
|
|
||||||
|
|
||||||
// remove from relevant array, but don't remove from all items.
|
|
||||||
// This way, it's removed from the display, but still synced via get dirty items
|
|
||||||
this.removeItemFromRespectiveArray(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeItemLocally(item, callback) {
|
|
||||||
super.removeItemLocally(item, callback);
|
|
||||||
this.removeItemFromRespectiveArray(item);
|
|
||||||
this.storageManager.deleteModel(item).then(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeItemFromRespectiveArray(item) {
|
|
||||||
if(item.content_type == "Tag") {
|
|
||||||
_.remove(this.tags, {uuid: item.uuid});
|
|
||||||
} else if(item.content_type == "Note") {
|
|
||||||
_.remove(this.notes, {uuid: item.uuid});
|
|
||||||
} else if(item.content_type == "SN|Component") {
|
|
||||||
_.remove(this.components, {uuid: item.uuid});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notesMatchingSmartTag(tag) {
|
|
||||||
const contentTypePredicate = new SFPredicate("content_type", "=", "Note");
|
|
||||||
const predicates = [contentTypePredicate, tag.content.predicate];
|
|
||||||
if(!tag.content.isTrashTag) {
|
|
||||||
const notTrashedPredicate = new SFPredicate("content.trashed", "=", false);
|
|
||||||
predicates.push(notTrashedPredicate);
|
|
||||||
}
|
|
||||||
const results = this.itemsMatchingPredicates(predicates);
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
trashSmartTag() {
|
|
||||||
return this.systemSmartTags.find((tag) => tag.content.isTrashTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
trashedItems() {
|
|
||||||
return this.notesMatchingSmartTag(this.trashSmartTag());
|
|
||||||
}
|
|
||||||
|
|
||||||
emptyTrash() {
|
|
||||||
const notes = this.trashedItems();
|
|
||||||
for(const note of notes) {
|
|
||||||
this.setItemToBeDeleted(note);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildSystemSmartTags() {
|
|
||||||
this.systemSmartTags = SNSmartTag.systemSmartTags();
|
|
||||||
}
|
|
||||||
|
|
||||||
getSmartTagWithId(id) {
|
|
||||||
return this.getSmartTags().find((candidate) => candidate.uuid == id);
|
|
||||||
}
|
|
||||||
|
|
||||||
getSmartTags() {
|
|
||||||
const userTags = this.validItemsForContentType("SN|SmartTag").sort((a, b) => {
|
|
||||||
return a.content.title < b.content.title ? -1 : 1;
|
|
||||||
});
|
|
||||||
return this.systemSmartTags.concat(userTags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Misc
|
|
||||||
*/
|
|
||||||
|
|
||||||
humanReadableDisplayForContentType(contentType) {
|
|
||||||
return {
|
|
||||||
"Note" : "note",
|
|
||||||
"Tag" : "tag",
|
|
||||||
"SN|SmartTag": "smart tag",
|
|
||||||
"Extension" : "action-based extension",
|
|
||||||
"SN|Component" : "component",
|
|
||||||
"SN|Editor" : "editor",
|
|
||||||
"SN|Theme" : "theme",
|
|
||||||
"SF|Extension" : "server extension",
|
|
||||||
"SF|MFA" : "two-factor authentication setting",
|
|
||||||
"SN|FileSafe|Credentials": "FileSafe credential",
|
|
||||||
"SN|FileSafe|FileMetadata": "FileSafe file",
|
|
||||||
"SN|FileSafe|Integration": "FileSafe integration"
|
|
||||||
}[contentType];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,285 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { isDesktopApplication } from '@/utils';
|
|
||||||
import { StorageManager } from './storageManager';
|
|
||||||
import { protocolManager } from 'snjs';
|
|
||||||
|
|
||||||
const MillisecondsPerSecond = 1000;
|
|
||||||
|
|
||||||
export class PasscodeManager {
|
|
||||||
/* @ngInject */
|
|
||||||
constructor($rootScope, authManager, storageManager, syncManager) {
|
|
||||||
this.authManager = authManager;
|
|
||||||
this.storageManager = storageManager;
|
|
||||||
this.syncManager = syncManager;
|
|
||||||
this.$rootScope = $rootScope;
|
|
||||||
|
|
||||||
this._hasPasscode = this.storageManager.getItemSync("offlineParams", StorageManager.Fixed) != null;
|
|
||||||
this._locked = this._hasPasscode;
|
|
||||||
|
|
||||||
this.visibilityObservers = [];
|
|
||||||
this.passcodeChangeObservers = [];
|
|
||||||
|
|
||||||
this.configureAutoLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
addPasscodeChangeObserver(callback) {
|
|
||||||
this.passcodeChangeObservers.push(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lockApplication() {
|
|
||||||
window.location.reload();
|
|
||||||
this.cancelAutoLockTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
isLocked() {
|
|
||||||
return this._locked;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPasscode() {
|
|
||||||
return this._hasPasscode;
|
|
||||||
}
|
|
||||||
|
|
||||||
keys() {
|
|
||||||
return this._keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
addVisibilityObserver(callback) {
|
|
||||||
this.visibilityObservers.push(callback);
|
|
||||||
return callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeVisibilityObserver(callback) {
|
|
||||||
_.pull(this.visibilityObservers, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
notifiyVisibilityObservers(visible) {
|
|
||||||
for(const callback of this.visibilityObservers) {
|
|
||||||
callback(visible);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async setAutoLockInterval(interval) {
|
|
||||||
return this.storageManager.setItem(PasscodeManager.AutoLockIntervalKey, JSON.stringify(interval), StorageManager.FixedEncrypted);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAutoLockInterval() {
|
|
||||||
const interval = await this.storageManager.getItem(PasscodeManager.AutoLockIntervalKey, StorageManager.FixedEncrypted);
|
|
||||||
if(interval) {
|
|
||||||
return JSON.parse(interval);
|
|
||||||
} else {
|
|
||||||
return PasscodeManager.AutoLockIntervalNone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
passcodeAuthParams() {
|
|
||||||
var authParams = JSON.parse(this.storageManager.getItemSync("offlineParams", StorageManager.Fixed));
|
|
||||||
if(authParams && !authParams.version) {
|
|
||||||
var keys = this.keys();
|
|
||||||
if(keys && keys.ak) {
|
|
||||||
// If there's no version stored, and there's an ak, it has to be 002. Newer versions would have their version stored in authParams.
|
|
||||||
authParams.version = "002";
|
|
||||||
} else {
|
|
||||||
authParams.version = "001";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return authParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
async verifyPasscode(passcode) {
|
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
var params = this.passcodeAuthParams();
|
|
||||||
const keys = await protocolManager.computeEncryptionKeysForUser(passcode, params);
|
|
||||||
if(keys.pw !== params.hash) {
|
|
||||||
resolve(false);
|
|
||||||
} else {
|
|
||||||
resolve(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
unlock(passcode, callback) {
|
|
||||||
var params = this.passcodeAuthParams();
|
|
||||||
protocolManager.computeEncryptionKeysForUser(passcode, params).then((keys) => {
|
|
||||||
if(keys.pw !== params.hash) {
|
|
||||||
callback(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._keys = keys;
|
|
||||||
this._authParams = params;
|
|
||||||
this.decryptLocalStorage(keys, params).then(() => {
|
|
||||||
this._locked = false;
|
|
||||||
callback(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setPasscode(passcode, callback) {
|
|
||||||
var uuid = protocolManager.crypto.generateUUIDSync();
|
|
||||||
|
|
||||||
protocolManager.generateInitialKeysAndAuthParamsForUser(uuid, passcode).then((results) => {
|
|
||||||
const keys = results.keys;
|
|
||||||
const authParams = results.authParams;
|
|
||||||
|
|
||||||
authParams.hash = keys.pw;
|
|
||||||
this._keys = keys;
|
|
||||||
this._hasPasscode = true;
|
|
||||||
this._authParams = authParams;
|
|
||||||
|
|
||||||
// Encrypting will initially clear localStorage
|
|
||||||
this.encryptLocalStorage(keys, authParams);
|
|
||||||
|
|
||||||
// After it's cleared, it's safe to write to it
|
|
||||||
this.storageManager.setItem("offlineParams", JSON.stringify(authParams), StorageManager.Fixed);
|
|
||||||
callback(true);
|
|
||||||
|
|
||||||
this.notifyObserversOfPasscodeChange();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
changePasscode(newPasscode, callback) {
|
|
||||||
this.setPasscode(newPasscode, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
clearPasscode() {
|
|
||||||
this.storageManager.setItemsMode(this.authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.Fixed); // Transfer from Ephemeral
|
|
||||||
this.storageManager.removeItem("offlineParams", StorageManager.Fixed);
|
|
||||||
this._keys = null;
|
|
||||||
this._hasPasscode = false;
|
|
||||||
|
|
||||||
this.notifyObserversOfPasscodeChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyObserversOfPasscodeChange() {
|
|
||||||
for(var observer of this.passcodeChangeObservers) {
|
|
||||||
observer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptLocalStorage(keys, authParams) {
|
|
||||||
this.storageManager.setKeys(keys, authParams);
|
|
||||||
// Switch to Ephemeral storage, wiping Fixed storage
|
|
||||||
// Last argument is `force`, which we set to true because in the case of changing passcode
|
|
||||||
this.storageManager.setItemsMode(this.authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.FixedEncrypted, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
async decryptLocalStorage(keys, authParams) {
|
|
||||||
this.storageManager.setKeys(keys, authParams);
|
|
||||||
return this.storageManager.decryptStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
configureAutoLock() {
|
|
||||||
PasscodeManager.AutoLockPollFocusInterval = 1 * MillisecondsPerSecond;
|
|
||||||
|
|
||||||
PasscodeManager.AutoLockIntervalNone = 0;
|
|
||||||
PasscodeManager.AutoLockIntervalImmediate = 1;
|
|
||||||
PasscodeManager.AutoLockIntervalOneMinute = 60 * MillisecondsPerSecond;
|
|
||||||
PasscodeManager.AutoLockIntervalFiveMinutes = 300 * MillisecondsPerSecond;
|
|
||||||
PasscodeManager.AutoLockIntervalOneHour = 3600 * MillisecondsPerSecond;
|
|
||||||
|
|
||||||
PasscodeManager.AutoLockIntervalKey = "AutoLockIntervalKey";
|
|
||||||
|
|
||||||
if(isDesktopApplication()) {
|
|
||||||
// desktop only
|
|
||||||
this.$rootScope.$on("window-lost-focus", () => {
|
|
||||||
this.documentVisibilityChanged(false);
|
|
||||||
});
|
|
||||||
this.$rootScope.$on("window-gained-focus", () => {
|
|
||||||
this.documentVisibilityChanged(true);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// tab visibility listener, web only
|
|
||||||
document.addEventListener('visibilitychange', (e) => {
|
|
||||||
const visible = document.visibilityState === "visible";
|
|
||||||
this.documentVisibilityChanged(visible);
|
|
||||||
});
|
|
||||||
|
|
||||||
// verify document is in focus every so often as visibilitychange event is not triggered
|
|
||||||
// on a typical window blur event but rather on tab changes
|
|
||||||
this.pollFocusTimeout = setInterval(() => {
|
|
||||||
const hasFocus = document.hasFocus();
|
|
||||||
|
|
||||||
if(hasFocus && this.lastFocusState === "hidden") {
|
|
||||||
this.documentVisibilityChanged(true);
|
|
||||||
} else if(!hasFocus && this.lastFocusState === "visible") {
|
|
||||||
this.documentVisibilityChanged(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// save this to compare against next time around
|
|
||||||
this.lastFocusState = hasFocus ? "visible" : "hidden";
|
|
||||||
}, PasscodeManager.AutoLockPollFocusInterval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getAutoLockIntervalOptions() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
value: PasscodeManager.AutoLockIntervalNone,
|
|
||||||
label: "Off"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: PasscodeManager.AutoLockIntervalImmediate,
|
|
||||||
label: "Immediately"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: PasscodeManager.AutoLockIntervalOneMinute,
|
|
||||||
label: "1m"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: PasscodeManager.AutoLockIntervalFiveMinutes,
|
|
||||||
label: "5m"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: PasscodeManager.AutoLockIntervalOneHour,
|
|
||||||
label: "1h"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
documentVisibilityChanged(visible) {
|
|
||||||
if(visible) {
|
|
||||||
// check to see if lockAfterDate is not null, and if the application isn't locked.
|
|
||||||
// if that's the case, it needs to be locked immediately.
|
|
||||||
if(this.lockAfterDate && new Date() > this.lockAfterDate && !this.isLocked()) {
|
|
||||||
this.lockApplication();
|
|
||||||
} else {
|
|
||||||
if(!this.isLocked()) {
|
|
||||||
this.syncManager.sync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.cancelAutoLockTimer();
|
|
||||||
} else {
|
|
||||||
this.beginAutoLockTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.notifiyVisibilityObservers(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
async beginAutoLockTimer() {
|
|
||||||
var interval = await this.getAutoLockInterval();
|
|
||||||
if(interval == PasscodeManager.AutoLockIntervalNone) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use a timeout if possible, but if the computer is put to sleep, timeouts won't work.
|
|
||||||
// Need to set a date as backup. this.lockAfterDate does not need to be persisted, as
|
|
||||||
// living in memory seems sufficient. If memory is cleared, then the application will lock anyway.
|
|
||||||
const addToNow = (seconds) => {
|
|
||||||
const date = new Date();
|
|
||||||
date.setSeconds(date.getSeconds() + seconds);
|
|
||||||
return date;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.lockAfterDate = addToNow(interval / MillisecondsPerSecond);
|
|
||||||
this.lockTimeout = setTimeout(() => {
|
|
||||||
this.lockApplication();
|
|
||||||
// We don't need to look at this anymore since we've succeeded with timeout lock
|
|
||||||
this.lockAfterDate = null;
|
|
||||||
}, interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelAutoLockTimer() {
|
|
||||||
clearTimeout(this.lockTimeout);
|
|
||||||
this.lockAfterDate = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import { NoteHistoryEntry } from '@/models/noteHistoryEntry';
|
|
||||||
import { SFSessionHistoryManager , SFItemHistory } from 'snjs';
|
|
||||||
|
|
||||||
export class SessionHistory extends SFSessionHistoryManager {
|
|
||||||
/* @ngInject */
|
|
||||||
constructor(
|
|
||||||
modelManager,
|
|
||||||
storageManager,
|
|
||||||
authManager,
|
|
||||||
passcodeManager,
|
|
||||||
$timeout
|
|
||||||
) {
|
|
||||||
SFItemHistory.HistoryEntryClassMapping = {
|
|
||||||
"Note" : NoteHistoryEntry
|
|
||||||
};
|
|
||||||
|
|
||||||
// Session History can be encrypted with passcode keys. If it changes, we need to resave session
|
|
||||||
// history with the new keys.
|
|
||||||
passcodeManager.addPasscodeChangeObserver(() => {
|
|
||||||
this.saveToDisk();
|
|
||||||
});
|
|
||||||
|
|
||||||
var keyRequestHandler = async () => {
|
|
||||||
const offline = authManager.offline();
|
|
||||||
const auth_params = offline ? passcodeManager.passcodeAuthParams() : await authManager.getAuthParams();
|
|
||||||
const keys = offline ? passcodeManager.keys() : await authManager.keys();
|
|
||||||
|
|
||||||
return {
|
|
||||||
keys: keys,
|
|
||||||
offline: offline,
|
|
||||||
auth_params: auth_params
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var contentTypes = ["Note"];
|
|
||||||
super(
|
|
||||||
modelManager,
|
|
||||||
storageManager,
|
|
||||||
keyRequestHandler,
|
|
||||||
contentTypes,
|
|
||||||
$timeout
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { SFSingletonManager } from 'snjs';
|
|
||||||
|
|
||||||
export class SingletonManager extends SFSingletonManager {
|
|
||||||
// constructor needed for angularjs injection to work
|
|
||||||
// eslint-disable-next-line no-useless-constructor
|
|
||||||
/* @ngInject */
|
|
||||||
constructor(modelManager, syncManager) {
|
|
||||||
super(modelManager, syncManager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,241 +0,0 @@
|
|||||||
import { protocolManager, SNEncryptedStorage, SFStorageManager , SFItemParams } from 'snjs';
|
|
||||||
|
|
||||||
export class MemoryStorage {
|
|
||||||
constructor() {
|
|
||||||
this.memory = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
getItem(key) {
|
|
||||||
return this.memory[key] || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getItemSync(key) {
|
|
||||||
return this.getItem(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
get length() {
|
|
||||||
return Object.keys(this.memory).length;
|
|
||||||
}
|
|
||||||
|
|
||||||
setItem(key, value) {
|
|
||||||
this.memory[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeItem(key) {
|
|
||||||
delete this.memory[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this.memory = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
keys() {
|
|
||||||
return Object.keys(this.memory);
|
|
||||||
}
|
|
||||||
|
|
||||||
key(index) {
|
|
||||||
return Object.keys(this.memory)[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class StorageManager extends SFStorageManager {
|
|
||||||
|
|
||||||
/* @ngInject */
|
|
||||||
constructor(dbManager, alertManager) {
|
|
||||||
super();
|
|
||||||
this.dbManager = dbManager;
|
|
||||||
this.alertManager = alertManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize(hasPasscode, ephemeral) {
|
|
||||||
if(hasPasscode) {
|
|
||||||
// We don't want to save anything in fixed storage except for actual item data (in IndexedDB)
|
|
||||||
this.storage = this.memoryStorage;
|
|
||||||
this.itemsStorageMode = StorageManager.FixedEncrypted;
|
|
||||||
} else if(ephemeral) {
|
|
||||||
// We don't want to save anything in fixed storage as well as IndexedDB
|
|
||||||
this.storage = this.memoryStorage;
|
|
||||||
this.itemsStorageMode = StorageManager.Ephemeral;
|
|
||||||
} else {
|
|
||||||
this.storage = localStorage;
|
|
||||||
this.itemsStorageMode = StorageManager.Fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.modelStorageMode = ephemeral ? StorageManager.Ephemeral : StorageManager.Fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
get memoryStorage() {
|
|
||||||
if(!this._memoryStorage) {
|
|
||||||
this._memoryStorage = new MemoryStorage();
|
|
||||||
}
|
|
||||||
return this._memoryStorage;
|
|
||||||
}
|
|
||||||
|
|
||||||
setItemsMode(mode, force) {
|
|
||||||
var newStorage = this.getVault(mode);
|
|
||||||
if(newStorage !== this.storage || mode !== this.itemsStorageMode || force) {
|
|
||||||
// transfer storages
|
|
||||||
var length = this.storage.length;
|
|
||||||
for(var i = 0; i < length; i++) {
|
|
||||||
var key = this.storage.key(i);
|
|
||||||
newStorage.setItem(key, this.storage.getItem(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.itemsStorageMode = mode;
|
|
||||||
if(newStorage !== this.storage) {
|
|
||||||
// Only clear if this.storage isn't the same reference as newStorage
|
|
||||||
this.storage.clear();
|
|
||||||
}
|
|
||||||
this.storage = newStorage;
|
|
||||||
|
|
||||||
if(mode == StorageManager.FixedEncrypted) {
|
|
||||||
this.writeEncryptedStorageToDisk();
|
|
||||||
} else if(mode == StorageManager.Fixed) {
|
|
||||||
// Remove encrypted storage
|
|
||||||
this.removeItem("encryptedStorage", StorageManager.Fixed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getVault(vaultKey) {
|
|
||||||
if(vaultKey) {
|
|
||||||
if(vaultKey == StorageManager.Ephemeral || vaultKey == StorageManager.FixedEncrypted) {
|
|
||||||
return this.memoryStorage;
|
|
||||||
} else {
|
|
||||||
return localStorage;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return this.storage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async setItem(key, value, vaultKey) {
|
|
||||||
var storage = this.getVault(vaultKey);
|
|
||||||
try {
|
|
||||||
storage.setItem(key, value);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Exception while trying to setItem in StorageManager:", e);
|
|
||||||
this.alertManager.alert({text: "The application's local storage is out of space. If you have Session History save-to-disk enabled, please disable it, and try again."});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(vaultKey === StorageManager.FixedEncrypted || (!vaultKey && this.itemsStorageMode === StorageManager.FixedEncrypted)) {
|
|
||||||
return this.writeEncryptedStorageToDisk();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getItem(key, vault) {
|
|
||||||
return this.getItemSync(key, vault);
|
|
||||||
}
|
|
||||||
|
|
||||||
getItemSync(key, vault) {
|
|
||||||
var storage = this.getVault(vault);
|
|
||||||
return storage.getItem(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeItem(key, vault) {
|
|
||||||
var storage = this.getVault(vault);
|
|
||||||
return storage.removeItem(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
async clear() {
|
|
||||||
this.memoryStorage.clear();
|
|
||||||
localStorage.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
storageAsHash() {
|
|
||||||
var hash = {};
|
|
||||||
var length = this.storage.length;
|
|
||||||
for(var i = 0; i < length; i++) {
|
|
||||||
var key = this.storage.key(i);
|
|
||||||
hash[key] = this.storage.getItem(key);
|
|
||||||
}
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
setKeys(keys, authParams) {
|
|
||||||
this.encryptedStorageKeys = keys;
|
|
||||||
this.encryptedStorageAuthParams = authParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
async writeEncryptedStorageToDisk() {
|
|
||||||
var encryptedStorage = new SNEncryptedStorage();
|
|
||||||
// Copy over totality of current storage
|
|
||||||
encryptedStorage.content.storage = this.storageAsHash();
|
|
||||||
|
|
||||||
// Save new encrypted storage in Fixed storage
|
|
||||||
var params = new SFItemParams(encryptedStorage, this.encryptedStorageKeys, this.encryptedStorageAuthParams);
|
|
||||||
const syncParams = await params.paramsForSync();
|
|
||||||
this.setItem("encryptedStorage", JSON.stringify(syncParams), StorageManager.Fixed);
|
|
||||||
}
|
|
||||||
|
|
||||||
async decryptStorage() {
|
|
||||||
var stored = JSON.parse(this.getItemSync("encryptedStorage", StorageManager.Fixed));
|
|
||||||
await protocolManager.decryptItem(stored, this.encryptedStorageKeys);
|
|
||||||
var encryptedStorage = new SNEncryptedStorage(stored);
|
|
||||||
|
|
||||||
for(var key of Object.keys(encryptedStorage.content.storage)) {
|
|
||||||
this.setItem(key, encryptedStorage.storage[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPasscode() {
|
|
||||||
return this.getItemSync("encryptedStorage", StorageManager.Fixed) !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
bestStorageMode() {
|
|
||||||
return this.hasPasscode() ? StorageManager.FixedEncrypted : StorageManager.Fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Model Storage
|
|
||||||
|
|
||||||
If using ephemeral storage, we don't need to write it to anything as references will be held already by controllers
|
|
||||||
and the global modelManager service.
|
|
||||||
*/
|
|
||||||
|
|
||||||
setModelStorageMode(mode) {
|
|
||||||
if(mode == this.modelStorageMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mode == StorageManager.Ephemeral) {
|
|
||||||
// Clear IndexedDB
|
|
||||||
this.dbManager.clearAllModels(null);
|
|
||||||
} else {
|
|
||||||
// Fixed
|
|
||||||
}
|
|
||||||
|
|
||||||
this.modelStorageMode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAllModels() {
|
|
||||||
if(this.modelStorageMode == StorageManager.Fixed) {
|
|
||||||
return this.dbManager.getAllModels();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveModel(item) {
|
|
||||||
return this.saveModels([item]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveModels(items, onsuccess, onerror) {
|
|
||||||
if(this.modelStorageMode == StorageManager.Fixed) {
|
|
||||||
return this.dbManager.saveModels(items);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteModel(item) {
|
|
||||||
if(this.modelStorageMode == StorageManager.Fixed) {
|
|
||||||
return this.dbManager.deleteModel(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async clearAllModels() {
|
|
||||||
return this.dbManager.clearAllModels();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageManager.FixedEncrypted = "FixedEncrypted"; // encrypted memoryStorage + localStorage persistence
|
|
||||||
StorageManager.Ephemeral = "Ephemeral"; // memoryStorage
|
|
||||||
StorageManager.Fixed = "Fixed"; // localStorage
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import angular from 'angular';
|
|
||||||
import { SFSyncManager } from 'snjs';
|
|
||||||
|
|
||||||
export class SyncManager extends SFSyncManager {
|
|
||||||
/* @ngInject */
|
|
||||||
constructor(
|
|
||||||
modelManager,
|
|
||||||
storageManager,
|
|
||||||
httpManager,
|
|
||||||
$timeout,
|
|
||||||
$interval,
|
|
||||||
$compile,
|
|
||||||
$rootScope
|
|
||||||
) {
|
|
||||||
super(modelManager, storageManager, httpManager, $timeout, $interval);
|
|
||||||
this.$rootScope = $rootScope;
|
|
||||||
this.$compile = $compile;
|
|
||||||
|
|
||||||
// this.loggingEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
presentConflictResolutionModal(items, callback) {
|
|
||||||
var scope = this.$rootScope.$new(true);
|
|
||||||
scope.item1 = items[0];
|
|
||||||
scope.item2 = items[1];
|
|
||||||
scope.callback = callback;
|
|
||||||
var el = this.$compile( "<conflict-resolution-modal item1='item1' item2='item2' callback='callback' class='sk-modal'></conflict-resolution-modal>" )(scope);
|
|
||||||
angular.element(document.body).append(el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@ export class ThemeManager {
|
|||||||
componentManager,
|
componentManager,
|
||||||
desktopManager,
|
desktopManager,
|
||||||
storageManager,
|
storageManager,
|
||||||
passcodeManager,
|
lockManager,
|
||||||
appState
|
appState
|
||||||
) {
|
) {
|
||||||
this.componentManager = componentManager;
|
this.componentManager = componentManager;
|
||||||
@@ -24,13 +24,6 @@ export class ThemeManager {
|
|||||||
|
|
||||||
this.registerObservers();
|
this.registerObservers();
|
||||||
|
|
||||||
// When a passcode is added, all local storage will be encrypted (it doesn't know what was
|
|
||||||
// originally saved as Fixed or FixedEncrypted). We want to rewrite cached themes here to Fixed
|
|
||||||
// so that it's readable without authentication.
|
|
||||||
passcodeManager.addPasscodeChangeObserver(() => {
|
|
||||||
this.cacheThemes();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (desktopManager.isDesktop) {
|
if (desktopManager.isDesktop) {
|
||||||
appState.addObserver((eventName, data) => {
|
appState.addObserver((eventName, data) => {
|
||||||
if (eventName === APP_STATE_EVENT_DESKTOP_EXTS_READY) {
|
if (eventName === APP_STATE_EVENT_DESKTOP_EXTS_READY) {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
import { PrivilegesManager } from '@/services/privilegesManager';
|
||||||
|
import { isDesktopApplication } from '@/utils';
|
||||||
|
import pull from 'lodash/pull';
|
||||||
|
|
||||||
export const APP_STATE_EVENT_TAG_CHANGED = 1;
|
export const APP_STATE_EVENT_TAG_CHANGED = 1;
|
||||||
export const APP_STATE_EVENT_NOTE_CHANGED = 2;
|
export const APP_STATE_EVENT_NOTE_CHANGED = 2;
|
||||||
@@ -8,6 +10,8 @@ export const APP_STATE_EVENT_EDITOR_FOCUSED = 5;
|
|||||||
export const APP_STATE_EVENT_BEGAN_BACKUP_DOWNLOAD = 6;
|
export const APP_STATE_EVENT_BEGAN_BACKUP_DOWNLOAD = 6;
|
||||||
export const APP_STATE_EVENT_ENDED_BACKUP_DOWNLOAD = 7;
|
export const APP_STATE_EVENT_ENDED_BACKUP_DOWNLOAD = 7;
|
||||||
export const APP_STATE_EVENT_DESKTOP_EXTS_READY = 8;
|
export const APP_STATE_EVENT_DESKTOP_EXTS_READY = 8;
|
||||||
|
export const APP_STATE_EVENT_WINDOW_DID_FOCUS = 9;
|
||||||
|
export const APP_STATE_EVENT_WINDOW_DID_BLUR = 10;
|
||||||
|
|
||||||
export const EVENT_SOURCE_USER_INTERACTION = 1;
|
export const EVENT_SOURCE_USER_INTERACTION = 1;
|
||||||
export const EVENT_SOURCE_SCRIPT = 2;
|
export const EVENT_SOURCE_SCRIPT = 2;
|
||||||
@@ -15,15 +19,44 @@ export const EVENT_SOURCE_SCRIPT = 2;
|
|||||||
export class AppState {
|
export class AppState {
|
||||||
|
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor($timeout, privilegesManager) {
|
constructor(
|
||||||
|
$timeout,
|
||||||
|
$rootScope,
|
||||||
|
privilegesManager
|
||||||
|
) {
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
|
this.$rootScope = $rootScope;
|
||||||
this.privilegesManager = privilegesManager;
|
this.privilegesManager = privilegesManager;
|
||||||
this.observers = [];
|
this.observers = [];
|
||||||
|
this.registerVisibilityObservers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerVisibilityObservers() {
|
||||||
|
if (isDesktopApplication()) {
|
||||||
|
this.$rootScope.$on('window-lost-focus', () => {
|
||||||
|
this.notifyEvent(APP_STATE_EVENT_WINDOW_DID_BLUR);
|
||||||
|
});
|
||||||
|
this.$rootScope.$on('window-gained-focus', () => {
|
||||||
|
this.notifyEvent(APP_STATE_EVENT_WINDOW_DID_FOCUS);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
/* Tab visibility listener, web only */
|
||||||
|
document.addEventListener('visibilitychange', (e) => {
|
||||||
|
const visible = document.visibilityState === "visible";
|
||||||
|
const event = visible
|
||||||
|
? APP_STATE_EVENT_WINDOW_DID_FOCUS
|
||||||
|
: APP_STATE_EVENT_WINDOW_DID_BLUR;
|
||||||
|
this.notifyEvent(event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns A function that unregisters this observer */
|
||||||
addObserver(callback) {
|
addObserver(callback) {
|
||||||
this.observers.push(callback);
|
this.observers.push(callback);
|
||||||
return callback;
|
return () => {
|
||||||
|
pull(this.observers, callback);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async notifyEvent(eventName, data) {
|
async notifyEvent(eventName, data) {
|
||||||
@@ -66,7 +99,7 @@ export class AppState {
|
|||||||
await this.privilegesManager.actionRequiresPrivilege(
|
await this.privilegesManager.actionRequiresPrivilege(
|
||||||
PrivilegesManager.ActionViewProtectedNotes
|
PrivilegesManager.ActionViewProtectedNotes
|
||||||
)) {
|
)) {
|
||||||
this.privilegesManager.presentPrivilegesModal(
|
this.godService.presentPrivilegesModal(
|
||||||
PrivilegesManager.ActionViewProtectedNotes,
|
PrivilegesManager.ActionViewProtectedNotes,
|
||||||
run
|
run
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ export function isNullOrUndefined(value) {
|
|||||||
|
|
||||||
export function getPlatformString() {
|
export function getPlatformString() {
|
||||||
try {
|
try {
|
||||||
var platform = navigator.platform.toLowerCase();
|
const platform = navigator.platform.toLowerCase();
|
||||||
var trimmed = '';
|
let trimmed = '';
|
||||||
if (platform.indexOf('mac') !== -1) {
|
if (platform.indexOf('mac') !== -1) {
|
||||||
trimmed = 'mac';
|
trimmed = 'mac';
|
||||||
} else if (platform.indexOf('win') !== -1) {
|
} else if (platform.indexOf('win') !== -1) {
|
||||||
|
|||||||
114
app/assets/javascripts/web_device_interface.js
Normal file
114
app/assets/javascripts/web_device_interface.js
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { DeviceInterface } from 'snjs';
|
||||||
|
|
||||||
|
export class WebDeviceInterface extends DeviceInterface {
|
||||||
|
|
||||||
|
async getRawStorageValue(key) {
|
||||||
|
return localStorage.getItem(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllRawStorageKeyValues() {
|
||||||
|
const results = [];
|
||||||
|
for(const key of Object.keys(localStorage)) {
|
||||||
|
results.push({
|
||||||
|
key: key,
|
||||||
|
value: localStorage[key]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setRawStorageValue(key, value) {
|
||||||
|
localStorage.setItem(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeRawStorageValue(key) {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeAllRawStorageValues() {
|
||||||
|
localStorage.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @database */
|
||||||
|
|
||||||
|
_getDatabaseKeyPrefix() {
|
||||||
|
if(this.namespace) {
|
||||||
|
return `${this.namespace}-item-`;
|
||||||
|
} else {
|
||||||
|
return `item-`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_keyForPayloadId(id) {
|
||||||
|
return `${this._getDatabaseKeyPrefix()}${id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRawDatabasePayloadWithId(id) {
|
||||||
|
return localStorage.getItem(this._keyForPayloadId(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllRawDatabasePayloads() {
|
||||||
|
const models = [];
|
||||||
|
for(const key in localStorage) {
|
||||||
|
if(key.startsWith(this._getDatabaseKeyPrefix())) {
|
||||||
|
models.push(JSON.parse(localStorage[key]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return models;
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveRawDatabasePayload(payload) {
|
||||||
|
localStorage.setItem(
|
||||||
|
this._keyForPayloadId(payload.uuid),
|
||||||
|
JSON.stringify(payload)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveRawDatabasePayloads(payloads) {
|
||||||
|
for(const payload of payloads) {
|
||||||
|
await this.saveRawDatabasePayload(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeRawDatabasePayloadWithId(id) {
|
||||||
|
localStorage.removeItem(this._keyForPayloadId(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeAllRawDatabasePayloads() {
|
||||||
|
for(const key in localStorage) {
|
||||||
|
if(key.startsWith(this._getDatabaseKeyPrefix())) {
|
||||||
|
delete localStorage[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @keychian */
|
||||||
|
async getRawKeychainValue() {
|
||||||
|
if(this.keychainValue) {
|
||||||
|
return this.keychainValue;
|
||||||
|
} else {
|
||||||
|
const authParams = localStorage.getItem('auth_params');
|
||||||
|
if(!authParams) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const version = JSON.parse(authParams).version;
|
||||||
|
return {
|
||||||
|
mk: localStorage.getItem('mk'),
|
||||||
|
pw: localStorage.getItem('pw'),
|
||||||
|
ak: localStorage.getItem('ak'),
|
||||||
|
version: version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setKeychainValue(value) {
|
||||||
|
this.keychainValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearKeychainValue() {
|
||||||
|
this.keychainValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -204,10 +204,10 @@
|
|||||||
.inline Security Update Available
|
.inline Security Update Available
|
||||||
.sk-panel-section
|
.sk-panel-section
|
||||||
.sk-panel-section-title Encryption
|
.sk-panel-section-title Encryption
|
||||||
.sk-panel-section-subtitle.info(ng-if='self.encryptionEnabled()')
|
.sk-panel-section-subtitle.info(ng-if='self.state.encryptionEnabled')
|
||||||
| {{self.encryptionStatusForNotes()}}
|
| {{self.encryptionStatusForNotes()}}
|
||||||
p.sk-p
|
p.sk-p
|
||||||
| {{self.encryptionStatusString()}}
|
| {{self.state.encryptionStatusString}}
|
||||||
.sk-panel-section
|
.sk-panel-section
|
||||||
.sk-panel-section-title Passcode Lock
|
.sk-panel-section-title Passcode Lock
|
||||||
div(ng-if='!self.hasPasscode()')
|
div(ng-if='!self.hasPasscode()')
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
.main-ui-view(
|
.main-ui-view(
|
||||||
ng-class='platform'
|
ng-class='self.platformString'
|
||||||
)
|
)
|
||||||
lock-screen(
|
lock-screen(
|
||||||
ng-if='needsUnlock',
|
ng-if='self.state.needsUnlock'
|
||||||
on-success='onSuccessfulUnlock'
|
|
||||||
)
|
)
|
||||||
#app.app(
|
#app.app(
|
||||||
ng-class='appClass',
|
ng-class='self.state.appClass',
|
||||||
ng-if='!needsUnlock'
|
ng-if='!self.state.needsUnlock'
|
||||||
)
|
)
|
||||||
tags-panel
|
tags-panel
|
||||||
notes-panel
|
notes-panel
|
||||||
editor-panel
|
editor-panel
|
||||||
|
|
||||||
footer(
|
footer(
|
||||||
ng-if='!needsUnlock'
|
ng-if='!self.state.needsUnlock'
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<meta name="og:description" content="Standard Notes is a basic notes app that delivers only the essentials in note taking. Because of its simplicity and resistance to growth, users can count on durability and convenience."/>
|
<meta name="og:description" content="Standard Notes is a basic notes app that delivers only the essentials in note taking. Because of its simplicity and resistance to growth, users can count on durability and convenience."/>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window._default_sf_server = "<%= ENV['SF_DEFAULT_SERVER'] %>";
|
window._default_sync_server = "<%= ENV['SF_DEFAULT_SERVER'] %>";
|
||||||
window._extensions_manager_location = "<%= ENV['EXTENSIONS_MANAGER_LOCATION'] %>";
|
window._extensions_manager_location = "<%= ENV['EXTENSIONS_MANAGER_LOCATION'] %>";
|
||||||
window._batch_manager_location = "<%= ENV['BATCH_MANAGER_LOCATION'] %>";
|
window._batch_manager_location = "<%= ENV['BATCH_MANAGER_LOCATION'] %>";
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
80773
dist/javascripts/app.js
vendored
80773
dist/javascripts/app.js
vendored
File diff suppressed because one or more lines are too long
2
dist/javascripts/app.js.map
vendored
2
dist/javascripts/app.js.map
vendored
File diff suppressed because one or more lines are too long
2652
dist/stylesheets/app.css
vendored
2652
dist/stylesheets/app.css
vendored
File diff suppressed because one or more lines are too long
2
dist/stylesheets/app.css.map
vendored
2
dist/stylesheets/app.css.map
vendored
File diff suppressed because one or more lines are too long
@@ -25,7 +25,7 @@
|
|||||||
<title>Notes · Standard Notes</title>
|
<title>Notes · Standard Notes</title>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window._default_sf_server = "https://sync.standardnotes.org";
|
window._default_sync_server = "https://sync.standardnotes.org";
|
||||||
window._extensions_manager_location = "public/extensions/extensions-manager/dist/index.html";
|
window._extensions_manager_location = "public/extensions/extensions-manager/dist/index.html";
|
||||||
window._batch_manager_location = "public/extensions/batch-manager/dist/index.min.html";
|
window._batch_manager_location = "public/extensions/batch-manager/dist/index.min.html";
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
14998
package-lock.json
generated
14998
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -47,7 +47,7 @@
|
|||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
"serve-static": "^1.14.1",
|
"serve-static": "^1.14.1",
|
||||||
"sn-stylekit": "2.0.20",
|
"sn-stylekit": "2.0.20",
|
||||||
"snjs": "1.0.6",
|
"snjs": "file:~/Desktop/sn/dev/snjs",
|
||||||
"webpack": "^4.41.5",
|
"webpack": "^4.41.5",
|
||||||
"webpack-cli": "^3.3.10"
|
"webpack-cli": "^3.3.10"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user