Functioning UI
This commit is contained in:
@@ -55,15 +55,14 @@ import { trusted } from './filters';
|
||||
|
||||
import {
|
||||
ArchiveManager,
|
||||
DatabaseManager,
|
||||
DesktopManager,
|
||||
KeyboardManager,
|
||||
NativeExtManager,
|
||||
GodService,
|
||||
LockManager,
|
||||
NativeExtManager,
|
||||
PreferencesManager,
|
||||
StatusManager,
|
||||
ThemeManager,
|
||||
AlertManager,
|
||||
PreferencesManager
|
||||
} from './services';
|
||||
|
||||
angular.module('app', ['ngSanitize']);
|
||||
@@ -108,11 +107,12 @@ angular
|
||||
.directive('accountMenu', () => new AccountMenu())
|
||||
.directive('actionsMenu', () => new ActionsMenu())
|
||||
.directive('componentModal', () => new ComponentModal())
|
||||
.directive(
|
||||
'componentView',
|
||||
($rootScope, componentManager, desktopManager, $timeout) =>
|
||||
new ComponentView($rootScope, componentManager, desktopManager, $timeout)
|
||||
)
|
||||
.directive('componentView', () => new ComponentView())
|
||||
// .directive(
|
||||
// 'componentView',
|
||||
// ($rootScope, componentManager, desktopManager, $timeout) =>
|
||||
// new ComponentView($rootScope, componentManager, desktopManager, $timeout)
|
||||
// )
|
||||
.directive('conflictResolutionModal', () => new ConflictResolutionModal())
|
||||
.directive('editorMenu', () => new EditorMenu())
|
||||
.directive('inputModal', () => new InputModal())
|
||||
@@ -134,16 +134,15 @@ angular
|
||||
// Services
|
||||
angular
|
||||
.module('app')
|
||||
.service('application', Application)
|
||||
.service('appState', AppState)
|
||||
.service('preferencesManager', PreferencesManager)
|
||||
.service('application', Application)
|
||||
.service('archiveManager', ArchiveManager)
|
||||
.service('databaseManager', DatabaseManager)
|
||||
.service('desktopManager', DesktopManager)
|
||||
.service('godService', GodService)
|
||||
.service('keyboardManager', KeyboardManager)
|
||||
.service('nativeExtManager', NativeExtManager)
|
||||
.service('lockManager', LockManager)
|
||||
.service('nativeExtManager', NativeExtManager)
|
||||
.service('preferencesManager', PreferencesManager)
|
||||
.service('statusManager', StatusManager)
|
||||
.service('storageManager', StorageManager)
|
||||
.service('alertManager', AlertManager)
|
||||
.service('themeManager', ThemeManager);
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import {
|
||||
import {
|
||||
SNApplication,
|
||||
SNAlertManager,
|
||||
Platforms
|
||||
} from 'snjs';
|
||||
import angular from 'angular';
|
||||
import { AlertManager } from '@/services/alertManager';
|
||||
Environments,
|
||||
platformFromString
|
||||
} from 'snjs';
|
||||
import angular from 'angular';
|
||||
import { getPlatformString } from '@/utils';
|
||||
import { AlertManager } from '@/services/alertManager';
|
||||
|
||||
import { WebDeviceInterface } from '@/web_device_interface';
|
||||
|
||||
export class Application extends SNApplication {
|
||||
constructor(
|
||||
desktopManager
|
||||
) {
|
||||
constructor() {
|
||||
const deviceInterface = new WebDeviceInterface();
|
||||
super({
|
||||
platform: Platforms.Web,
|
||||
environment: Environments.Web,
|
||||
platform: platformFromString(getPlatformString()),
|
||||
namespace: '',
|
||||
host: window._default_sync_server,
|
||||
deviceInterface: deviceInterface,
|
||||
@@ -25,30 +26,26 @@ export class Application extends SNApplication {
|
||||
}
|
||||
]
|
||||
});
|
||||
this.desktopManager = desktopManager;
|
||||
deviceInterface.setApplication(this);
|
||||
this.overrideComponentManagerFunctions();
|
||||
}
|
||||
|
||||
overrideComponentManagerFunctions() {
|
||||
function openModalComponent(component) {
|
||||
var scope = this.$rootScope.$new(true);
|
||||
const scope = this.$rootScope.$new(true);
|
||||
scope.component = component;
|
||||
var el = this.$compile("<component-modal component='component' class='sk-modal'></component-modal>")(scope);
|
||||
const 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);
|
||||
const 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);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from 'snjs';
|
||||
import find from 'lodash/find';
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { KeyboardModifiers, KeyboardKeys } from '@/services/keyboardManager';
|
||||
import template from '%/editor.pug';
|
||||
import { PureCtrl } from '@Controllers';
|
||||
import { AppStateEvents, EventSources } from '@/state';
|
||||
@@ -80,9 +81,12 @@ class EditorCtrl extends PureCtrl {
|
||||
this.addAppStateObserver();
|
||||
this.addAppEventObserver();
|
||||
this.addSyncStatusObserver();
|
||||
this.streamItems();
|
||||
this.registerComponentHandler();
|
||||
this.registerKeyboardShortcuts();
|
||||
|
||||
application.onReady(() => {
|
||||
this.streamItems();
|
||||
this.registerComponentHandler();
|
||||
});
|
||||
|
||||
/** Used by .pug template */
|
||||
this.prefKeyMonospace = PrefKeys.EditorMonospaceEnabled;
|
||||
@@ -269,7 +273,7 @@ class EditorCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
editorForNote(note) {
|
||||
return this.componentManager.editorForNote(note);
|
||||
return this.application.componentManager.editorForNote(note);
|
||||
}
|
||||
|
||||
setMenuState(menu, state) {
|
||||
@@ -861,7 +865,7 @@ class EditorCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
registerComponentHandler() {
|
||||
this.componentManager.registerHandler({
|
||||
this.application.componentManager.registerHandler({
|
||||
identifier: 'editor',
|
||||
areas: [
|
||||
'note-tags',
|
||||
@@ -973,7 +977,7 @@ class EditorCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
reloadComponentStackArray() {
|
||||
const components = this.componentManager.componentsForArea('editor-stack')
|
||||
const components = this.application.componentManager.componentsForArea('editor-stack')
|
||||
.sort((a, b) => {
|
||||
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
|
||||
});
|
||||
@@ -988,7 +992,7 @@ class EditorCtrl extends PureCtrl {
|
||||
if (this.state.note) {
|
||||
for (const component of this.state.componentStack) {
|
||||
if (component.active) {
|
||||
this.componentManager.setComponentHidden(
|
||||
this.application.componentManager.setComponentHidden(
|
||||
component,
|
||||
!component.isExplicitlyEnabledForItem(this.state.note)
|
||||
);
|
||||
@@ -996,21 +1000,21 @@ class EditorCtrl extends PureCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
this.componentManager.contextItemDidChangeInArea('note-tags');
|
||||
this.componentManager.contextItemDidChangeInArea('editor-stack');
|
||||
this.componentManager.contextItemDidChangeInArea('editor-editor');
|
||||
this.application.componentManager.contextItemDidChangeInArea('note-tags');
|
||||
this.application.componentManager.contextItemDidChangeInArea('editor-stack');
|
||||
this.application.componentManager.contextItemDidChangeInArea('editor-editor');
|
||||
}
|
||||
|
||||
toggleStackComponentForCurrentItem(component) {
|
||||
if (component.hidden || !component.active) {
|
||||
this.componentManager.setComponentHidden(component, false);
|
||||
this.application.componentManager.setComponentHidden(component, false);
|
||||
this.associateComponentWithCurrentNote(component);
|
||||
if (!component.active) {
|
||||
this.componentManager.activateComponent(component);
|
||||
this.application.componentManager.activateComponent(component);
|
||||
}
|
||||
this.componentManager.contextItemDidChangeInArea('editor-stack');
|
||||
this.application.componentManager.contextItemDidChangeInArea('editor-stack');
|
||||
} else {
|
||||
this.componentManager.setComponentHidden(component, true);
|
||||
this.application.componentManager.setComponentHidden(component, true);
|
||||
this.disassociateComponentWithCurrentNote(component);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,21 +37,29 @@ class FooterCtrl {
|
||||
this.showSyncResolution = false;
|
||||
|
||||
this.addAppStateObserver();
|
||||
this.updateOfflineStatus();
|
||||
this.addAppEventObserver();
|
||||
this.findErrors();
|
||||
this.streamItems();
|
||||
this.registerComponentHandler();
|
||||
this.addRootScopeListeners();
|
||||
|
||||
this.godService.checkForSecurityUpdate().then((available) => {
|
||||
this.securityUpdateAvailable = available;
|
||||
});
|
||||
this.statusManager.addStatusObserver((string) => {
|
||||
this.$timeout(() => {
|
||||
this.arbitraryStatusMessage = string;
|
||||
});
|
||||
});
|
||||
|
||||
application.onReady(() => {
|
||||
this.application.hasPasscode().then((value) => {
|
||||
this.hasPasscode = value;
|
||||
});
|
||||
|
||||
this.godService.checkForSecurityUpdate().then((available) => {
|
||||
this.securityUpdateAvailable = available;
|
||||
});
|
||||
this.user = this.application.getUser();
|
||||
this.updateOfflineStatus();
|
||||
this.addAppEventObserver();
|
||||
this.findErrors();
|
||||
this.streamItems();
|
||||
this.registerComponentHandler();
|
||||
});
|
||||
}
|
||||
|
||||
addRootScopeListeners() {
|
||||
@@ -99,7 +107,7 @@ class FooterCtrl {
|
||||
}
|
||||
|
||||
addAppEventObserver() {
|
||||
this.application.addEventHandler((eventName) => {
|
||||
this.application.addEventObserver((eventName) => {
|
||||
if (eventName === ApplicationEvents.LoadedLocalData) {
|
||||
if(this.offline && this.application.getNoteCount() === 0) {
|
||||
this.showAccountMenu = true;
|
||||
@@ -203,12 +211,8 @@ class FooterCtrl {
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
getUser() {
|
||||
return this.application.getUser();
|
||||
}
|
||||
|
||||
updateOfflineStatus() {
|
||||
this.offline = this.application.noUser();
|
||||
this.offline = this.application.noAccount();
|
||||
}
|
||||
|
||||
openSecurityUpdate() {
|
||||
@@ -232,10 +236,6 @@ class FooterCtrl {
|
||||
this.showAccountMenu = false;
|
||||
}
|
||||
|
||||
hasPasscode() {
|
||||
return this.application.hasPasscode();
|
||||
}
|
||||
|
||||
lockApp() {
|
||||
this.$rootScope.lockApplication();
|
||||
}
|
||||
@@ -351,7 +351,7 @@ class FooterCtrl {
|
||||
}
|
||||
|
||||
clickOutsideAccountMenu() {
|
||||
if(this.application.privilegesManager.authenticationInProgress()) {
|
||||
if(this.godService.authenticationInProgress()) {
|
||||
return;
|
||||
}
|
||||
this.showAccountMenu = false;
|
||||
|
||||
@@ -4,6 +4,7 @@ import template from '%/notes.pug';
|
||||
import { ApplicationEvents, ContentTypes } from 'snjs';
|
||||
import { PureCtrl } from '@Controllers';
|
||||
import { AppStateEvents } from '@/state';
|
||||
import { KeyboardModifiers, KeyboardKeys } from '@/services/keyboardManager';
|
||||
import {
|
||||
PrefKeys
|
||||
} from '@/services/preferencesManager';
|
||||
@@ -70,13 +71,15 @@ class NotesCtrl extends PureCtrl {
|
||||
|
||||
this.addAppStateObserver();
|
||||
this.addAppEventObserver();
|
||||
this.streamNotesAndTags();
|
||||
this.reloadPreferences();
|
||||
this.resetPagination();
|
||||
this.registerKeyboardShortcuts();
|
||||
angular.element(document).ready(() => {
|
||||
this.reloadPreferences();
|
||||
});
|
||||
application.onReady(() => {
|
||||
this.streamNotesAndTags();
|
||||
this.reloadPreferences();
|
||||
});
|
||||
}
|
||||
|
||||
addAppStateObserver() {
|
||||
@@ -99,7 +102,7 @@ class NotesCtrl extends PureCtrl {
|
||||
if (eventName === ApplicationEvents.SignedIn) {
|
||||
/** Delete dummy note if applicable */
|
||||
if (this.state.selectedNote && this.state.selectedNote.dummy) {
|
||||
this.application.removeItemLocally({ item: this.state.selectedNote });
|
||||
this.application.deleteItemLocally({ item: this.state.selectedNote });
|
||||
this.selectNote(null).then(() => {
|
||||
this.reloadNotes();
|
||||
});
|
||||
@@ -110,10 +113,6 @@ class NotesCtrl extends PureCtrl {
|
||||
*/
|
||||
this.createDummyOnSynCompletionIfNoNotes = true;
|
||||
}
|
||||
} else if (eventName === ApplicationEvents.LoadedLocalData) {
|
||||
if (this.state.notes.length === 0) {
|
||||
this.createNewNote();
|
||||
}
|
||||
} else if (eventName === ApplicationEvents.CompletedSync) {
|
||||
if (this.createDummyOnSynCompletionIfNoNotes && this.state.notes.length === 0) {
|
||||
this.createDummyOnSynCompletionIfNoNotes = false;
|
||||
@@ -152,7 +151,7 @@ class NotesCtrl extends PureCtrl {
|
||||
|
||||
async handleTagChange(tag, previousTag) {
|
||||
if (this.state.selectedNote && this.state.selectedNote.dummy) {
|
||||
this.application.removeItemLocally({ item: this.state.selectedNote });
|
||||
this.application.deleteItemLocally({ item: this.state.selectedNote });
|
||||
if (previousTag) {
|
||||
_.remove(previousTag.notes, this.state.selectedNote);
|
||||
}
|
||||
@@ -251,7 +250,7 @@ class NotesCtrl extends PureCtrl {
|
||||
}
|
||||
const previousNote = this.state.selectedNote;
|
||||
if (previousNote && previousNote.dummy) {
|
||||
this.application.removeItemLocally({ previousNote });
|
||||
this.application.deleteItemLocally({ item: previousNote });
|
||||
this.removeNoteFromList(previousNote);
|
||||
}
|
||||
await this.setState({
|
||||
@@ -502,12 +501,17 @@ class NotesCtrl extends PureCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
createNewNote() {
|
||||
async createNewNote() {
|
||||
const selectedTag = this.appState.getSelectedTag();
|
||||
if (!selectedTag) {
|
||||
debugger;
|
||||
throw 'Attempting to create note with no selected tag';
|
||||
}
|
||||
if (this.state.selectedNote && this.state.selectedNote.dummy) {
|
||||
return;
|
||||
}
|
||||
const title = "Note" + (this.state.notes ? (" " + (this.state.notes.length + 1)) : "");
|
||||
const newNote = this.application.createItem({
|
||||
const newNote = await this.application.createItem({
|
||||
contentType: ContentTypes.Note,
|
||||
content: {
|
||||
text: '',
|
||||
@@ -517,7 +521,6 @@ class NotesCtrl extends PureCtrl {
|
||||
newNote.client_updated_at = new Date();
|
||||
newNote.dummy = true;
|
||||
this.application.setItemNeedsSync({ item: newNote });
|
||||
const selectedTag = this.appState.getSelectedTag();
|
||||
if (!selectedTag.isSmartTag()) {
|
||||
selectedTag.addItemAsRelationship(newNote);
|
||||
this.application.setItemNeedsSync({ item: selectedTag });
|
||||
|
||||
@@ -22,7 +22,7 @@ class RootCtrl extends PureCtrl {
|
||||
$timeout,
|
||||
application,
|
||||
appState,
|
||||
databaseManager,
|
||||
desktopManager,
|
||||
lockManager,
|
||||
preferencesManager,
|
||||
themeManager /** Unused below, required to load globally */,
|
||||
@@ -34,7 +34,7 @@ class RootCtrl extends PureCtrl {
|
||||
this.$timeout = $timeout;
|
||||
this.application = application;
|
||||
this.appState = appState;
|
||||
this.databaseManager = databaseManager;
|
||||
this.desktopManager = desktopManager;
|
||||
this.lockManager = lockManager;
|
||||
this.preferencesManager = preferencesManager;
|
||||
this.statusManager = statusManager;
|
||||
@@ -44,27 +44,34 @@ class RootCtrl extends PureCtrl {
|
||||
appClass: ''
|
||||
};
|
||||
this.loadApplication();
|
||||
this.handleAutoSignInFromParams();
|
||||
this.addAppStateObserver();
|
||||
this.addDragDropHandlers();
|
||||
|
||||
application.onReady(() => {
|
||||
this.handleAutoSignInFromParams();
|
||||
});
|
||||
}
|
||||
|
||||
async loadApplication() {
|
||||
await this.application.prepareForLaunch({
|
||||
callbacks: {
|
||||
authChallengeResponses: async (challenges) => {
|
||||
console.log("Needs challenge repsonses", challenges);
|
||||
if (challenges.includes(Challenges.LocalPasscode)) {
|
||||
this.setState({ needsUnlock: true });
|
||||
}
|
||||
},
|
||||
onReady: async () => {
|
||||
await this.appState.setApplicationReady();
|
||||
}
|
||||
}
|
||||
});
|
||||
await this.application.launch();
|
||||
this.setState({ needsUnlock: false });
|
||||
await this.openDatabase();
|
||||
await this.preferencesManager.initialize();
|
||||
this.addSyncStatusObserver();
|
||||
this.addSyncEventHandler();
|
||||
this.application.componentManager.setDesktopManager(this.desktopManager);
|
||||
this.preferencesManager.initialize();
|
||||
// this.addSyncStatusObserver();
|
||||
// this.addSyncEventHandler();
|
||||
}
|
||||
|
||||
onUpdateAvailable() {
|
||||
@@ -92,20 +99,6 @@ class RootCtrl extends PureCtrl {
|
||||
});
|
||||
}
|
||||
|
||||
async openDatabase() {
|
||||
this.databaseManager.setLocked(false);
|
||||
this.databaseManager.openDatabase({
|
||||
onUpgradeNeeded: () => {
|
||||
/**
|
||||
* New database/database wiped, delete syncToken so that items
|
||||
* can be refetched entirely from server
|
||||
*/
|
||||
this.application.syncManager.clearSyncPositionTokens();
|
||||
this.application.sync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// addSyncStatusObserver() {
|
||||
// this.syncStatusObserver = syncManager.registerSyncStatusObserver((status) => {
|
||||
// if (status.retrievedCount > 20) {
|
||||
|
||||
@@ -21,18 +21,21 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
this.appState = appState;
|
||||
this.preferencesManager = preferencesManager;
|
||||
this.panelController = {};
|
||||
this.beginStreamingItems();
|
||||
this.addAppStateObserver();
|
||||
this.loadPreferences();
|
||||
this.registerComponentHandler();
|
||||
this.state = {
|
||||
smartTags: this.application.getSmartTags(),
|
||||
smartTags: [],
|
||||
noteCounts: {}
|
||||
};
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.selectTag(this.state.smartTags[0]);
|
||||
application.onReady(() => {
|
||||
this.beginStreamingItems();
|
||||
const smartTags = this.application.getSmartTags();
|
||||
this.setState({
|
||||
smartTags: smartTags,
|
||||
});
|
||||
this.selectTag(smartTags[0]);
|
||||
});
|
||||
}
|
||||
|
||||
beginStreamingItems() {
|
||||
@@ -41,7 +44,7 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
stream: async ({ items }) => {
|
||||
await this.setState({
|
||||
tags: this.application.getItems({ contentType: ContentTypes.Tag }),
|
||||
smartTags: this.application.getItems({ contentType: ContentTypes.SmartTag }),
|
||||
smartTags: this.application.getSmartTags(),
|
||||
});
|
||||
this.reloadNoteCounts();
|
||||
if (this.state.selectedTag) {
|
||||
@@ -159,11 +162,11 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
this.appState.setSelectedTag(tag);
|
||||
}
|
||||
|
||||
clickedAddNewTag() {
|
||||
async clickedAddNewTag() {
|
||||
if (this.state.editingTag) {
|
||||
return;
|
||||
}
|
||||
const newTag = this.application.createItem({
|
||||
const newTag = await this.application.createItem({
|
||||
contentType: ContentTypes.Tag
|
||||
});
|
||||
this.setState({
|
||||
|
||||
185
app/assets/javascripts/database.js
Normal file
185
app/assets/javascripts/database.js
Normal file
@@ -0,0 +1,185 @@
|
||||
export class Database {
|
||||
constructor() {
|
||||
this.locked = true;
|
||||
}
|
||||
|
||||
setApplication(application) {
|
||||
this.alertManager = application.alertManager;
|
||||
}
|
||||
|
||||
displayOfflineAlert() {
|
||||
var message = "There was an issue loading your offline database. This could happen for two reasons:";
|
||||
message += "\n\n1. You're in a private window in your browser. We can't save your data without access to the local database. Please use a non-private window.";
|
||||
message += "\n\n2. You have two windows of the app open at the same time. Please close any other app instances and reload the page.";
|
||||
this.alertManager.alert({ text: message });
|
||||
}
|
||||
|
||||
setLocked(locked) {
|
||||
this.locked = locked;
|
||||
}
|
||||
|
||||
async openDatabase({ onUpgradeNeeded } = {}) {
|
||||
if (this.locked) {
|
||||
throw 'Attempting to open locked database';
|
||||
}
|
||||
|
||||
const request = window.indexedDB.open('standardnotes', 1);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request.onerror = (event) => {
|
||||
if (event.target.errorCode) {
|
||||
this.alertManager.alert({ text: 'Offline database issue: ' + event.target.errorCode });
|
||||
} else {
|
||||
this.displayOfflineAlert();
|
||||
}
|
||||
console.error('Offline database issue:', event);
|
||||
resolve(null);
|
||||
};
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
const db = event.target.result;
|
||||
db.onversionchange = function (event) {
|
||||
db.close();
|
||||
};
|
||||
db.onerror = function (errorEvent) {
|
||||
console.error('Database error: ' + errorEvent.target.errorCode);
|
||||
};
|
||||
resolve(db);
|
||||
};
|
||||
|
||||
request.onblocked = (event) => {
|
||||
console.error('Request blocked error:', event.target.errorCode);
|
||||
};
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
db.onversionchange = function (event) {
|
||||
db.close();
|
||||
};
|
||||
|
||||
// Create an objectStore for this database
|
||||
const objectStore = db.createObjectStore('items', { keyPath: 'uuid' });
|
||||
objectStore.createIndex('uuid', 'uuid', { unique: true });
|
||||
objectStore.transaction.oncomplete = function (event) {
|
||||
// Ready to store values in the newly created objectStore.
|
||||
if (db.version === 1 && onUpgradeNeeded) {
|
||||
onUpgradeNeeded();
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async getAllPayloads() {
|
||||
const db = await this.openDatabase();
|
||||
const objectStore = db.transaction('items').objectStore('items');
|
||||
const payloads = [];
|
||||
return new Promise((resolve) => {
|
||||
objectStore.openCursor().onsuccess = (event) => {
|
||||
const cursor = event.target.result;
|
||||
if (cursor) {
|
||||
payloads.push(cursor.value);
|
||||
cursor.continue();
|
||||
} else {
|
||||
resolve(payloads);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async savePayload(payload) {
|
||||
this.savePayloads([payload]);
|
||||
}
|
||||
|
||||
async savePayloads(payloads) {
|
||||
const showGenericError = (error) => {
|
||||
this.alertManager.alert({
|
||||
text: `Unable to save changes locally due to an unknown system issue. Issue Code: ${error.code} Issue Name: ${error.name}.`
|
||||
});
|
||||
};
|
||||
|
||||
if (payloads.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const db = await this.openDatabase();
|
||||
const transaction = db.transaction('items', 'readwrite');
|
||||
return new Promise(async (resolve, reject) => {
|
||||
transaction.oncomplete = (event) => { };
|
||||
transaction.onerror = function (event) {
|
||||
console.error('Transaction error:', event.target.errorCode);
|
||||
showGenericError(event.target.error);
|
||||
};
|
||||
transaction.onblocked = function (event) {
|
||||
console.error('Transaction blocked error:', event.target.errorCode);
|
||||
showGenericError(event.target.error);
|
||||
};
|
||||
transaction.onabort = function (event) {
|
||||
console.error('Offline saving aborted:', event);
|
||||
const error = event.target.error;
|
||||
if (error.name === 'QuotaExceededError') {
|
||||
this.alertManager.alert({ text:
|
||||
'Unable to save changes locally because your device is out of space. Please free up some disk space and try again, otherwise, your data may end up in an inconsistent state.'
|
||||
});
|
||||
} else {
|
||||
showGenericError(error);
|
||||
}
|
||||
reject(error);
|
||||
};
|
||||
|
||||
const payloadObjectStore = transaction.objectStore('items');
|
||||
|
||||
const putPayload = async (payload) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = payloadObjectStore.put(payload);
|
||||
request.onerror = (event) => {
|
||||
console.error('DB put error:', event.target.error);
|
||||
resolve();
|
||||
};
|
||||
request.onsuccess = resolve;
|
||||
});
|
||||
};
|
||||
|
||||
for (const payload of payloads) {
|
||||
await putPayload(payload);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
async deletePayload(uuid) {
|
||||
const db = await this.openDatabase();
|
||||
const request = db.transaction('items', 'readwrite').objectStore('items').delete(uuid);
|
||||
return new Promise((resolve, reject) => {
|
||||
request.onsuccess = (event) => {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = (event) => {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
reject();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async clearAllPayloads() {
|
||||
const deleteRequest = window.indexedDB.deleteDatabase('standardnotes');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
deleteRequest.onerror = function (event) {
|
||||
console.error('Error deleting database.');
|
||||
resolve();
|
||||
};
|
||||
|
||||
deleteRequest.onsuccess = function (event) {
|
||||
resolve();
|
||||
};
|
||||
|
||||
deleteRequest.onblocked = function (event) {
|
||||
console.error('Delete request blocked');
|
||||
this.alertManager.alert({ text: 'Your browser is blocking Standard Notes from deleting the local database. Make sure there are no other open windows of this app and try again. If the issue persists, please manually delete app data to sign out.' });
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { isDesktopApplication, isNullOrUndefined } from '@/utils';
|
||||
import template from '%/directives/account-menu.pug';
|
||||
import { ProtectedActions } from 'snjs';
|
||||
import { PureCtrl } from '@Controllers';
|
||||
import { AppStateEvents } from '@/state';
|
||||
import {
|
||||
STRING_ACCOUNT_MENU_UNCHECK_MERGE,
|
||||
STRING_SIGN_OUT_CONFIRMATION,
|
||||
@@ -32,25 +33,25 @@ class AccountMenuCtrl extends PureCtrl {
|
||||
$scope,
|
||||
$rootScope,
|
||||
$timeout,
|
||||
archiveManager,
|
||||
appVersion,
|
||||
application,
|
||||
appState,
|
||||
archiveManager,
|
||||
godService,
|
||||
lockManager,
|
||||
application
|
||||
) {
|
||||
super($timeout);
|
||||
this.$scope = $scope;
|
||||
this.$rootScope = $rootScope;
|
||||
this.$timeout = $timeout;
|
||||
this.appState = appState;
|
||||
this.application = application;
|
||||
this.archiveManager = archiveManager;
|
||||
this.godService = godService;
|
||||
this.lockManager = lockManager;
|
||||
this.application = application;
|
||||
|
||||
this.state = {
|
||||
appVersion: 'v' + (window.electronAppVersion || appVersion),
|
||||
user: this.application.getUser(),
|
||||
canAddPasscode: !this.application.isEphemeralSession(),
|
||||
passcodeAutoLockOptions: this.lockManager.getAutoLockIntervalOptions(),
|
||||
formData: {
|
||||
mergeLocal: true,
|
||||
@@ -58,12 +59,17 @@ class AccountMenuCtrl extends PureCtrl {
|
||||
},
|
||||
mutable: {}
|
||||
};
|
||||
|
||||
application.onReady(() => {
|
||||
this.setState({
|
||||
user: this.application.getUser(),
|
||||
canAddPasscode: !this.application.isEphemeralSession(),
|
||||
});
|
||||
this.loadHost();
|
||||
this.checkForSecurityUpdate();
|
||||
this.reloadAutoLockInterval();
|
||||
this.loadBackupsAvailability();
|
||||
});
|
||||
this.syncStatus = this.application.getSyncStatus();
|
||||
this.loadHost();
|
||||
this.checkForSecurityUpdate();
|
||||
this.reloadAutoLockInterval();
|
||||
this.loadBackupsAvailability();
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
|
||||
@@ -11,9 +11,11 @@ class PrivilegesManagementModalCtrl {
|
||||
this.$element = $element;
|
||||
this.$timeout = $timeout;
|
||||
this.application = application;
|
||||
this.hasPasscode = application.hasPasscode();
|
||||
this.hasAccount = !application.noUser();
|
||||
this.reloadPrivileges();
|
||||
application.onReady(() => {
|
||||
this.hasPasscode = application.hasPasscode();
|
||||
this.hasAccount = !application.noAccount();
|
||||
this.reloadPrivileges();
|
||||
});
|
||||
}
|
||||
|
||||
displayInfoForCredential(credential) {
|
||||
|
||||
@@ -3,11 +3,8 @@ import { EncryptionIntents, ProtectedActions } from 'snjs';
|
||||
|
||||
export class ArchiveManager {
|
||||
/* @ngInject */
|
||||
constructor(lockManager, application, authManager, modelManager, privilegesManager) {
|
||||
constructor(lockManager, application) {
|
||||
this.lockManager = lockManager;
|
||||
this.authManager = authManager;
|
||||
modelManager = modelManager;
|
||||
this.privilegesManager = privilegesManager;
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
@@ -16,7 +13,7 @@ export class ArchiveManager {
|
||||
*/
|
||||
/** @public */
|
||||
async downloadBackup(encrypted) {
|
||||
return this.downloadBackupOfItems(modelManager.allItems, encrypted);
|
||||
return this.downloadBackupOfItems(this.application.modelManager.allItems, encrypted);
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@@ -39,7 +36,7 @@ export class ArchiveManager {
|
||||
});
|
||||
};
|
||||
|
||||
if (await this.privilegesManager.actionRequiresPrivilege(ProtectedActions.ManageBackups)) {
|
||||
if (await this.application.privilegesManager.actionRequiresPrivilege(ProtectedActions.ManageBackups)) {
|
||||
this.godService.presentPrivilegesModal(ProtectedActions.ManageBackups, () => {
|
||||
run();
|
||||
});
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
export class DatabaseManager {
|
||||
/* @ngInject */
|
||||
constructor(alertManager) {
|
||||
this.locked = true;
|
||||
this.alertManager = alertManager;
|
||||
}
|
||||
|
||||
displayOfflineAlert() {
|
||||
var message = "There was an issue loading your offline database. This could happen for two reasons:";
|
||||
message += "\n\n1. You're in a private window in your browser. We can't save your data without access to the local database. Please use a non-private window.";
|
||||
message += "\n\n2. You have two windows of the app open at the same time. Please close any other app instances and reload the page.";
|
||||
this.alertManager.alert({text: message});
|
||||
}
|
||||
|
||||
setLocked(locked) {
|
||||
this.locked = locked;
|
||||
}
|
||||
|
||||
async openDatabase({onUpgradeNeeded} = {}) {
|
||||
if(this.locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
const request = window.indexedDB.open("standardnotes", 1);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request.onerror = (event) => {
|
||||
if(event.target.errorCode) {
|
||||
this.alertManager.alert({text: "Offline database issue: " + event.target.errorCode});
|
||||
} else {
|
||||
this.displayOfflineAlert();
|
||||
}
|
||||
console.error("Offline database issue:", event);
|
||||
resolve(null);
|
||||
};
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
const db = event.target.result;
|
||||
db.onversionchange = function(event) {
|
||||
db.close();
|
||||
};
|
||||
db.onerror = function(errorEvent) {
|
||||
console.error("Database error: " + errorEvent.target.errorCode);
|
||||
};
|
||||
resolve(db);
|
||||
};
|
||||
|
||||
request.onblocked = (event) => {
|
||||
console.error("Request blocked error:", event.target.errorCode);
|
||||
};
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
db.onversionchange = function(event) {
|
||||
db.close();
|
||||
};
|
||||
|
||||
// Create an objectStore for this database
|
||||
const objectStore = db.createObjectStore("items", { keyPath: "uuid" });
|
||||
objectStore.createIndex("uuid", "uuid", { unique: true });
|
||||
objectStore.transaction.oncomplete = function(event) {
|
||||
// Ready to store values in the newly created objectStore.
|
||||
if(db.version === 1 && onUpgradeNeeded) {
|
||||
onUpgradeNeeded();
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async getAllModels() {
|
||||
const db = await this.openDatabase();
|
||||
const objectStore = db.transaction("items").objectStore("items");
|
||||
const items = [];
|
||||
return new Promise(async (resolve, reject) => {
|
||||
objectStore.openCursor().onsuccess = (event) => {
|
||||
const cursor = event.target.result;
|
||||
if (cursor) {
|
||||
items.push(cursor.value);
|
||||
cursor.continue();
|
||||
} else {
|
||||
resolve(items);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async saveModel(item) {
|
||||
this.saveModels([item]);
|
||||
}
|
||||
|
||||
async saveModels(items) {
|
||||
const showGenericError = (error) => {
|
||||
this.alertManager.alert({text: `Unable to save changes locally due to an unknown system issue. Issue Code: ${error.code} Issue Name: ${error.name}.`});
|
||||
};
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if(items.length === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const db = await this.openDatabase();
|
||||
const transaction = db.transaction("items", "readwrite");
|
||||
transaction.oncomplete = (event) => {};
|
||||
transaction.onerror = function(event) {
|
||||
console.error("Transaction error:", event.target.errorCode);
|
||||
showGenericError(event.target.error);
|
||||
};
|
||||
transaction.onblocked = function(event) {
|
||||
console.error("Transaction blocked error:", event.target.errorCode);
|
||||
showGenericError(event.target.error);
|
||||
};
|
||||
transaction.onabort = function(event) {
|
||||
console.error("Offline saving aborted:", event);
|
||||
const error = event.target.error;
|
||||
if(error.name == "QuotaExceededError") {
|
||||
this.alertManager.alert({text: "Unable to save changes locally because your device is out of space. Please free up some disk space and try again, otherwise, your data may end up in an inconsistent state."});
|
||||
} else {
|
||||
showGenericError(error);
|
||||
}
|
||||
reject(error);
|
||||
};
|
||||
|
||||
const itemObjectStore = transaction.objectStore("items");
|
||||
|
||||
const putItem = async (item) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = itemObjectStore.put(item);
|
||||
request.onerror = (event) => {
|
||||
console.error("DB put error:", event.target.error);
|
||||
resolve();
|
||||
};
|
||||
request.onsuccess = resolve;
|
||||
});
|
||||
};
|
||||
|
||||
for(const item of items) {
|
||||
await putItem(item);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
async deleteModel(item) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const db = await this.openDatabase();
|
||||
const request = db.transaction("items", "readwrite").objectStore("items").delete(item.uuid);
|
||||
request.onsuccess = (event) => {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = (event) => {
|
||||
reject();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async clearAllModels() {
|
||||
const deleteRequest = window.indexedDB.deleteDatabase("standardnotes");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
deleteRequest.onerror = function(event) {
|
||||
console.error("Error deleting database.");
|
||||
resolve();
|
||||
};
|
||||
|
||||
deleteRequest.onsuccess = function(event) {
|
||||
resolve();
|
||||
};
|
||||
|
||||
deleteRequest.onblocked = function(event) {
|
||||
console.error("Delete request blocked");
|
||||
this.alertManager.alert({text: "Your browser is blocking Standard Notes from deleting the local database. Make sure there are no other open windows of this app and try again. If the issue persists, please manually delete app data to sign out."});
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import angular from 'angular';
|
||||
|
||||
export class GodService {
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
$rootScope,
|
||||
@@ -14,7 +13,7 @@ export class GodService {
|
||||
}
|
||||
|
||||
async checkForSecurityUpdate() {
|
||||
if (this.offline()) {
|
||||
if (this.application.noAccount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export { AlertManager } from './alertManager';
|
||||
export { ArchiveManager } from './archiveManager';
|
||||
export { DatabaseManager } from './databaseManager';
|
||||
export { DesktopManager } from './desktopManager';
|
||||
export { GodService } from './godService';
|
||||
export { KeyboardManager } from './keyboardManager';
|
||||
export { NativeExtManager } from './nativeExtManager';
|
||||
export { LockManager } from './lockManager';
|
||||
export { NativeExtManager } from './nativeExtManager';
|
||||
export { PreferencesManager } from './preferencesManager';
|
||||
export { StatusManager } from './statusManager';
|
||||
export { ThemeManager } from './themeManager';
|
||||
export { AlertManager } from './alertManager';
|
||||
export { PreferencesManager } from './preferencesManager';
|
||||
|
||||
@@ -34,10 +34,7 @@ export class LockManager {
|
||||
}
|
||||
|
||||
async setAutoLockInterval(interval) {
|
||||
return this.application.setValue(
|
||||
STORAGE_KEY_AUTOLOCK_INTERVAL,
|
||||
JSON.stringify(interval),
|
||||
);
|
||||
return this.application.setValue(STORAGE_KEY_AUTOLOCK_INTERVAL, interval);
|
||||
}
|
||||
|
||||
async getAutoLockInterval() {
|
||||
@@ -45,7 +42,7 @@ export class LockManager {
|
||||
STORAGE_KEY_AUTOLOCK_INTERVAL,
|
||||
);
|
||||
if(interval) {
|
||||
return JSON.parse(interval);
|
||||
return interval;
|
||||
} else {
|
||||
return LOCK_INTERVAL_NONE;
|
||||
}
|
||||
@@ -93,10 +90,11 @@ export class LockManager {
|
||||
];
|
||||
}
|
||||
|
||||
documentVisibilityChanged(visible) {
|
||||
async documentVisibilityChanged(visible) {
|
||||
if(visible) {
|
||||
const locked = await this.application.isPasscodeLocked();
|
||||
if(
|
||||
!this.isLocked() &&
|
||||
!locked &&
|
||||
this.lockAfterDate &&
|
||||
new Date() > this.lockAfterDate
|
||||
) {
|
||||
|
||||
@@ -15,10 +15,8 @@ export class NativeExtManager {
|
||||
this.resolveExtensionsManager();
|
||||
this.resolveBatchManager();
|
||||
|
||||
appState.addObserver(async (eventName) => {
|
||||
if (eventName === AppStateEvents.ApplicationReady) {
|
||||
await this.initialize();
|
||||
}
|
||||
application.onReady(() => {
|
||||
this.initialize();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,8 @@ export class PreferencesManager {
|
||||
) {
|
||||
this.application = application;
|
||||
this.appState = appState;
|
||||
appState.addObserver(async (eventName) => {
|
||||
if (eventName === AppStateEvents.ApplicationReady) {
|
||||
await this.initialize();
|
||||
}
|
||||
application.onReady(() => {
|
||||
this.initialize();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import { SNTheme, StorageValueModes, EncryptionIntents } from 'snjs';
|
||||
import { ContentTypes, StorageValueModes, EncryptionIntents } from 'snjs';
|
||||
import { AppStateEvents } from '@/state';
|
||||
|
||||
const CACHED_THEMES_KEY = 'cachedThemes';
|
||||
@@ -16,9 +16,16 @@ export class ThemeManager {
|
||||
this.desktopManager = desktopManager;
|
||||
this.activeThemes = [];
|
||||
this.registerObservers();
|
||||
if (!desktopManager.isDesktop) {
|
||||
this.activateCachedThemes();
|
||||
}
|
||||
application.onReady(() => {
|
||||
if (!desktopManager.isDesktop) {
|
||||
this.activateCachedThemes();
|
||||
}
|
||||
});
|
||||
appState.addObserver((eventName, data) => {
|
||||
if (eventName === AppStateEvents.DesktopExtsReady) {
|
||||
this.activateCachedThemes();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async activateCachedThemes() {
|
||||
@@ -30,11 +37,6 @@ export class ThemeManager {
|
||||
}
|
||||
|
||||
registerObservers() {
|
||||
this.appState.addObserver((eventName, data) => {
|
||||
if (eventName === AppStateEvents.DesktopExtsReady) {
|
||||
this.activateCachedThemes();
|
||||
}
|
||||
});
|
||||
this.desktopManager.registerUpdateObserver((component) => {
|
||||
// Reload theme if active
|
||||
if (component.active && component.isTheme()) {
|
||||
@@ -112,10 +114,9 @@ export class ThemeManager {
|
||||
});
|
||||
return processedPayload;
|
||||
}));
|
||||
const data = JSON.stringify(mapped);
|
||||
return this.application.setValue(
|
||||
CACHED_THEMES_KEY,
|
||||
data,
|
||||
mapped,
|
||||
StorageValueModes.Nonwrapped
|
||||
);
|
||||
}
|
||||
@@ -133,10 +134,15 @@ export class ThemeManager {
|
||||
StorageValueModes.Nonwrapped
|
||||
);
|
||||
if (cachedThemes) {
|
||||
const parsed = JSON.parse(cachedThemes);
|
||||
return parsed.map((theme) => {
|
||||
return new SNTheme(theme);
|
||||
});
|
||||
const themes = [];
|
||||
for(const cachedTheme of cachedThemes) {
|
||||
const theme = await this.application.createItem({
|
||||
contentType: ContentTypes.Theme,
|
||||
content: cachedTheme.content
|
||||
});
|
||||
themes.push(theme);
|
||||
}
|
||||
return themes;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ export const EventSources = {
|
||||
};
|
||||
|
||||
export class AppState {
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
$timeout,
|
||||
@@ -81,6 +80,11 @@ export class AppState {
|
||||
});
|
||||
}
|
||||
|
||||
async setApplicationReady() {
|
||||
this.applicationReady = true;
|
||||
await this.notifyEvent(AppStateEvents.ApplicationReady);
|
||||
}
|
||||
|
||||
setSelectedTag(tag) {
|
||||
if (this.selectedTag === tag) {
|
||||
return;
|
||||
|
||||
@@ -1,14 +1,40 @@
|
||||
import { DeviceInterface } from 'snjs';
|
||||
import { DeviceInterface, getGlobalScope } from 'snjs';
|
||||
import { Database } from '@/database';
|
||||
|
||||
const KEYCHAIN_STORAGE_KEY = 'keychain';
|
||||
|
||||
export class WebDeviceInterface extends DeviceInterface {
|
||||
|
||||
constructor({
|
||||
namespace,
|
||||
} = {}) {
|
||||
super({
|
||||
namespace,
|
||||
timeout: setTimeout.bind(getGlobalScope()),
|
||||
interval: setInterval.bind(getGlobalScope())
|
||||
});
|
||||
this.createDatabase();
|
||||
}
|
||||
|
||||
createDatabase() {
|
||||
this.database = new Database();
|
||||
}
|
||||
|
||||
setApplication(application) {
|
||||
this.database.setApplication(application);
|
||||
}
|
||||
|
||||
/**
|
||||
* @value storage
|
||||
*/
|
||||
|
||||
async getRawStorageValue(key) {
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
|
||||
async getAllRawStorageKeyValues() {
|
||||
const results = [];
|
||||
for(const key of Object.keys(localStorage)) {
|
||||
for (const key of Object.keys(localStorage)) {
|
||||
results.push({
|
||||
key: key,
|
||||
value: localStorage[key]
|
||||
@@ -29,6 +55,78 @@ export class WebDeviceInterface extends DeviceInterface {
|
||||
localStorage.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @database
|
||||
*/
|
||||
|
||||
async openDatabase() {
|
||||
this.database.setLocked(false);
|
||||
this.database.openDatabase({
|
||||
onUpgradeNeeded: () => {
|
||||
/**
|
||||
* New database/database wiped, delete syncToken so that items
|
||||
* can be refetched entirely from server
|
||||
*/
|
||||
/** @todo notify parent */
|
||||
// this.syncManager.clearSyncPositionTokens();
|
||||
// this.sync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** @private */
|
||||
getDatabaseKeyPrefix() {
|
||||
if (this.namespace) {
|
||||
return `${this.namespace}-item-`;
|
||||
} else {
|
||||
return `item-`;
|
||||
}
|
||||
}
|
||||
|
||||
/** @private */
|
||||
keyForPayloadId(id) {
|
||||
return `${this.getDatabaseKeyPrefix()}${id}`;
|
||||
}
|
||||
|
||||
async getAllRawDatabasePayloads() {
|
||||
return this.database.getAllPayloads();
|
||||
}
|
||||
|
||||
async saveRawDatabasePayload(payload) {
|
||||
return this.database.savePayload(payload);
|
||||
}
|
||||
|
||||
async saveRawDatabasePayloads(payloads) {
|
||||
return this.database.savePayloads(payloads);
|
||||
}
|
||||
|
||||
async removeRawDatabasePayloadWithId(id) {
|
||||
return this.database.deletePayload(id);
|
||||
}
|
||||
|
||||
async removeAllRawDatabasePayloads() {
|
||||
return this.database.clearAllPayloads();
|
||||
}
|
||||
|
||||
/** @keychian */
|
||||
async getRawKeychainValue() {
|
||||
const value = localStorage.getItem(KEYCHAIN_STORAGE_KEY);
|
||||
if(value) {
|
||||
return JSON.parse(value);
|
||||
}
|
||||
}
|
||||
|
||||
async setKeychainValue(value) {
|
||||
localStorage.setItem(KEYCHAIN_STORAGE_KEY, JSON.stringify(value));
|
||||
}
|
||||
|
||||
async clearKeychainValue() {
|
||||
localStorage.removeItem(KEYCHAIN_STORAGE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @actions
|
||||
*/
|
||||
openUrl(url) {
|
||||
const win = window.open(url, '_blank');
|
||||
if (win) {
|
||||
@@ -36,85 +134,4 @@ export class WebDeviceInterface extends DeviceInterface {
|
||||
}
|
||||
}
|
||||
|
||||
/** @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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
)
|
||||
.sk-app-bar-item-column
|
||||
.sk-circle.small(
|
||||
ng-class="ctrl.error ? 'danger' : (ctrl.getUser() ? 'info' : 'neutral')"
|
||||
ng-class="ctrl.error ? 'danger' : (ctrl.user ? 'info' : 'neutral')"
|
||||
)
|
||||
.sk-app-bar-item-column
|
||||
.sk-label.title(ng-class='{red: ctrl.error}') Account
|
||||
@@ -84,10 +84,10 @@
|
||||
elem-ready='ctrl.initSvgForShortcut(shortcut)',
|
||||
ng-attr-id='dock-svg-{{shortcut.component.uuid}}'
|
||||
)
|
||||
.sk-app-bar-item.border(ng-if='ctrl.hasPasscode()')
|
||||
.sk-app-bar-item.border(ng-if='ctrl.hasPasscode')
|
||||
#lock-item.sk-app-bar-item(
|
||||
ng-click='ctrl.lockApp()',
|
||||
ng-if='ctrl.hasPasscode()',
|
||||
ng-if='ctrl.hasPasscode',
|
||||
title='Locks application and wipes unencrypted data from memory.'
|
||||
)
|
||||
.sk-label
|
||||
|
||||
2933
dist/javascripts/app.js
vendored
2933
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
Reference in New Issue
Block a user