Compiles
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
"rules": {
|
||||
"standard/no-callback-literal": 0, // Disable this as we have too many callbacks relying on literals
|
||||
"no-throw-literal": 0,
|
||||
"no-console": "error",
|
||||
// "no-console": "error",
|
||||
"semi": 1
|
||||
},
|
||||
"env": {
|
||||
|
||||
@@ -54,24 +54,13 @@ import {
|
||||
import { trusted } from './filters';
|
||||
|
||||
import {
|
||||
ActionsManager,
|
||||
ArchiveManager,
|
||||
AuthManager,
|
||||
ComponentManager,
|
||||
DatabaseManager,
|
||||
DesktopManager,
|
||||
HttpManager,
|
||||
KeyboardManager,
|
||||
MigrationManager,
|
||||
ModelManager,
|
||||
NativeExtManager,
|
||||
LockManager,
|
||||
PrivilegesManager,
|
||||
SessionHistory,
|
||||
SingletonManager,
|
||||
StatusManager,
|
||||
StorageManager,
|
||||
SyncManager,
|
||||
ThemeManager,
|
||||
AlertManager,
|
||||
PreferencesManager
|
||||
@@ -148,23 +137,13 @@ angular
|
||||
.service('application', Application)
|
||||
.service('appState', AppState)
|
||||
.service('preferencesManager', PreferencesManager)
|
||||
.service('actionsManager', ActionsManager)
|
||||
.service('archiveManager', ArchiveManager)
|
||||
.service('authManager', AuthManager)
|
||||
.service('componentManager', ComponentManager)
|
||||
.service('databaseManager', DatabaseManager)
|
||||
.service('desktopManager', DesktopManager)
|
||||
.service('httpManager', HttpManager)
|
||||
.service('keyboardManager', KeyboardManager)
|
||||
.service('migrationManager', MigrationManager)
|
||||
.service('modelManager', ModelManager)
|
||||
.service('nativeExtManager', NativeExtManager)
|
||||
.service('lockManager', LockManager)
|
||||
.service('privilegesManager', PrivilegesManager)
|
||||
.service('sessionHistory', SessionHistory)
|
||||
.service('singletonManager', SingletonManager)
|
||||
.service('statusManager', StatusManager)
|
||||
.service('storageManager', StorageManager)
|
||||
.service('syncManager', SyncManager)
|
||||
.service('alertManager', AlertManager)
|
||||
.service('themeManager', ThemeManager);
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import {
|
||||
SNApplication,
|
||||
SNAlertManager
|
||||
Platforms,
|
||||
Environments
|
||||
SNAlertManager,
|
||||
Platforms
|
||||
} from 'snjs';
|
||||
import angular from 'angular';
|
||||
import { AlertManager } from '@/services/alertManager'
|
||||
import { AlertManager } from '@/services/alertManager';
|
||||
|
||||
import { WebDeviceInterface } from '@/web_device_interface';
|
||||
|
||||
|
||||
@@ -2,37 +2,24 @@ import angular from 'angular';
|
||||
import {
|
||||
ApplicationEvents,
|
||||
isPayloadSourceRetrieved,
|
||||
CONTENT_TYPE_NOTE,
|
||||
CONTENT_TYPE_TAG,
|
||||
CONTENT_TYPE_COMPONENT,
|
||||
ContentTypes,
|
||||
ProtectedActions
|
||||
} from 'snjs';
|
||||
import find from 'lodash/find';
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { KeyboardManager } from '@/services/keyboardManager';
|
||||
import template from '%/editor.pug';
|
||||
import { PureCtrl } from '@Controllers';
|
||||
import {
|
||||
APP_STATE_EVENT_NOTE_CHANGED,
|
||||
APP_STATE_EVENT_PREFERENCES_CHANGED,
|
||||
EVENT_SOURCE_SCRIPT
|
||||
} from '@/state';
|
||||
import { AppStateEvents, EventSources } from '@/state';
|
||||
import {
|
||||
STRING_DELETED_NOTE,
|
||||
STRING_INVALID_NOTE,
|
||||
STRING_ELLIPSES,
|
||||
STRING_GENERIC_SAVE_ERROR,
|
||||
STRING_DELETE_PLACEHOLDER_ATTEMPT,
|
||||
STRING_DELETE_LOCKED_ATTEMPT,
|
||||
StringDeleteNote,
|
||||
StringEmptyTrash
|
||||
} from '@/strings';
|
||||
import {
|
||||
PREF_EDITOR_WIDTH,
|
||||
PREF_EDITOR_LEFT,
|
||||
PREF_EDITOR_MONOSPACE_ENABLED,
|
||||
PREF_EDITOR_SPELLCHECK,
|
||||
PREF_EDITOR_RESIZERS_ENABLED
|
||||
} from '@/services/preferencesManager';
|
||||
import { PrefKeys } from '@/services/preferencesManager';
|
||||
|
||||
const NOTE_PREVIEW_CHAR_LIMIT = 80;
|
||||
const MINIMUM_STATUS_DURATION = 400;
|
||||
@@ -40,19 +27,23 @@ const SAVE_TIMEOUT_DEBOUNCE = 350;
|
||||
const SAVE_TIMEOUT_NO_DEBOUNCE = 100;
|
||||
const EDITOR_DEBOUNCE = 200;
|
||||
|
||||
const APP_DATA_KEY_PINNED = 'pinned';
|
||||
const APP_DATA_KEY_LOCKED = 'locked';
|
||||
const APP_DATA_KEY_ARCHIVED = 'archived';
|
||||
const APP_DATA_KEY_PREFERS_PLAIN_EDITOR = 'prefersPlainEditor';
|
||||
|
||||
const ELEMENT_ID_NOTE_TEXT_EDITOR = 'note-text-editor';
|
||||
const ELEMENT_ID_NOTE_TITLE_EDITOR = 'note-title-editor';
|
||||
const ELEMENT_ID_EDITOR_CONTENT = 'editor-content';
|
||||
const ELEMENT_ID_NOTE_TAGS_COMPONENT_CONTAINER = 'note-tags-component-container';
|
||||
|
||||
const DESKTOP_MONOSPACE_FAMILY = `Menlo,Consolas,'DejaVu Sans Mono',monospace`;
|
||||
const WEB_MONOSPACE_FAMILY = `monospace`;
|
||||
const SANS_SERIF_FAMILY = `inherit`;
|
||||
const AppDataKeys = {
|
||||
Pinned: 'pinned',
|
||||
Locked: 'locked',
|
||||
Archived: 'archived',
|
||||
PrefersPlainEditor: 'prefersPlainEditor'
|
||||
};
|
||||
const ElementIds = {
|
||||
NoteTextEditor: 'note-text-editor',
|
||||
NoteTitleEditor: 'note-title-editor',
|
||||
EditorContent: 'editor-content',
|
||||
NoteTagsComponentContainer: 'note-tags-component-container'
|
||||
};
|
||||
const Fonts = {
|
||||
DesktopMonospaceFamily: `Menlo,Consolas,'DejaVu Sans Mono',monospace`,
|
||||
WebMonospaceFamily: `monospace`,
|
||||
SansSerifFamily: `inherit`
|
||||
};
|
||||
|
||||
class EditorCtrl extends PureCtrl {
|
||||
/* @ngInject */
|
||||
@@ -61,17 +52,14 @@ class EditorCtrl extends PureCtrl {
|
||||
$rootScope,
|
||||
appState,
|
||||
application,
|
||||
actionsManager,
|
||||
desktopManager,
|
||||
keyboardManager,
|
||||
preferencesManager,
|
||||
sessionHistory /** Unused below, required to load globally */
|
||||
) {
|
||||
super($timeout);
|
||||
this.$rootScope = $rootScope;
|
||||
this.application = application;
|
||||
this.appState = appState;
|
||||
this.actionsManager = actionsManager;
|
||||
this.desktopManager = desktopManager;
|
||||
this.keyboardManager = keyboardManager;
|
||||
this.preferencesManager = preferencesManager;
|
||||
@@ -97,19 +85,19 @@ class EditorCtrl extends PureCtrl {
|
||||
this.registerKeyboardShortcuts();
|
||||
|
||||
/** Used by .pug template */
|
||||
this.prefKeyMonospace = PREF_EDITOR_MONOSPACE_ENABLED;
|
||||
this.prefKeySpellcheck = PREF_EDITOR_SPELLCHECK;
|
||||
this.prefKeyMarginResizers = PREF_EDITOR_RESIZERS_ENABLED;
|
||||
this.prefKeyMonospace = PrefKeys.EditorMonospaceEnabled;
|
||||
this.prefKeySpellcheck = PrefKeys.EditorSpellcheck;
|
||||
this.prefKeyMarginResizers = PrefKeys.EditorResizersEnabled;
|
||||
}
|
||||
|
||||
addAppStateObserver() {
|
||||
this.appState.addObserver((eventName, data) => {
|
||||
if (eventName === APP_STATE_EVENT_NOTE_CHANGED) {
|
||||
if (eventName === AppStateEvents.NoteChanged) {
|
||||
this.handleNoteSelectionChange(
|
||||
this.appState.getSelectedNote(),
|
||||
data.previousNote
|
||||
);
|
||||
} else if (eventName === APP_STATE_EVENT_PREFERENCES_CHANGED) {
|
||||
} else if (eventName === AppStateEvents.PreferencesChanged) {
|
||||
this.loadPreferences();
|
||||
}
|
||||
});
|
||||
@@ -117,7 +105,7 @@ class EditorCtrl extends PureCtrl {
|
||||
|
||||
streamItems() {
|
||||
this.application.streamItems({
|
||||
contentType: CONTENT_TYPE_NOTE,
|
||||
contentType: ContentTypes.Note,
|
||||
stream: async ({ items, source }) => {
|
||||
if (!this.state.note) {
|
||||
return;
|
||||
@@ -139,7 +127,7 @@ class EditorCtrl extends PureCtrl {
|
||||
});
|
||||
|
||||
this.application.streamItems({
|
||||
contentType: CONTENT_TYPE_TAG,
|
||||
contentType: ContentTypes.Tag,
|
||||
stream: async ({ items, source }) => {
|
||||
if (!this.state.note) {
|
||||
return;
|
||||
@@ -158,7 +146,7 @@ class EditorCtrl extends PureCtrl {
|
||||
});
|
||||
|
||||
this.application.streamItems({
|
||||
contentType: CONTENT_TYPE_COMPONENT,
|
||||
contentType: ContentTypes.Component,
|
||||
stream: async ({ items, source }) => {
|
||||
if (!this.state.note) {
|
||||
return;
|
||||
@@ -267,7 +255,7 @@ class EditorCtrl extends PureCtrl {
|
||||
|
||||
addSyncStatusObserver() {
|
||||
/** @todo */
|
||||
// this.syncStatusObserver = this.syncManager.
|
||||
// this.syncStatusObserver = syncManager.
|
||||
// registerSyncStatusObserver((status) => {
|
||||
// if (status.localError) {
|
||||
// this.$timeout(() => {
|
||||
@@ -321,11 +309,11 @@ class EditorCtrl extends PureCtrl {
|
||||
}
|
||||
if (editor) {
|
||||
const prefersPlain = this.state.note.getAppDataItem(
|
||||
APP_DATA_KEY_PREFERS_PLAIN_EDITOR
|
||||
AppDataKeys.PrefersPlainEditor
|
||||
) === true;
|
||||
if (prefersPlain) {
|
||||
this.state.note.setAppDataItem(
|
||||
APP_DATA_KEY_PREFERS_PLAIN_EDITOR,
|
||||
AppDataKeys.PrefersPlainEditor,
|
||||
false
|
||||
);
|
||||
this.application.setItemNeedsSync({ item: this.state.note });
|
||||
@@ -333,9 +321,9 @@ class EditorCtrl extends PureCtrl {
|
||||
this.associateComponentWithCurrentNote(editor);
|
||||
} else {
|
||||
/** Note prefers plain editor */
|
||||
if (!this.state.note.getAppDataItem(APP_DATA_KEY_PREFERS_PLAIN_EDITOR)) {
|
||||
if (!this.state.note.getAppDataItem(AppDataKeys.PrefersPlainEditor)) {
|
||||
this.state.note.setAppDataItem(
|
||||
APP_DATA_KEY_PREFERS_PLAIN_EDITOR,
|
||||
AppDataKeys.PrefersPlainEditor,
|
||||
true
|
||||
);
|
||||
this.application.setItemNeedsSync({ item: this.state.note });
|
||||
@@ -356,7 +344,7 @@ class EditorCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
hasAvailableExtensions() {
|
||||
return this.actionsManager.extensionsInContextOfItem(this.state.note).length > 0;
|
||||
return this.application.actionsManager.extensionsInContextOfItem(this.state.note).length > 0;
|
||||
}
|
||||
|
||||
performFirefoxPinnedTabFix() {
|
||||
@@ -494,15 +482,15 @@ class EditorCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
focusEditor() {
|
||||
const element = document.getElementById(ELEMENT_ID_NOTE_TEXT_EDITOR);
|
||||
const element = document.getElementById(ElementIds.NoteTextEditor);
|
||||
if (element) {
|
||||
this.lastEditorFocusEventSource = EVENT_SOURCE_SCRIPT;
|
||||
this.lastEditorFocusEventSource = EventSources.Script;
|
||||
element.focus();
|
||||
}
|
||||
}
|
||||
|
||||
focusTitle() {
|
||||
document.getElementById(ELEMENT_ID_NOTE_TITLE_EDITOR).focus();
|
||||
document.getElementById(ElementIds.NoteTitleEditor).focus();
|
||||
}
|
||||
|
||||
clickedTextArea() {
|
||||
@@ -627,7 +615,7 @@ class EditorCtrl extends PureCtrl {
|
||||
|
||||
togglePin() {
|
||||
this.state.note.setAppDataItem(
|
||||
APP_DATA_KEY_PINNED,
|
||||
AppDataKeys.Pinned,
|
||||
!this.state.note.pinned
|
||||
);
|
||||
this.saveNote({
|
||||
@@ -638,7 +626,7 @@ class EditorCtrl extends PureCtrl {
|
||||
|
||||
toggleLockNote() {
|
||||
this.state.note.setAppDataItem(
|
||||
APP_DATA_KEY_LOCKED,
|
||||
AppDataKeys.Locked,
|
||||
!this.state.note.locked
|
||||
);
|
||||
this.saveNote({
|
||||
@@ -674,7 +662,7 @@ class EditorCtrl extends PureCtrl {
|
||||
|
||||
toggleArchiveNote() {
|
||||
this.state.note.setAppDataItem(
|
||||
APP_DATA_KEY_ARCHIVED,
|
||||
AppDataKeys.Archived,
|
||||
!this.state.note.archived
|
||||
);
|
||||
this.saveNote({
|
||||
@@ -734,7 +722,7 @@ class EditorCtrl extends PureCtrl {
|
||||
this.application.setItemsNeedsSync({ items: toRemove });
|
||||
const tags = [];
|
||||
for (const tagString of strings) {
|
||||
const existingRelationship = _.find(
|
||||
const existingRelationship = find(
|
||||
this.state.note.tags,
|
||||
{ title: tagString }
|
||||
);
|
||||
@@ -754,13 +742,13 @@ class EditorCtrl extends PureCtrl {
|
||||
onPanelResizeFinish = (width, left, isMaxWidth) => {
|
||||
if (isMaxWidth) {
|
||||
this.preferencesManager.setUserPrefValue(
|
||||
PREF_EDITOR_WIDTH,
|
||||
PrefKeys.EditorWidth,
|
||||
null
|
||||
);
|
||||
} else {
|
||||
if (width !== undefined && width !== null) {
|
||||
this.preferencesManager.setUserPrefValue(
|
||||
PREF_EDITOR_WIDTH,
|
||||
PrefKeys.EditorWidth,
|
||||
width
|
||||
);
|
||||
this.leftResizeControl.setWidth(width);
|
||||
@@ -768,7 +756,7 @@ class EditorCtrl extends PureCtrl {
|
||||
}
|
||||
if (left !== undefined && left !== null) {
|
||||
this.preferencesManager.setUserPrefValue(
|
||||
PREF_EDITOR_LEFT,
|
||||
PrefKeys.EditorLeft,
|
||||
left
|
||||
);
|
||||
this.rightResizeControl.setLeft(left);
|
||||
@@ -778,15 +766,15 @@ class EditorCtrl extends PureCtrl {
|
||||
|
||||
loadPreferences() {
|
||||
const monospaceEnabled = this.preferencesManager.getValue(
|
||||
PREF_EDITOR_MONOSPACE_ENABLED,
|
||||
PrefKeys.EditorMonospaceEnabled,
|
||||
true
|
||||
);
|
||||
const spellcheck = this.preferencesManager.getValue(
|
||||
PREF_EDITOR_SPELLCHECK,
|
||||
PrefKeys.EditorSpellcheck,
|
||||
true
|
||||
);
|
||||
const marginResizersEnabled = this.preferencesManager.getValue(
|
||||
PREF_EDITOR_RESIZERS_ENABLED,
|
||||
PrefKeys.EditorResizersEnabled,
|
||||
true
|
||||
);
|
||||
this.setState({
|
||||
@@ -795,7 +783,7 @@ class EditorCtrl extends PureCtrl {
|
||||
marginResizersEnabled
|
||||
});
|
||||
|
||||
if (!document.getElementById(ELEMENT_ID_EDITOR_CONTENT)) {
|
||||
if (!document.getElementById(ElementIds.EditorContent)) {
|
||||
/** Elements have not yet loaded due to ng-if around wrapper */
|
||||
return;
|
||||
}
|
||||
@@ -804,7 +792,7 @@ class EditorCtrl extends PureCtrl {
|
||||
|
||||
if (this.state.marginResizersEnabled) {
|
||||
const width = this.preferencesManager.getValue(
|
||||
PREF_EDITOR_WIDTH,
|
||||
PrefKeys.EditorWidth,
|
||||
null
|
||||
);
|
||||
if (width != null) {
|
||||
@@ -812,7 +800,7 @@ class EditorCtrl extends PureCtrl {
|
||||
this.rightResizeControl.setWidth(width);
|
||||
}
|
||||
const left = this.preferencesManager.getValue(
|
||||
PREF_EDITOR_LEFT,
|
||||
PrefKeys.EditorLeft,
|
||||
null
|
||||
);
|
||||
if (left != null) {
|
||||
@@ -824,19 +812,19 @@ class EditorCtrl extends PureCtrl {
|
||||
|
||||
reloadFont() {
|
||||
const editor = document.getElementById(
|
||||
ELEMENT_ID_NOTE_TEXT_EDITOR
|
||||
ElementIds.NoteTextEditor
|
||||
);
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
if (this.state.monospaceEnabled) {
|
||||
if (this.state.isDesktop) {
|
||||
editor.style.fontFamily = DESKTOP_MONOSPACE_FAMILY;
|
||||
editor.style.fontFamily = Fonts.DesktopMonospaceFamily;
|
||||
} else {
|
||||
editor.style.fontFamily = WEB_MONOSPACE_FAMILY;
|
||||
editor.style.fontFamily = Fonts.WebMonospaceFamily;
|
||||
}
|
||||
} else {
|
||||
editor.style.fontFamily = SANS_SERIF_FAMILY;
|
||||
editor.style.fontFamily = Fonts.SansSerifFamily;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -849,7 +837,7 @@ class EditorCtrl extends PureCtrl {
|
||||
);
|
||||
this.reloadFont();
|
||||
|
||||
if (key === PREF_EDITOR_SPELLCHECK) {
|
||||
if (key === PrefKeys.EditorSpellcheck) {
|
||||
/** Allows textarea to reload */
|
||||
await this.setState({
|
||||
noteReady: false
|
||||
@@ -858,7 +846,7 @@ class EditorCtrl extends PureCtrl {
|
||||
noteReady: true
|
||||
});
|
||||
this.reloadFont();
|
||||
} else if (key === PREF_EDITOR_RESIZERS_ENABLED && this[key] === true) {
|
||||
} else if (key === PrefKeys.EditorResizersEnabled && this[key] === true) {
|
||||
this.$timeout(() => {
|
||||
this.leftResizeControl.flash();
|
||||
this.rightResizeControl.flash();
|
||||
@@ -956,7 +944,7 @@ class EditorCtrl extends PureCtrl {
|
||||
if (data.type === 'container') {
|
||||
if (component.area === 'note-tags') {
|
||||
const container = document.getElementById(
|
||||
ELEMENT_ID_NOTE_TAGS_COMPONENT_CONTAINER
|
||||
ElementIds.NoteTagsComponentContainer
|
||||
);
|
||||
setSize(container, data);
|
||||
}
|
||||
@@ -1055,7 +1043,7 @@ class EditorCtrl extends PureCtrl {
|
||||
registerKeyboardShortcuts() {
|
||||
this.altKeyObserver = this.keyboardManager.addKeyObserver({
|
||||
modifiers: [
|
||||
KeyboardManager.KeyModifierAlt
|
||||
KeyboardModifiers.Alt
|
||||
],
|
||||
onKeyDown: () => {
|
||||
this.setState({
|
||||
@@ -1070,23 +1058,23 @@ class EditorCtrl extends PureCtrl {
|
||||
});
|
||||
|
||||
this.trashKeyObserver = this.keyboardManager.addKeyObserver({
|
||||
key: KeyboardManager.KeyBackspace,
|
||||
key: KeyboardKeys.Backspace,
|
||||
notElementIds: [
|
||||
ELEMENT_ID_NOTE_TEXT_EDITOR,
|
||||
ELEMENT_ID_NOTE_TITLE_EDITOR
|
||||
ElementIds.NoteTextEditor,
|
||||
ElementIds.NoteTitleEditor
|
||||
],
|
||||
modifiers: [KeyboardManager.KeyModifierMeta],
|
||||
modifiers: [KeyboardModifiers.Meta],
|
||||
onKeyDown: () => {
|
||||
this.deleteNote();
|
||||
},
|
||||
});
|
||||
|
||||
this.deleteKeyObserver = this.keyboardManager.addKeyObserver({
|
||||
key: KeyboardManager.KeyBackspace,
|
||||
key: KeyboardKeys.Backspace,
|
||||
modifiers: [
|
||||
KeyboardManager.KeyModifierMeta,
|
||||
KeyboardManager.KeyModifierShift,
|
||||
KeyboardManager.KeyModifierAlt
|
||||
KeyboardModifiers.Meta,
|
||||
KeyboardModifiers.Shift,
|
||||
KeyboardModifiers.Alt
|
||||
],
|
||||
onKeyDown: (event) => {
|
||||
event.preventDefault();
|
||||
@@ -1107,11 +1095,11 @@ class EditorCtrl extends PureCtrl {
|
||||
* not fired.
|
||||
*/
|
||||
const editor = document.getElementById(
|
||||
ELEMENT_ID_NOTE_TEXT_EDITOR
|
||||
ElementIds.NoteTextEditor
|
||||
);
|
||||
this.tabObserver = this.keyboardManager.addKeyObserver({
|
||||
element: editor,
|
||||
key: KeyboardManager.KeyTab,
|
||||
key: KeyboardKeys.Tab,
|
||||
onKeyDown: (event) => {
|
||||
if (this.state.note.locked || event.shiftKey) {
|
||||
return;
|
||||
|
||||
@@ -2,15 +2,11 @@ import { dateToLocalizedString } from '@/utils';
|
||||
import {
|
||||
ApplicationEvents,
|
||||
TIMING_STRATEGY_FORCE_SPAWN_NEW,
|
||||
ProtectedActions
|
||||
ProtectedActions,
|
||||
ContentTypes
|
||||
} from 'snjs';
|
||||
import template from '%/footer.pug';
|
||||
import {
|
||||
APP_STATE_EVENT_EDITOR_FOCUSED,
|
||||
APP_STATE_EVENT_BEGAN_BACKUP_DOWNLOAD,
|
||||
APP_STATE_EVENT_ENDED_BACKUP_DOWNLOAD,
|
||||
EVENT_SOURCE_USER_INTERACTION
|
||||
} from '@/state';
|
||||
import { AppStateEvents, EventSources } from '@/state';
|
||||
import {
|
||||
STRING_GENERIC_SYNC_ERROR,
|
||||
STRING_NEW_UPDATE_READY
|
||||
@@ -74,16 +70,16 @@ class FooterCtrl {
|
||||
|
||||
addAppStateObserver() {
|
||||
this.appState.addObserver((eventName, data) => {
|
||||
if(eventName === APP_STATE_EVENT_EDITOR_FOCUSED) {
|
||||
if (data.eventSource === EVENT_SOURCE_USER_INTERACTION) {
|
||||
if(eventName === AppStateEvents.EditorFocused) {
|
||||
if (data.eventSource === EventSources.UserInteraction) {
|
||||
this.closeAllRooms();
|
||||
this.closeAccountMenu();
|
||||
}
|
||||
} else if(eventName === APP_STATE_EVENT_BEGAN_BACKUP_DOWNLOAD) {
|
||||
} else if(eventName === AppStateEvents.BeganBackupDownload) {
|
||||
this.backupStatus = this.statusManager.addStatusFromString(
|
||||
"Saving local backup..."
|
||||
);
|
||||
} else if(eventName === APP_STATE_EVENT_ENDED_BACKUP_DOWNLOAD) {
|
||||
} else if(eventName === AppStateEvents.EndedBackupDownload) {
|
||||
if(data.success) {
|
||||
this.backupStatus = this.statusManager.replaceStatusWithString(
|
||||
this.backupStatus,
|
||||
@@ -125,10 +121,10 @@ class FooterCtrl {
|
||||
|
||||
streamItems() {
|
||||
this.application.streamItems({
|
||||
contentType: CONTENT_TYPE_COMPONENT,
|
||||
contentType: ContentTypes.Component,
|
||||
stream: async () => {
|
||||
this.rooms = this.application.getItems({
|
||||
contentType: CONTENT_TYPE_COMPONENT
|
||||
contentType: ContentTypes.Component
|
||||
}).filter((candidate) => {
|
||||
return candidate.area === 'rooms' && !candidate.deleted;
|
||||
});
|
||||
@@ -143,7 +139,7 @@ class FooterCtrl {
|
||||
contentType: 'SN|Theme',
|
||||
stream: async () => {
|
||||
const themes = this.application.getDisplayableItems({
|
||||
contentType: CONTENT_TYPE_THEME
|
||||
contentType: ContentTypes.Theme
|
||||
}).filter((candidate) => {
|
||||
return (
|
||||
!candidate.deleted &&
|
||||
@@ -159,7 +155,7 @@ class FooterCtrl {
|
||||
this.reloadDockShortcuts();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
registerComponentHandler() {
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
import template from '%/lock-screen.pug';
|
||||
import {
|
||||
APP_STATE_EVENT_WINDOW_DID_FOCUS
|
||||
} from '@/state';
|
||||
import { AppStateEvents } from '@/state';
|
||||
|
||||
const ELEMENT_ID_PASSCODE_INPUT = 'passcode-input';
|
||||
|
||||
class LockScreenCtrl {
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
$scope,
|
||||
alertManager,
|
||||
application,
|
||||
appState
|
||||
) {
|
||||
this.$scope = $scope;
|
||||
this.alertManager = alertManager;
|
||||
this.application = application;
|
||||
this.appState = appState;
|
||||
this.formData = {};
|
||||
@@ -37,7 +32,7 @@ class LockScreenCtrl {
|
||||
|
||||
addVisibilityObserver() {
|
||||
this.unregisterObserver = this.appState.addObserver((eventName, data) => {
|
||||
if (eventName === APP_STATE_EVENT_WINDOW_DID_FOCUS) {
|
||||
if (eventName === AppStateEvents.WindowDidFocus) {
|
||||
const input = this.passcodeInput;
|
||||
if(input) {
|
||||
input.focus();
|
||||
@@ -56,7 +51,7 @@ class LockScreenCtrl {
|
||||
this.passcodeInput.blur();
|
||||
const success = await this.onValue()(this.formData.passcode);
|
||||
if(!success) {
|
||||
this.alertManager.alert({
|
||||
this.application.alertManager.alert({
|
||||
text: "Invalid passcode. Please try again.",
|
||||
onClose: () => {
|
||||
this.passcodeInput.focus();
|
||||
@@ -70,7 +65,7 @@ class LockScreenCtrl {
|
||||
}
|
||||
|
||||
beginDeleteData() {
|
||||
this.alertManager.confirm({
|
||||
this.application.alertManager.confirm({
|
||||
text: "Are you sure you want to clear all local data?",
|
||||
destructive: true,
|
||||
onConfirm: async () => {
|
||||
|
||||
@@ -1,24 +1,11 @@
|
||||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
import template from '%/notes.pug';
|
||||
import { ApplicationEvents, CONTENT_TYPE_NOTE, CONTENT_TYPE_TAG } from 'snjs';
|
||||
import { KeyboardManager } from '@/services/keyboardManager';
|
||||
import { ApplicationEvents, ContentTypes } from 'snjs';
|
||||
import { PureCtrl } from '@Controllers';
|
||||
import { AppStateEvents } from '@/state';
|
||||
import {
|
||||
APP_STATE_EVENT_NOTE_CHANGED,
|
||||
APP_STATE_EVENT_TAG_CHANGED,
|
||||
APP_STATE_EVENT_PREFERENCES_CHANGED,
|
||||
APP_STATE_EVENT_EDITOR_FOCUSED
|
||||
} from '@/state';
|
||||
import {
|
||||
PREF_NOTES_PANEL_WIDTH,
|
||||
PREF_SORT_NOTES_BY,
|
||||
PREF_SORT_NOTES_REVERSE,
|
||||
PREF_NOTES_SHOW_ARCHIVED,
|
||||
PREF_NOTES_HIDE_PINNED,
|
||||
PREF_NOTES_HIDE_NOTE_PREVIEW,
|
||||
PREF_NOTES_HIDE_DATE,
|
||||
PREF_NOTES_HIDE_TAGS
|
||||
PrefKeys
|
||||
} from '@/services/preferencesManager';
|
||||
import {
|
||||
PANEL_NAME_NOTES
|
||||
@@ -37,8 +24,6 @@ import {
|
||||
*/
|
||||
const MIN_NOTE_CELL_HEIGHT = 51.0;
|
||||
const DEFAULT_LIST_NUM_NOTES = 20;
|
||||
|
||||
|
||||
const ELEMENT_ID_SEARCH_BAR = 'search-bar';
|
||||
const ELEMENT_ID_SCROLL_CONTAINER = 'notes-scrollable';
|
||||
|
||||
@@ -96,14 +81,14 @@ class NotesCtrl extends PureCtrl {
|
||||
|
||||
addAppStateObserver() {
|
||||
this.appState.addObserver((eventName, data) => {
|
||||
if (eventName === APP_STATE_EVENT_TAG_CHANGED) {
|
||||
if (eventName === AppStateEvents.TagChanged) {
|
||||
this.handleTagChange(this.appState.getSelectedTag(), data.previousTag);
|
||||
} else if (eventName === APP_STATE_EVENT_NOTE_CHANGED) {
|
||||
} else if (eventName === AppStateEvents.NoteChanged) {
|
||||
this.handleNoteSelection(this.appState.getSelectedNote());
|
||||
} else if (eventName === APP_STATE_EVENT_PREFERENCES_CHANGED) {
|
||||
} else if (eventName === AppStateEvents.PreferencesChanged) {
|
||||
this.reloadPreferences();
|
||||
this.reloadNotes();
|
||||
} else if (eventName === APP_STATE_EVENT_EDITOR_FOCUSED) {
|
||||
} else if (eventName === AppStateEvents.EditorFocused) {
|
||||
this.setShowMenuFalse();
|
||||
}
|
||||
});
|
||||
@@ -129,7 +114,7 @@ class NotesCtrl extends PureCtrl {
|
||||
if (this.state.notes.length === 0) {
|
||||
this.createNewNote();
|
||||
}
|
||||
} else if(eventName === ApplicationEvents.CompletedSync) {
|
||||
} else if (eventName === ApplicationEvents.CompletedSync) {
|
||||
if (this.createDummyOnSynCompletionIfNoNotes && this.state.notes.length === 0) {
|
||||
this.createDummyOnSynCompletionIfNoNotes = false;
|
||||
this.createNewNote();
|
||||
@@ -140,7 +125,7 @@ class NotesCtrl extends PureCtrl {
|
||||
|
||||
streamNotesAndTags() {
|
||||
this.application.streamItems({
|
||||
contentType: [CONTENT_TYPE_NOTE, CONTENT_TYPE_TAG],
|
||||
contentType: [ContentTypes.Note, ContentTypes.Tag],
|
||||
stream: async ({ items }) => {
|
||||
await this.reloadNotes();
|
||||
const selectedNote = this.state.selectedNote;
|
||||
@@ -155,7 +140,7 @@ class NotesCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
/** Note has changed values, reset its flags */
|
||||
const notes = items.filter((item) => item.content_type === CONTENT_TYPE_NOTE);
|
||||
const notes = items.filter((item) => item.content_type === ContentTypes.Note);
|
||||
for (const note of notes) {
|
||||
this.loadFlagsForNote(note);
|
||||
note.cachedCreatedAtString = note.createdAtString();
|
||||
@@ -167,7 +152,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.removeItemLocally({ item: this.state.selectedNote });
|
||||
if (previousTag) {
|
||||
_.remove(previousTag.notes, this.state.selectedNote);
|
||||
}
|
||||
@@ -266,7 +251,7 @@ class NotesCtrl extends PureCtrl {
|
||||
}
|
||||
const previousNote = this.state.selectedNote;
|
||||
if (previousNote && previousNote.dummy) {
|
||||
this.application.removeItemLocally({previousNote});
|
||||
this.application.removeItemLocally({ previousNote });
|
||||
this.removeNoteFromList(previousNote);
|
||||
}
|
||||
await this.setState({
|
||||
@@ -279,7 +264,7 @@ class NotesCtrl extends PureCtrl {
|
||||
this.selectedIndex = Math.max(0, this.displayableNotes().indexOf(note));
|
||||
if (note.content.conflict_of) {
|
||||
note.content.conflict_of = null;
|
||||
this.application.saveItem({item: note});
|
||||
this.application.saveItem({ item: note });
|
||||
}
|
||||
if (this.isFiltering()) {
|
||||
this.desktopManager.searchText(this.state.noteFilter.text);
|
||||
@@ -290,7 +275,7 @@ class NotesCtrl extends PureCtrl {
|
||||
const viewOptions = {};
|
||||
const prevSortValue = this.state.sortBy;
|
||||
let sortBy = this.preferencesManager.getValue(
|
||||
PREF_SORT_NOTES_BY,
|
||||
PrefKeys.SortNotesBy,
|
||||
SORT_KEY_CREATED_AT
|
||||
);
|
||||
if (sortBy === SORT_KEY_UPDATED_AT) {
|
||||
@@ -299,27 +284,27 @@ class NotesCtrl extends PureCtrl {
|
||||
}
|
||||
viewOptions.sortBy = sortBy;
|
||||
viewOptions.sortReverse = this.preferencesManager.getValue(
|
||||
PREF_SORT_NOTES_REVERSE,
|
||||
PrefKeys.SortNotesReverse,
|
||||
false
|
||||
);
|
||||
viewOptions.showArchived = this.preferencesManager.getValue(
|
||||
PREF_NOTES_SHOW_ARCHIVED,
|
||||
PrefKeys.NotesShowArchived,
|
||||
false
|
||||
);
|
||||
viewOptions.hidePinned = this.preferencesManager.getValue(
|
||||
PREF_NOTES_HIDE_PINNED,
|
||||
PrefKeys.NotesHidePinned,
|
||||
false
|
||||
);
|
||||
viewOptions.hideNotePreview = this.preferencesManager.getValue(
|
||||
PREF_NOTES_HIDE_NOTE_PREVIEW,
|
||||
PrefKeys.NotesHideNotePreview,
|
||||
false
|
||||
);
|
||||
viewOptions.hideDate = this.preferencesManager.getValue(
|
||||
PREF_NOTES_HIDE_DATE,
|
||||
PrefKeys.NotesHideDate,
|
||||
false
|
||||
);
|
||||
viewOptions.hideTags = this.preferencesManager.getValue(
|
||||
PREF_NOTES_HIDE_TAGS,
|
||||
PrefKeys.NotesHideTags,
|
||||
false
|
||||
);
|
||||
this.setState({
|
||||
@@ -329,7 +314,7 @@ class NotesCtrl extends PureCtrl {
|
||||
this.selectFirstNote();
|
||||
}
|
||||
const width = this.preferencesManager.getValue(
|
||||
PREF_NOTES_PANEL_WIDTH
|
||||
PrefKeys.NotesPanelWidth
|
||||
);
|
||||
if (width) {
|
||||
this.panelController.setWidth(width);
|
||||
@@ -344,7 +329,7 @@ class NotesCtrl extends PureCtrl {
|
||||
|
||||
onPanelResize = (newWidth, lastLeft, isAtMaxWidth, isCollapsed) => {
|
||||
this.preferencesManager.setUserPrefValue(
|
||||
PREF_NOTES_PANEL_WIDTH,
|
||||
PrefKeys.NotesPanelWidth,
|
||||
newWidth
|
||||
);
|
||||
this.preferencesManager.syncUserPreferences();
|
||||
@@ -523,7 +508,7 @@ class NotesCtrl extends PureCtrl {
|
||||
}
|
||||
const title = "Note" + (this.state.notes ? (" " + (this.state.notes.length + 1)) : "");
|
||||
const newNote = this.application.createItem({
|
||||
contentType: CONTENT_TYPE_NOTE,
|
||||
contentType: ContentTypes.Note,
|
||||
content: {
|
||||
text: '',
|
||||
title: title
|
||||
@@ -531,7 +516,7 @@ class NotesCtrl extends PureCtrl {
|
||||
});
|
||||
newNote.client_updated_at = new Date();
|
||||
newNote.dummy = true;
|
||||
this.application.setItemNeedsSync({item: newNote});
|
||||
this.application.setItemNeedsSync({ item: newNote });
|
||||
const selectedTag = this.appState.getSelectedTag();
|
||||
if (!selectedTag.isSmartTag()) {
|
||||
selectedTag.addItemAsRelationship(newNote);
|
||||
@@ -605,7 +590,7 @@ class NotesCtrl extends PureCtrl {
|
||||
toggleReverseSort() {
|
||||
this.selectedMenuItem();
|
||||
this.preferencesManager.setUserPrefValue(
|
||||
PREF_SORT_NOTES_REVERSE,
|
||||
PrefKeys.SortNotesReverse,
|
||||
!this.state.sortReverse
|
||||
);
|
||||
this.preferencesManager.syncUserPreferences();
|
||||
@@ -613,7 +598,7 @@ class NotesCtrl extends PureCtrl {
|
||||
|
||||
setSortBy(type) {
|
||||
this.preferencesManager.setUserPrefValue(
|
||||
PREF_SORT_NOTES_BY,
|
||||
PrefKeys.SortNotesBy,
|
||||
type
|
||||
);
|
||||
this.preferencesManager.syncUserPreferences();
|
||||
@@ -649,8 +634,8 @@ class NotesCtrl extends PureCtrl {
|
||||
this.newNoteKeyObserver = this.keyboardManager.addKeyObserver({
|
||||
key: 'n',
|
||||
modifiers: [
|
||||
KeyboardManager.KeyModifierMeta,
|
||||
KeyboardManager.KeyModifierCtrl
|
||||
KeyboardModifiers.Meta,
|
||||
KeyboardModifiers.Ctrl
|
||||
],
|
||||
onKeyDown: (event) => {
|
||||
event.preventDefault();
|
||||
@@ -659,7 +644,7 @@ class NotesCtrl extends PureCtrl {
|
||||
});
|
||||
|
||||
this.nextNoteKeyObserver = this.keyboardManager.addKeyObserver({
|
||||
key: KeyboardManager.KeyDown,
|
||||
key: KeyboardKeys.Down,
|
||||
elements: [
|
||||
document.body,
|
||||
this.getSearchBar()
|
||||
@@ -674,7 +659,7 @@ class NotesCtrl extends PureCtrl {
|
||||
});
|
||||
|
||||
this.nextNoteKeyObserver = this.keyboardManager.addKeyObserver({
|
||||
key: KeyboardManager.KeyUp,
|
||||
key: KeyboardKeys.Up,
|
||||
element: document.body,
|
||||
onKeyDown: (event) => {
|
||||
this.selectPreviousNote();
|
||||
@@ -684,8 +669,8 @@ class NotesCtrl extends PureCtrl {
|
||||
this.searchKeyObserver = this.keyboardManager.addKeyObserver({
|
||||
key: "f",
|
||||
modifiers: [
|
||||
KeyboardManager.KeyModifierMeta,
|
||||
KeyboardManager.KeyModifierShift
|
||||
KeyboardModifiers.Meta,
|
||||
KeyboardModifiers.Shift
|
||||
],
|
||||
onKeyDown: (event) => {
|
||||
const searchBar = this.getSearchBar();
|
||||
|
||||
@@ -2,10 +2,7 @@ import _ from 'lodash';
|
||||
import { Challenges } from 'snjs';
|
||||
import { getPlatformString } from '@/utils';
|
||||
import template from '%/root.pug';
|
||||
import {
|
||||
APP_STATE_EVENT_PANEL_RESIZED,
|
||||
APP_STATE_EVENT_WINDOW_DID_FOCUS
|
||||
} from '@/state';
|
||||
import { AppStateEvents } from '@/state';
|
||||
import {
|
||||
PANEL_NAME_NOTES,
|
||||
PANEL_NAME_TAGS
|
||||
@@ -15,6 +12,7 @@ import {
|
||||
STRING_DEFAULT_FILE_ERROR,
|
||||
StringSyncException
|
||||
} from '@/strings';
|
||||
import { PureCtrl } from './abstract/pure_ctrl';
|
||||
|
||||
class RootCtrl extends PureCtrl {
|
||||
/* @ngInject */
|
||||
@@ -45,7 +43,6 @@ class RootCtrl extends PureCtrl {
|
||||
needsUnlock: false,
|
||||
appClass: ''
|
||||
};
|
||||
|
||||
this.loadApplication();
|
||||
this.handleAutoSignInFromParams();
|
||||
this.addAppStateObserver();
|
||||
@@ -53,7 +50,7 @@ class RootCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
async loadApplication() {
|
||||
this.application.prepareForLaunch({
|
||||
await this.application.prepareForLaunch({
|
||||
callbacks: {
|
||||
authChallengeResponses: async (challenges) => {
|
||||
if (challenges.includes(Challenges.LocalPasscode)) {
|
||||
@@ -65,7 +62,7 @@ class RootCtrl extends PureCtrl {
|
||||
await this.application.launch();
|
||||
this.setState({ needsUnlock: false });
|
||||
await this.openDatabase();
|
||||
this.preferencesManager.load();
|
||||
await this.preferencesManager.initialize();
|
||||
this.addSyncStatusObserver();
|
||||
this.addSyncEventHandler();
|
||||
}
|
||||
@@ -76,7 +73,7 @@ class RootCtrl extends PureCtrl {
|
||||
|
||||
addAppStateObserver() {
|
||||
this.appState.addObserver(async (eventName, data) => {
|
||||
if (eventName === APP_STATE_EVENT_PANEL_RESIZED) {
|
||||
if (eventName === AppStateEvents.PanelResized) {
|
||||
if (data.panel === PANEL_NAME_NOTES) {
|
||||
this.notesCollapsed = data.collapsed;
|
||||
}
|
||||
@@ -87,7 +84,7 @@ class RootCtrl extends PureCtrl {
|
||||
if (this.notesCollapsed) { appClass += "collapsed-notes"; }
|
||||
if (this.tagsCollapsed) { appClass += " collapsed-tags"; }
|
||||
this.setState({ appClass });
|
||||
} else if (eventName === APP_STATE_EVENT_WINDOW_DID_FOCUS) {
|
||||
} else if (eventName === AppStateEvents.WindowDidFocus) {
|
||||
if (!(await this.application.isPasscodeLocked())) {
|
||||
this.application.sync();
|
||||
}
|
||||
@@ -110,7 +107,7 @@ class RootCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
// addSyncStatusObserver() {
|
||||
// this.syncStatusObserver = this.syncManager.registerSyncStatusObserver((status) => {
|
||||
// this.syncStatusObserver = syncManager.registerSyncStatusObserver((status) => {
|
||||
// if (status.retrievedCount > 20) {
|
||||
// const text = `Downloading ${status.retrievedCount} items. Keep app open.`;
|
||||
// this.syncStatus = this.statusManager.replaceStatusWithString(
|
||||
@@ -143,7 +140,7 @@ class RootCtrl extends PureCtrl {
|
||||
|
||||
// addSyncEventHandler() {
|
||||
// let lastShownDate;
|
||||
// this.syncManager.addEventHandler((syncEvent, data) => {
|
||||
// syncManager.addEventHandler((syncEvent, data) => {
|
||||
// this.$rootScope.$broadcast(
|
||||
// syncEvent,
|
||||
// data || {}
|
||||
@@ -183,14 +180,14 @@ class RootCtrl extends PureCtrl {
|
||||
// status
|
||||
// );
|
||||
// };
|
||||
// this.syncManager.loadLocalItems({ incrementalCallback }).then(() => {
|
||||
// syncManager.loadLocalItems({ incrementalCallback }).then(() => {
|
||||
// this.$timeout(() => {
|
||||
// this.$rootScope.$broadcast("initial-data-loaded");
|
||||
// this.syncStatus = this.statusManager.replaceStatusWithString(
|
||||
// this.syncStatus,
|
||||
// "Syncing..."
|
||||
// );
|
||||
// this.syncManager.sync({
|
||||
// syncManager.sync({
|
||||
// checkIntegrity: true
|
||||
// }).then(() => {
|
||||
// this.syncStatus = this.statusManager.removeStatus(this.syncStatus);
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { SNNote, SNSmartTag, CONTENT_TYPE_TAG, CONTENT_TYPE_SMART_TAG } from 'snjs';
|
||||
import { SNNote, SNSmartTag, ContentTypes } from 'snjs';
|
||||
import template from '%/tags.pug';
|
||||
import {
|
||||
APP_STATE_EVENT_PREFERENCES_CHANGED,
|
||||
APP_STATE_EVENT_TAG_CHANGED
|
||||
} from '@/state';
|
||||
import { AppStateEvents } from '@/state';
|
||||
import { PANEL_NAME_TAGS } from '@/controllers/constants';
|
||||
import { PREF_TAGS_PANEL_WIDTH } from '@/services/preferencesManager';
|
||||
import { PrefKeys } from '@/services/preferencesManager';
|
||||
import { STRING_DELETE_TAG } from '@/strings';
|
||||
import { PureCtrl } from '@Controllers';
|
||||
|
||||
@@ -40,11 +37,11 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
|
||||
beginStreamingItems() {
|
||||
this.application.streamItems({
|
||||
contentType: CONTENT_TYPE_TAG,
|
||||
stream: async ({items}) => {
|
||||
contentType: ContentTypes.Tag,
|
||||
stream: async ({ items }) => {
|
||||
await this.setState({
|
||||
tags: this.application.getItems({contentType: CONTENT_TYPE_TAG}),
|
||||
smartTags: this.application.getItems({ contentType: CONTENT_TYPE_SMART_TAG }),
|
||||
tags: this.application.getItems({ contentType: ContentTypes.Tag }),
|
||||
smartTags: this.application.getItems({ contentType: ContentTypes.SmartTag }),
|
||||
});
|
||||
this.reloadNoteCounts();
|
||||
if (this.state.selectedTag) {
|
||||
@@ -62,9 +59,9 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
|
||||
addAppStateObserver() {
|
||||
this.appState.addObserver((eventName, data) => {
|
||||
if (eventName === APP_STATE_EVENT_PREFERENCES_CHANGED) {
|
||||
if (eventName === AppStateEvents.PreferencesChanged) {
|
||||
this.loadPreferences();
|
||||
} else if (eventName === APP_STATE_EVENT_TAG_CHANGED) {
|
||||
} else if (eventName === AppStateEvents.TagChanged) {
|
||||
this.setState({
|
||||
selectedTag: this.appState.getSelectedTag()
|
||||
});
|
||||
@@ -93,7 +90,7 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
loadPreferences() {
|
||||
const width = this.preferencesManager.getValue(PREF_TAGS_PANEL_WIDTH);
|
||||
const width = this.preferencesManager.getValue(PrefKeys.TagsPanelWidth);
|
||||
if (width) {
|
||||
this.panelController.setWidth(width);
|
||||
if (this.panelController.isCollapsed()) {
|
||||
@@ -107,7 +104,7 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
|
||||
onPanelResize = (newWidth, lastLeft, isAtMaxWidth, isCollapsed) => {
|
||||
this.preferencesManager.setUserPrefValue(
|
||||
PREF_TAGS_PANEL_WIDTH,
|
||||
PrefKeys.TagsPanelWidth,
|
||||
newWidth,
|
||||
true
|
||||
);
|
||||
@@ -130,7 +127,7 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
actionHandler: (component, action, data) => {
|
||||
if (action === 'select-item') {
|
||||
if (data.item.content_type === 'Tag') {
|
||||
const tag = this.application.findItem({uuid: data.item.uuid});
|
||||
const tag = this.application.findItem({ uuid: data.item.uuid });
|
||||
if (tag) {
|
||||
this.selectTag(tag);
|
||||
}
|
||||
@@ -157,7 +154,7 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
}
|
||||
if (tag.content.conflict_of) {
|
||||
tag.content.conflict_of = null;
|
||||
this.application.saveItem({item: tag});
|
||||
this.application.saveItem({ item: tag });
|
||||
}
|
||||
this.appState.setSelectedTag(tag);
|
||||
}
|
||||
@@ -167,7 +164,7 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
return;
|
||||
}
|
||||
const newTag = this.application.createItem({
|
||||
contentType: CONTENT_TYPE_TAG
|
||||
contentType: ContentTypes.Tag
|
||||
});
|
||||
this.setState({
|
||||
previousTag: this.state.selectedTag,
|
||||
@@ -177,7 +174,7 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
});
|
||||
/** @todo Should not be accessing internal function */
|
||||
/** Rely on local state instead of adding to global state */
|
||||
this.application.modelManager.insertItems({items: [newTag]});
|
||||
this.application.modelManager.insertItems({ items: [newTag] });
|
||||
}
|
||||
|
||||
tagTitleDidChange(tag) {
|
||||
@@ -188,14 +185,14 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
|
||||
async saveTag($event, tag) {
|
||||
$event.target.blur();
|
||||
await this.setState({
|
||||
editingTag: null
|
||||
await this.setState({
|
||||
editingTag: null
|
||||
});
|
||||
if (!tag.title || tag.title.length === 0) {
|
||||
if (this.state.editingTag) {
|
||||
tag.title = this.editingOriginalName;
|
||||
this.editingOriginalName = null;
|
||||
} else if(this.state.newTag) {
|
||||
} else if (this.state.newTag) {
|
||||
/** @todo Should not be accessing internal function */
|
||||
/** Rely on local state instead of adding to global state */
|
||||
this.application.modelManager.removeItemLocally(tag);
|
||||
@@ -206,10 +203,10 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
this.setState({ newTag: null });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.editingOriginalName = null;
|
||||
|
||||
const matchingTag = this.application.findTag({title: tag.title});
|
||||
const matchingTag = this.application.findTag({ title: tag.title });
|
||||
const alreadyExists = matchingTag && matchingTag !== tag;
|
||||
if (this.state.newTag === tag && alreadyExists) {
|
||||
this.application.alertManager.alert({
|
||||
@@ -222,7 +219,7 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
return;
|
||||
}
|
||||
|
||||
this.application.saveItem({item: tag});
|
||||
this.application.saveItem({ item: tag });
|
||||
this.selectTag(tag);
|
||||
this.setState({
|
||||
newTag: null
|
||||
@@ -247,7 +244,7 @@ class TagsPanelCtrl extends PureCtrl {
|
||||
text: STRING_DELETE_TAG,
|
||||
destructive: true,
|
||||
onConfirm: () => {
|
||||
this.application.deleteItem({item: tag});
|
||||
this.application.deleteItem({ item: tag });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { PureCtrl } from '@Controllers';
|
||||
class ActionsMenuCtrl extends PureCtrl {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
$scope,
|
||||
$timeout,
|
||||
actionsManager,
|
||||
godService
|
||||
|
||||
@@ -2,22 +2,25 @@ import angular from 'angular';
|
||||
import template from '%/directives/panel-resizer.pug';
|
||||
import { debounce } from '@/utils';
|
||||
|
||||
const PANEL_SIDE_RIGHT = 'right';
|
||||
const PANEL_SIDE_LEFT = 'left';
|
||||
|
||||
const MOUSE_EVENT_MOVE = 'mousemove';
|
||||
const MOUSE_EVENT_DOWN = 'mousedown';
|
||||
const MOUSE_EVENT_UP = 'mouseup';
|
||||
|
||||
const PanelSides = {
|
||||
Right: 'right',
|
||||
Left: 'left'
|
||||
};
|
||||
const MouseEvents = {
|
||||
Move: 'mousemove',
|
||||
Down: 'mousedown',
|
||||
Up: 'mouseup'
|
||||
};
|
||||
const CssClasses = {
|
||||
Hoverable: 'hoverable',
|
||||
AlwaysVisible: 'always-visible',
|
||||
Dragging: 'dragging',
|
||||
NoSelection: 'no-selection',
|
||||
Collapsed: 'collapsed',
|
||||
AnimateOpacity: 'animate-opacity',
|
||||
};
|
||||
const WINDOW_EVENT_RESIZE = 'resize';
|
||||
|
||||
const PANEL_CSS_CLASS_HOVERABLE = 'hoverable';
|
||||
const PANEL_CSS_CLASS_ALWAYS_VISIBLE = 'always-visible';
|
||||
const PANEL_CSS_CLASS_DRAGGING = 'dragging';
|
||||
const PANEL_CSS_CLASS_NO_SELECTION = 'no-selection';
|
||||
const PANEL_CSS_CLASS_COLLAPSED = 'collapsed';
|
||||
const PANEL_CSS_CLASS_ANIMATE_OPACITY = 'animate-opacity';
|
||||
|
||||
class PanelResizerCtrl {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
@@ -78,14 +81,14 @@ class PanelResizerCtrl {
|
||||
this.appFrame = null;
|
||||
this.widthBeforeLastDblClick = 0;
|
||||
|
||||
if (this.property === PANEL_SIDE_RIGHT) {
|
||||
if (this.property === PanelSides.Right) {
|
||||
this.configureRightPanel();
|
||||
}
|
||||
if (this.alwaysVisible) {
|
||||
this.resizerColumn.classList.add(PANEL_CSS_CLASS_ALWAYS_VISIBLE);
|
||||
this.resizerColumn.classList.add(CssClasses.AlwaysVisible);
|
||||
}
|
||||
if (this.hoverable) {
|
||||
this.resizerColumn.classList.add(PANEL_CSS_CLASS_HOVERABLE);
|
||||
this.resizerColumn.classList.add(CssClasses.Hoverable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,26 +143,26 @@ class PanelResizerCtrl {
|
||||
}
|
||||
|
||||
addMouseDownListener() {
|
||||
this.resizerColumn.addEventListener(MOUSE_EVENT_DOWN, (event) => {
|
||||
this.resizerColumn.addEventListener(MouseEvents.Down, (event) => {
|
||||
this.addInvisibleOverlay();
|
||||
this.pressed = true;
|
||||
this.lastDownX = event.clientX;
|
||||
this.startWidth = this.panel.scrollWidth;
|
||||
this.startLeft = this.panel.offsetLeft;
|
||||
this.panel.classList.add(PANEL_CSS_CLASS_NO_SELECTION);
|
||||
this.panel.classList.add(CssClasses.NoSelection);
|
||||
if (this.hoverable) {
|
||||
this.resizerColumn.classList.add(PANEL_CSS_CLASS_DRAGGING);
|
||||
this.resizerColumn.classList.add(CssClasses.Dragging);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addMouseMoveListener() {
|
||||
document.addEventListener(MOUSE_EVENT_MOVE, (event) => {
|
||||
document.addEventListener(MouseEvents.Move, (event) => {
|
||||
if (!this.pressed) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
if (this.property && this.property === PANEL_SIDE_LEFT) {
|
||||
if (this.property && this.property === PanelSides.Left) {
|
||||
this.handleLeftEvent(event);
|
||||
} else {
|
||||
this.handleWidthEvent(event);
|
||||
@@ -210,12 +213,12 @@ class PanelResizerCtrl {
|
||||
}
|
||||
|
||||
addMouseUpListener() {
|
||||
document.addEventListener(MOUSE_EVENT_UP, event => {
|
||||
document.addEventListener(MouseEvents.Up, event => {
|
||||
this.removeInvisibleOverlay();
|
||||
if (this.pressed) {
|
||||
this.pressed = false;
|
||||
this.resizerColumn.classList.remove(PANEL_CSS_CLASS_DRAGGING);
|
||||
this.panel.classList.remove(PANEL_CSS_CLASS_NO_SELECTION);
|
||||
this.resizerColumn.classList.remove(CssClasses.Dragging);
|
||||
this.panel.classList.remove(CssClasses.NoSelection);
|
||||
const isMaxWidth = this.isAtMaxWidth();
|
||||
if (this.onResizeFinish) {
|
||||
this.onResizeFinish()(
|
||||
@@ -232,7 +235,7 @@ class PanelResizerCtrl {
|
||||
|
||||
isAtMaxWidth() {
|
||||
return (
|
||||
Math.round(this.lastWidth + this.lastLeft) ===
|
||||
Math.round(this.lastWidth + this.lastLeft) ===
|
||||
Math.round(this.getParentRect().width)
|
||||
);
|
||||
}
|
||||
@@ -279,9 +282,9 @@ class PanelResizerCtrl {
|
||||
|
||||
this.collapsed = this.isCollapsed();
|
||||
if (this.collapsed) {
|
||||
this.resizerColumn.classList.add(PANEL_CSS_CLASS_COLLAPSED);
|
||||
this.resizerColumn.classList.add(CssClasses.Collapsed);
|
||||
} else {
|
||||
this.resizerColumn.classList.remove(PANEL_CSS_CLASS_COLLAPSED);
|
||||
this.resizerColumn.classList.remove(CssClasses.Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,9 +311,9 @@ class PanelResizerCtrl {
|
||||
|
||||
flash() {
|
||||
const FLASH_DURATION = 3000;
|
||||
this.resizerColumn.classList.add(PANEL_CSS_CLASS_ANIMATE_OPACITY);
|
||||
this.resizerColumn.classList.add(CssClasses.AnimateOpacity);
|
||||
this.$timeout(() => {
|
||||
this.resizerColumn.classList.remove(PANEL_CSS_CLASS_ANIMATE_OPACITY);
|
||||
this.resizerColumn.classList.remove(CssClasses.AnimateOpacity);
|
||||
}, FLASH_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
||||
import template from '%/directives/privileges-management-modal.pug';
|
||||
import { PrivilegeCredentials } from 'snjs';
|
||||
|
||||
class PrivilegesManagementModalCtrl {
|
||||
/* @ngInject */
|
||||
@@ -18,9 +18,9 @@ class PrivilegesManagementModalCtrl {
|
||||
|
||||
displayInfoForCredential(credential) {
|
||||
const info = this.application.privilegesManager.displayInfoForCredential(credential);
|
||||
if (credential === PrivilegesManager.CredentialLocalPasscode) {
|
||||
if (credential === PrivilegeCredentials.LocalPasscode) {
|
||||
info.availability = this.hasPasscode;
|
||||
} else if (credential === PrivilegesManager.CredentialAccountPassword) {
|
||||
} else if (credential === PrivilegeCredentials.AccountPassword) {
|
||||
info.availability = this.hasAccount;
|
||||
} else {
|
||||
info.availability = true;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
PAYLOAD_SOURCE_REMOTE_ACTION_RETRIEVED,
|
||||
CONTENT_TYPE_NOTE,
|
||||
CONTENT_TYPE_COMPONENT
|
||||
ContentTypes
|
||||
} from 'snjs';
|
||||
import template from '%/directives/revision-preview-modal.pug';
|
||||
|
||||
@@ -25,7 +24,7 @@ class RevisionPreviewModalCtrl {
|
||||
|
||||
async configure() {
|
||||
this.note = await this.application.createItem({
|
||||
contentType: CONTENT_TYPE_NOTE,
|
||||
contentType: ContentTypes.Note,
|
||||
content: this.content
|
||||
});
|
||||
|
||||
@@ -43,7 +42,7 @@ class RevisionPreviewModalCtrl {
|
||||
* editor object has non-copyable properties like .window, which cannot be transfered
|
||||
*/
|
||||
const editorCopy = await this.application.createItem({
|
||||
contentType: CONTENT_TYPE_COMPONENT,
|
||||
contentType: ContentTypes.Component,
|
||||
content: editorForNote.content
|
||||
});
|
||||
editorCopy.readonly = true;
|
||||
|
||||
@@ -1,48 +1,46 @@
|
||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
||||
import { EncryptionIntents, ProtectedActions } from 'snjs';
|
||||
|
||||
|
||||
export class ArchiveManager {
|
||||
/* @ngInject */
|
||||
constructor(lockManager, authManager, modelManager, privilegesManager) {
|
||||
constructor(lockManager, application, authManager, modelManager, privilegesManager) {
|
||||
this.lockManager = lockManager;
|
||||
this.authManager = authManager;
|
||||
this.modelManager = modelManager;
|
||||
modelManager = modelManager;
|
||||
this.privilegesManager = privilegesManager;
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
/*
|
||||
Public
|
||||
*/
|
||||
|
||||
/** @public */
|
||||
async downloadBackup(encrypted) {
|
||||
return this.downloadBackupOfItems(this.modelManager.allItems, encrypted);
|
||||
return this.downloadBackupOfItems(modelManager.allItems, encrypted);
|
||||
}
|
||||
|
||||
/** @public */
|
||||
async downloadBackupOfItems(items, encrypted) {
|
||||
const run = async () => {
|
||||
// download in Standard Notes format
|
||||
let keys, authParams;
|
||||
if(encrypted) {
|
||||
if(this.authManager.offline() && this.lockManager.hasPasscode()) {
|
||||
keys = this.lockManager.keys();
|
||||
authParams = this.lockManager.passcodeAuthParams();
|
||||
} else {
|
||||
keys = await this.authManager.keys();
|
||||
authParams = await this.authManager.getAuthParams();
|
||||
}
|
||||
}
|
||||
this.__itemsData(items, keys, authParams).then((data) => {
|
||||
const intent = encrypted
|
||||
? EncryptionIntents.FileEncrypted
|
||||
: EncryptionIntents.FileDecrypted;
|
||||
this.itemsData(items, intent).then((data) => {
|
||||
const modifier = encrypted ? "Encrypted" : "Decrypted";
|
||||
this.__downloadData(data, `Standard Notes ${modifier} Backup - ${this.__formattedDate()}.txt`);
|
||||
|
||||
this.downloadData(
|
||||
data,
|
||||
`Standard Notes ${modifier} Backup - ${this.formattedDate()}.txt`
|
||||
);
|
||||
// download as zipped plain text files
|
||||
if(!keys) {
|
||||
this.__downloadZippedItems(items);
|
||||
if (!encrypted) {
|
||||
this.downloadZippedItems(items);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if(await this.privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageBackups)) {
|
||||
this.godService.presentPrivilegesModal(PrivilegesManager.ActionManageBackups, () => {
|
||||
if (await this.privilegesManager.actionRequiresPrivilege(ProtectedActions.ManageBackups)) {
|
||||
this.godService.presentPrivilegesModal(ProtectedActions.ManageBackups, () => {
|
||||
run();
|
||||
});
|
||||
} else {
|
||||
@@ -50,29 +48,30 @@ export class ArchiveManager {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Private
|
||||
*/
|
||||
|
||||
__formattedDate() {
|
||||
/** @private */
|
||||
formattedDate() {
|
||||
var string = `${new Date()}`;
|
||||
// Match up to the first parenthesis, i.e do not include '(Central Standard Time)'
|
||||
var matches = string.match(/^(.*?) \(/);
|
||||
if(matches.length >= 2) {
|
||||
if (matches.length >= 2) {
|
||||
return matches[1];
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
async __itemsData(items, keys, authParams) {
|
||||
const data = await this.modelManager.getJSONDataForItems(items, keys, authParams);
|
||||
const blobData = new Blob([data], {type: 'text/json'});
|
||||
/** @private */
|
||||
async itemsData(items, intent) {
|
||||
const data = await this.application.protocolService.createBackupFile({
|
||||
subItems: items,
|
||||
intent: intent
|
||||
});
|
||||
const blobData = new Blob([data], { type: 'text/json' });
|
||||
return blobData;
|
||||
}
|
||||
|
||||
__loadZip(callback) {
|
||||
if(window.zip) {
|
||||
callback();
|
||||
/** @private */
|
||||
async loadZip() {
|
||||
if (window.zip) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -81,75 +80,77 @@ export class ArchiveManager {
|
||||
scriptTag.async = false;
|
||||
var headTag = document.getElementsByTagName('head')[0];
|
||||
headTag.appendChild(scriptTag);
|
||||
scriptTag.onload = function() {
|
||||
zip.workerScriptsPath = "assets/zip/";
|
||||
callback();
|
||||
};
|
||||
}
|
||||
|
||||
__downloadZippedItems(items) {
|
||||
this.__loadZip(() => {
|
||||
zip.createWriter(new zip.BlobWriter("application/zip"), (zipWriter) => {
|
||||
var index = 0;
|
||||
|
||||
const nextFile = () => {
|
||||
var item = items[index];
|
||||
var name, contents;
|
||||
|
||||
if(item.content_type === "Note") {
|
||||
name = item.content.title;
|
||||
contents = item.content.text;
|
||||
} else {
|
||||
name = item.content_type;
|
||||
contents = JSON.stringify(item.content, null, 2);
|
||||
}
|
||||
|
||||
if(!name) {
|
||||
name = "";
|
||||
}
|
||||
|
||||
const blob = new Blob([contents], {type: 'text/plain'});
|
||||
let filePrefix = name.replace(/\//g, "").replace(/\\+/g, "");
|
||||
const fileSuffix = `-${item.uuid.split("-")[0]}.txt`;
|
||||
// Standard max filename length is 255. Slice the note name down to allow filenameEnd
|
||||
filePrefix = filePrefix.slice(0, (255 - fileSuffix.length));
|
||||
const fileName = `${item.content_type}/${filePrefix}${fileSuffix}`;
|
||||
zipWriter.add(fileName, new zip.BlobReader(blob), () => {
|
||||
index++;
|
||||
if(index < items.length) {
|
||||
nextFile();
|
||||
} else {
|
||||
zipWriter.close((blob) => {
|
||||
this.__downloadData(blob, `Standard Notes Backup - ${this.__formattedDate()}.zip`);
|
||||
zipWriter = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
nextFile();
|
||||
}, onerror);
|
||||
return new Promise((resolve, reject) => {
|
||||
scriptTag.onload = function () {
|
||||
zip.workerScriptsPath = "assets/zip/";
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/** @private */
|
||||
async downloadZippedItems(items) {
|
||||
await this.loadZip();
|
||||
zip.createWriter(new zip.BlobWriter("application/zip"), (zipWriter) => {
|
||||
var index = 0;
|
||||
|
||||
__hrefForData(data) {
|
||||
const nextFile = () => {
|
||||
var item = items[index];
|
||||
var name, contents;
|
||||
|
||||
if (item.content_type === "Note") {
|
||||
name = item.content.title;
|
||||
contents = item.content.text;
|
||||
} else {
|
||||
name = item.content_type;
|
||||
contents = JSON.stringify(item.content, null, 2);
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
name = "";
|
||||
}
|
||||
|
||||
const blob = new Blob([contents], { type: 'text/plain' });
|
||||
let filePrefix = name.replace(/\//g, "").replace(/\\+/g, "");
|
||||
const fileSuffix = `-${item.uuid.split("-")[0]}.txt`;
|
||||
// Standard max filename length is 255. Slice the note name down to allow filenameEnd
|
||||
filePrefix = filePrefix.slice(0, (255 - fileSuffix.length));
|
||||
const fileName = `${item.content_type}/${filePrefix}${fileSuffix}`;
|
||||
zipWriter.add(fileName, new zip.BlobReader(blob), () => {
|
||||
index++;
|
||||
if (index < items.length) {
|
||||
nextFile();
|
||||
} else {
|
||||
zipWriter.close((blob) => {
|
||||
this.downloadData(blob, `Standard Notes Backup - ${this.formattedDate()}.zip`);
|
||||
zipWriter = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
nextFile();
|
||||
}, onerror);
|
||||
}
|
||||
|
||||
|
||||
/** @private */
|
||||
hrefForData(data) {
|
||||
// If we are replacing a previously generated file we need to
|
||||
// manually revoke the object URL to avoid memory leaks.
|
||||
if (this.textFile !== null) {
|
||||
window.URL.revokeObjectURL(this.textFile);
|
||||
}
|
||||
|
||||
this.textFile = window.URL.createObjectURL(data);
|
||||
|
||||
// returns a URL you can use as a href
|
||||
return this.textFile;
|
||||
}
|
||||
|
||||
__downloadData(data, fileName) {
|
||||
/** @private */
|
||||
downloadData(data, fileName) {
|
||||
var link = document.createElement('a');
|
||||
link.setAttribute('download', fileName);
|
||||
link.href = this.__hrefForData(data);
|
||||
link.href = this.hrefForData(data);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* eslint-disable camelcase */
|
||||
// An interface used by the Desktop app to interact with SN
|
||||
import _ from 'lodash';
|
||||
import pull from 'lodash/pull';
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { SFItemParams, SFModelManager } from 'snjs';
|
||||
import { EncryptionIntents } from 'snjs';
|
||||
|
||||
const COMPONENT_DATA_KEY_INSTALL_ERROR = 'installError';
|
||||
const COMPONENT_CONTENT_KEY_PACKAGE_INFO = 'package_info';
|
||||
@@ -12,33 +13,26 @@ export class DesktopManager {
|
||||
constructor(
|
||||
$rootScope,
|
||||
$timeout,
|
||||
modelManager,
|
||||
syncManager,
|
||||
authManager,
|
||||
lockManager,
|
||||
appState
|
||||
application,
|
||||
appState,
|
||||
) {
|
||||
this.lockManager = lockManager;
|
||||
this.modelManager = modelManager;
|
||||
this.authManager = authManager;
|
||||
this.syncManager = syncManager;
|
||||
this.$rootScope = $rootScope;
|
||||
this.$timeout = $timeout;
|
||||
this.appState = appState;
|
||||
this.timeout = $timeout;
|
||||
this.updateObservers = [];
|
||||
this.application = application;
|
||||
this.componentActivationObservers = [];
|
||||
|
||||
this.updateObservers = [];
|
||||
this.isDesktop = isDesktopApplication();
|
||||
|
||||
$rootScope.$on("initial-data-loaded", () => {
|
||||
$rootScope.$on('initial-data-loaded', () => {
|
||||
this.dataLoaded = true;
|
||||
if(this.dataLoadHandler) {
|
||||
if (this.dataLoadHandler) {
|
||||
this.dataLoadHandler();
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on("major-data-change", () => {
|
||||
if(this.majorDataChangeHandler) {
|
||||
$rootScope.$on('major-data-change', () => {
|
||||
if (this.majorDataChangeHandler) {
|
||||
this.majorDataChangeHandler();
|
||||
}
|
||||
});
|
||||
@@ -56,17 +50,20 @@ export class DesktopManager {
|
||||
return this.extServerHost;
|
||||
}
|
||||
|
||||
/*
|
||||
Sending a component in its raw state is really slow for the desktop app
|
||||
Keys are not passed into ItemParams, so the result is not encrypted
|
||||
/**
|
||||
* Sending a component in its raw state is really slow for the desktop app
|
||||
* Keys are not passed into ItemParams, so the result is not encrypted
|
||||
*/
|
||||
async convertComponentForTransmission(component) {
|
||||
return new SFItemParams(component).paramsForExportFile(true);
|
||||
return this.application.protocolService.payloadByEncryptingPayload({
|
||||
payload: component.payloadRepresentation(),
|
||||
intent: EncryptionIntents.FileDecrypted
|
||||
});
|
||||
}
|
||||
|
||||
// All `components` should be installed
|
||||
syncComponentsInstallation(components) {
|
||||
if(!this.isDesktop) {
|
||||
if (!this.isDesktop) {
|
||||
return;
|
||||
}
|
||||
Promise.all(components.map((component) => {
|
||||
@@ -91,21 +88,21 @@ export class DesktopManager {
|
||||
}
|
||||
|
||||
searchText(text) {
|
||||
if(!this.isDesktop) {
|
||||
if (!this.isDesktop) {
|
||||
return;
|
||||
}
|
||||
this.lastSearchedText = text;
|
||||
this.searchHandler && this.searchHandler(text);
|
||||
}
|
||||
|
||||
redoSearch() {
|
||||
if(this.lastSearchedText) {
|
||||
redoSearch() {
|
||||
if (this.lastSearchedText) {
|
||||
this.searchText(this.lastSearchedText);
|
||||
}
|
||||
}
|
||||
|
||||
deregisterUpdateObserver(observer) {
|
||||
_.pull(this.updateObservers, observer);
|
||||
pull(this.updateObservers, observer);
|
||||
}
|
||||
|
||||
// Pass null to cancel search
|
||||
@@ -114,19 +111,19 @@ export class DesktopManager {
|
||||
}
|
||||
|
||||
desktop_windowGainedFocus() {
|
||||
this.$rootScope.$broadcast("window-gained-focus");
|
||||
this.$rootScope.$broadcast('window-gained-focus');
|
||||
}
|
||||
|
||||
desktop_windowLostFocus() {
|
||||
this.$rootScope.$broadcast("window-lost-focus");
|
||||
this.$rootScope.$broadcast('window-lost-focus');
|
||||
}
|
||||
|
||||
desktop_onComponentInstallationComplete(componentData, error) {
|
||||
const component = this.modelManager.findItem(componentData.uuid);
|
||||
if(!component) {
|
||||
async desktop_onComponentInstallationComplete(componentData, error) {
|
||||
const component = await this.application.findItem({ uuid: componentData.uuid });
|
||||
if (!component) {
|
||||
return;
|
||||
}
|
||||
if(error) {
|
||||
if (error) {
|
||||
component.setAppDataItem(
|
||||
COMPONENT_DATA_KEY_INSTALL_ERROR,
|
||||
error
|
||||
@@ -136,35 +133,30 @@ export class DesktopManager {
|
||||
COMPONENT_CONTENT_KEY_PACKAGE_INFO,
|
||||
COMPONENT_CONTENT_KEY_LOCAL_URL
|
||||
];
|
||||
for(const key of permissableKeys) {
|
||||
for (const key of permissableKeys) {
|
||||
component[key] = componentData.content[key];
|
||||
}
|
||||
this.modelManager.notifySyncObserversOfModels(
|
||||
[component],
|
||||
SFModelManager.MappingSourceDesktopInstalled
|
||||
);
|
||||
component.setAppDataItem(
|
||||
COMPONENT_DATA_KEY_INSTALL_ERROR,
|
||||
null
|
||||
);
|
||||
}
|
||||
this.modelManager.setItemDirty(component);
|
||||
this.syncManager.sync();
|
||||
this.timeout(() => {
|
||||
for(const observer of this.updateObservers) {
|
||||
this.application.saveItem({ item: component });
|
||||
this.$timeout(() => {
|
||||
for (const observer of this.updateObservers) {
|
||||
observer.callback(component);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
desktop_registerComponentActivationObserver(callback) {
|
||||
const observer = {id: Math.random, callback: callback};
|
||||
const observer = { id: Math.random, callback: callback };
|
||||
this.componentActivationObservers.push(observer);
|
||||
return observer;
|
||||
}
|
||||
|
||||
desktop_deregisterComponentActivationObserver(observer) {
|
||||
_.pull(this.componentActivationObservers, observer);
|
||||
pull(this.componentActivationObservers, observer);
|
||||
}
|
||||
|
||||
/* Notify observers that a component has been registered/activated */
|
||||
@@ -172,14 +164,14 @@ export class DesktopManager {
|
||||
const serializedComponent = await this.convertComponentForTransmission(
|
||||
component
|
||||
);
|
||||
this.timeout(() => {
|
||||
for(const observer of this.componentActivationObservers) {
|
||||
this.$timeout(() => {
|
||||
for (const observer of this.componentActivationObservers) {
|
||||
observer.callback(serializedComponent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Used to resolve "sn://" */
|
||||
/* Used to resolve 'sn://' */
|
||||
desktop_setExtServerHost(host) {
|
||||
this.extServerHost = host;
|
||||
this.appState.desktopExtensionsReady();
|
||||
@@ -195,28 +187,16 @@ export class DesktopManager {
|
||||
|
||||
desktop_setInitialDataLoadHandler(handler) {
|
||||
this.dataLoadHandler = handler;
|
||||
if(this.dataLoaded) {
|
||||
if (this.dataLoaded) {
|
||||
this.dataLoadHandler();
|
||||
}
|
||||
}
|
||||
|
||||
async desktop_requestBackupFile(callback) {
|
||||
let keys, authParams;
|
||||
if(this.authManager.offline() && this.lockManager.hasPasscode()) {
|
||||
keys = this.lockManager.keys();
|
||||
authParams = this.lockManager.passcodeAuthParams();
|
||||
} else {
|
||||
keys = await this.authManager.keys();
|
||||
authParams = await this.authManager.getAuthParams();
|
||||
}
|
||||
const nullOnEmpty = true;
|
||||
this.modelManager.getAllItemsJSONData(
|
||||
keys,
|
||||
authParams,
|
||||
nullOnEmpty
|
||||
).then((data) => {
|
||||
callback(data);
|
||||
const data = await this.application.protocolService.createBackupFile({
|
||||
returnIfEmpty: true
|
||||
});
|
||||
callback(data);
|
||||
}
|
||||
|
||||
desktop_setMajorDataChangeHandler(handler) {
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
export { ActionsManager } from './actionsManager';
|
||||
export { ArchiveManager } from './archiveManager';
|
||||
export { AuthManager } from './authManager';
|
||||
export { ComponentManager } from './componentManager';
|
||||
export { DatabaseManager } from './databaseManager';
|
||||
export { DesktopManager } from './desktopManager';
|
||||
export { HttpManager } from './httpManager';
|
||||
export { KeyboardManager } from './keyboardManager';
|
||||
export { MigrationManager } from './migrationManager';
|
||||
export { ModelManager } from './modelManager';
|
||||
export { NativeExtManager } from './nativeExtManager';
|
||||
export { LockManager } from './lockManager';
|
||||
export { PrivilegesManager } from './privilegesManager';
|
||||
export { SessionHistory } from './sessionHistory';
|
||||
export { SingletonManager } from './singletonManager';
|
||||
export { StatusManager } from './statusManager';
|
||||
export { StorageManager } from './storageManager';
|
||||
export { SyncManager } from './syncManager';
|
||||
export { ThemeManager } from './themeManager';
|
||||
export { AlertManager } from './alertManager';
|
||||
export { PreferencesManager } from './preferencesManager';
|
||||
|
||||
@@ -1,42 +1,41 @@
|
||||
export class KeyboardManager {
|
||||
/** @public */
|
||||
export const KeyboardKeys = {
|
||||
Tab: "Tab",
|
||||
Backspace: "Backspace",
|
||||
Up: "ArrowUp",
|
||||
Down: "ArrowDown",
|
||||
};
|
||||
/** @public */
|
||||
export const KeyboardModifiers = {
|
||||
Shift: "Shift",
|
||||
Ctrl: "Control",
|
||||
/** ⌘ key on Mac, ⊞ key on Windows */
|
||||
Meta: "Meta",
|
||||
Alt: "Alt",
|
||||
};
|
||||
/** @private */
|
||||
const KeyboardKeyEvents = {
|
||||
Down: "KeyEventDown",
|
||||
Up: "KeyEventUp"
|
||||
};
|
||||
|
||||
export class KeyboardManager {
|
||||
constructor() {
|
||||
this.observers = [];
|
||||
|
||||
KeyboardManager.KeyTab = "Tab";
|
||||
KeyboardManager.KeyBackspace = "Backspace";
|
||||
KeyboardManager.KeyUp = "ArrowUp";
|
||||
KeyboardManager.KeyDown = "ArrowDown";
|
||||
|
||||
KeyboardManager.KeyModifierShift = "Shift";
|
||||
KeyboardManager.KeyModifierCtrl = "Control";
|
||||
// ⌘ key on Mac, ⊞ key on Windows
|
||||
KeyboardManager.KeyModifierMeta = "Meta";
|
||||
KeyboardManager.KeyModifierAlt = "Alt";
|
||||
|
||||
KeyboardManager.KeyEventDown = "KeyEventDown";
|
||||
KeyboardManager.KeyEventUp = "KeyEventUp";
|
||||
|
||||
KeyboardManager.AllModifiers = [
|
||||
KeyboardManager.KeyModifierShift,
|
||||
KeyboardManager.KeyModifierCtrl,
|
||||
KeyboardManager.KeyModifierMeta,
|
||||
KeyboardManager.KeyModifierAlt
|
||||
];
|
||||
|
||||
window.addEventListener('keydown', this.handleKeyDown.bind(this));
|
||||
window.addEventListener('keyup', this.handleKeyUp.bind(this));
|
||||
}
|
||||
|
||||
modifiersForEvent(event) {
|
||||
const eventModifiers = KeyboardManager.AllModifiers.filter((modifier) => {
|
||||
const allModifiers = Object.keys(KeyboardModifiers).map((key) => KeyboardModifiers[key]);
|
||||
const eventModifiers = allModifiers.filter((modifier) => {
|
||||
// For a modifier like ctrlKey, must check both event.ctrlKey and event.key.
|
||||
// That's because on keyup, event.ctrlKey would be false, but event.key == Control would be true.
|
||||
const matches = (
|
||||
((event.ctrlKey || event.key == KeyboardManager.KeyModifierCtrl) && modifier === KeyboardManager.KeyModifierCtrl) ||
|
||||
((event.metaKey || event.key == KeyboardManager.KeyModifierMeta) && modifier === KeyboardManager.KeyModifierMeta) ||
|
||||
((event.altKey || event.key == KeyboardManager.KeyModifierAlt) && modifier === KeyboardManager.KeyModifierAlt) ||
|
||||
((event.shiftKey || event.key == KeyboardManager.KeyModifierShift) && modifier === KeyboardManager.KeyModifierShift)
|
||||
((event.ctrlKey || event.key === KeyboardModifiers.Ctrl) && modifier === KeyboardModifiers.Ctrl) ||
|
||||
((event.metaKey || event.key === KeyboardModifiers.Meta) && modifier === KeyboardModifiers.Meta) ||
|
||||
((event.altKey || event.key === KeyboardModifiers.Alt) && modifier === KeyboardModifiers.Alt) ||
|
||||
((event.shiftKey || event.key === KeyboardModifiers.Shift) && modifier === KeyboardModifiers.Shift)
|
||||
);
|
||||
|
||||
return matches;
|
||||
@@ -45,50 +44,50 @@ export class KeyboardManager {
|
||||
return eventModifiers;
|
||||
}
|
||||
|
||||
eventMatchesKeyAndModifiers(event, key, modifiers = []) {
|
||||
eventMatchesKeyAndModifiers(event, key, modifiers = []) {
|
||||
const eventModifiers = this.modifiersForEvent(event);
|
||||
|
||||
if(eventModifiers.length != modifiers.length) {
|
||||
if (eventModifiers.length !== modifiers.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const modifier of modifiers) {
|
||||
if(!eventModifiers.includes(modifier)) {
|
||||
for (const modifier of modifiers) {
|
||||
if (!eventModifiers.includes(modifier)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Modifers match, check key
|
||||
if(!key) {
|
||||
if (!key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// In the browser, shift + f results in key 'f', but in Electron, shift + f results in 'F'
|
||||
// In our case we don't differentiate between the two.
|
||||
return key.toLowerCase() == event.key.toLowerCase();
|
||||
return key.toLowerCase() === event.key.toLowerCase();
|
||||
}
|
||||
|
||||
notifyObserver(event, keyEventType) {
|
||||
for(const observer of this.observers) {
|
||||
if(observer.element && event.target != observer.element) {
|
||||
for (const observer of this.observers) {
|
||||
if (observer.element && event.target !== observer.element) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(observer.elements && !observer.elements.includes(event.target)) {
|
||||
if (observer.elements && !observer.elements.includes(event.target)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(observer.notElement && observer.notElement == event.target) {
|
||||
if (observer.notElement && observer.notElement === event.target) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(observer.notElementIds && observer.notElementIds.includes(event.target.id)) {
|
||||
if (observer.notElementIds && observer.notElementIds.includes(event.target.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(this.eventMatchesKeyAndModifiers(event, observer.key, observer.modifiers)) {
|
||||
const callback = keyEventType == KeyboardManager.KeyEventDown ? observer.onKeyDown : observer.onKeyUp;
|
||||
if(callback) {
|
||||
if (this.eventMatchesKeyAndModifiers(event, observer.key, observer.modifiers)) {
|
||||
const callback = keyEventType === KeyboardKeyEvents.Down ? observer.onKeyDown : observer.onKeyUp;
|
||||
if (callback) {
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
@@ -96,15 +95,15 @@ export class KeyboardManager {
|
||||
}
|
||||
|
||||
handleKeyDown(event) {
|
||||
this.notifyObserver(event, KeyboardManager.KeyEventDown);
|
||||
this.notifyObserver(event, KeyboardKeyEvents.Down);
|
||||
}
|
||||
|
||||
handleKeyUp(event) {
|
||||
this.notifyObserver(event, KeyboardManager.KeyEventUp);
|
||||
this.notifyObserver(event, KeyboardKeyEvents.Up);
|
||||
}
|
||||
|
||||
addKeyObserver({key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds}) {
|
||||
const observer = {key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds};
|
||||
addKeyObserver({ key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds }) {
|
||||
const observer = { key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds };
|
||||
this.observers.push(observer);
|
||||
return observer;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import {
|
||||
APP_STATE_EVENT_WINDOW_DID_BLUR,
|
||||
APP_STATE_EVENT_WINDOW_DID_FOCUS
|
||||
} from '../state';
|
||||
import { AppStateEvents } from '../state';
|
||||
|
||||
const MILLISECONDS_PER_SECOND = 1000;
|
||||
const FOCUS_POLL_INTERVAL = 1 * MILLISECONDS_PER_SECOND;
|
||||
@@ -26,9 +22,9 @@ export class LockManager {
|
||||
|
||||
observeVisibility() {
|
||||
this.appState.addObserver((eventName, data) => {
|
||||
if(eventName === APP_STATE_EVENT_WINDOW_DID_BLUR) {
|
||||
if(eventName === AppStateEvents.WindowDidBlur) {
|
||||
this.documentVisibilityChanged(false);
|
||||
} else if(eventName === APP_STATE_EVENT_WINDOW_DID_FOCUS) {
|
||||
} else if(eventName === AppStateEvents.WindowDidFocus) {
|
||||
this.documentVisibilityChanged(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,183 +1,177 @@
|
||||
/* A class for handling installation of system extensions */
|
||||
import { isDesktopApplication, dictToArray } from '@/utils';
|
||||
import { SFPredicate, ContentTypes, CreateMaxPayloadFromAnyObject } from 'snjs';
|
||||
import { AppStateEvents } from '@/state';
|
||||
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { SFPredicate } from 'snjs';
|
||||
const STREAM_ITEMS_PERMISSION = 'stream-items';
|
||||
|
||||
/** A class for handling installation of system extensions */
|
||||
export class NativeExtManager {
|
||||
/* @ngInject */
|
||||
constructor(modelManager, syncManager, singletonManager) {
|
||||
this.modelManager = modelManager;
|
||||
this.syncManager = syncManager;
|
||||
this.singletonManager = singletonManager;
|
||||
|
||||
this.extManagerId = "org.standardnotes.extensions-manager";
|
||||
this.batchManagerId = "org.standardnotes.batch-manager";
|
||||
constructor(application, appState) {
|
||||
this.application = application;
|
||||
this.extManagerId = 'org.standardnotes.extensions-manager';
|
||||
this.batchManagerId = 'org.standardnotes.batch-manager';
|
||||
this.systemExtensions = [];
|
||||
|
||||
this.resolveExtensionsManager();
|
||||
this.resolveBatchManager();
|
||||
|
||||
appState.addObserver(async (eventName) => {
|
||||
if (eventName === AppStateEvents.ApplicationReady) {
|
||||
await this.initialize();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isSystemExtension(extension) {
|
||||
return this.systemExtensions.includes(extension.uuid);
|
||||
}
|
||||
|
||||
resolveExtensionsManager() {
|
||||
async initialize() {
|
||||
this.resolveExtensionsManager();
|
||||
this.resolveBatchManager();
|
||||
}
|
||||
|
||||
const contentTypePredicate = new SFPredicate("content_type", "=", "SN|Component");
|
||||
const packagePredicate = new SFPredicate("package_info.identifier", "=", this.extManagerId);
|
||||
|
||||
this.singletonManager.registerSingleton([contentTypePredicate, packagePredicate], (resolvedSingleton) => {
|
||||
// Resolved Singleton
|
||||
this.systemExtensions.push(resolvedSingleton.uuid);
|
||||
|
||||
var needsSync = false;
|
||||
if(isDesktopApplication()) {
|
||||
if(!resolvedSingleton.local_url) {
|
||||
resolvedSingleton.local_url = window._extensions_manager_location;
|
||||
needsSync = true;
|
||||
}
|
||||
} else {
|
||||
if(!resolvedSingleton.hosted_url) {
|
||||
resolvedSingleton.hosted_url = window._extensions_manager_location;
|
||||
needsSync = true;
|
||||
extensionsManagerTemplatePayload() {
|
||||
const url = window._extensions_manager_location;
|
||||
if (!url) {
|
||||
console.error('window._extensions_manager_location must be set.');
|
||||
return;
|
||||
}
|
||||
const packageInfo = {
|
||||
name: 'Extensions',
|
||||
identifier: this.extManagerId
|
||||
};
|
||||
const content = {
|
||||
name: packageInfo.name,
|
||||
area: 'rooms',
|
||||
package_info: packageInfo,
|
||||
permissions: [
|
||||
{
|
||||
name: STREAM_ITEMS_PERMISSION,
|
||||
content_types: [
|
||||
ContentTypes.Component,
|
||||
ContentTypes.Theme,
|
||||
ContentTypes.ServerExtension,
|
||||
ContentTypes.ActionsExtension,
|
||||
ContentTypes.Mfa,
|
||||
ContentTypes.Editor,
|
||||
ContentTypes.ExtensionRepo
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
if (isDesktopApplication()) {
|
||||
content.local_url = window._extensions_manager_location;
|
||||
} else {
|
||||
content.hosted_url = window._extensions_manager_location;
|
||||
}
|
||||
const payload = CreateMaxPayloadFromAnyObject({
|
||||
object: {
|
||||
content_type: ContentTypes.Component,
|
||||
content: content
|
||||
}
|
||||
});
|
||||
return payload;
|
||||
}
|
||||
|
||||
// Handle addition of SN|ExtensionRepo permission
|
||||
const permission = resolvedSingleton.content.permissions.find((p) => p.name == "stream-items");
|
||||
if(!permission.content_types.includes("SN|ExtensionRepo")) {
|
||||
permission.content_types.push("SN|ExtensionRepo");
|
||||
async resolveExtensionsManager() {
|
||||
const contentTypePredicate = new SFPredicate('content_type', '=', ContentTypes.Component);
|
||||
const packagePredicate = new SFPredicate('package_info.identifier', '=', this.extManagerId);
|
||||
const predicate = SFPredicate.CompoundPredicate([contentTypePredicate, packagePredicate]);
|
||||
const extensionsManager = await this.application.singletonManager.findOrCreateSingleton({
|
||||
predicate: predicate,
|
||||
createPayload: this.extensionsManagerTemplatePayload()
|
||||
});
|
||||
this.systemExtensions.push(extensionsManager.uuid);
|
||||
let needsSync = false;
|
||||
if (isDesktopApplication()) {
|
||||
if (!extensionsManager.local_url) {
|
||||
extensionsManager.local_url = window._extensions_manager_location;
|
||||
needsSync = true;
|
||||
}
|
||||
|
||||
if(needsSync) {
|
||||
this.modelManager.setItemDirty(resolvedSingleton, true);
|
||||
this.syncManager.sync();
|
||||
} else {
|
||||
if (!extensionsManager.hosted_url) {
|
||||
extensionsManager.hosted_url = window._extensions_manager_location;
|
||||
needsSync = true;
|
||||
}
|
||||
}, (valueCallback) => {
|
||||
// Safe to create. Create and return object.
|
||||
const url = window._extensions_manager_location;
|
||||
if(!url) {
|
||||
console.error("window._extensions_manager_location must be set.");
|
||||
return;
|
||||
}
|
||||
|
||||
const packageInfo = {
|
||||
name: "Extensions",
|
||||
identifier: this.extManagerId
|
||||
};
|
||||
|
||||
var item = {
|
||||
content_type: "SN|Component",
|
||||
content: {
|
||||
name: packageInfo.name,
|
||||
area: "rooms",
|
||||
package_info: packageInfo,
|
||||
permissions: [
|
||||
{
|
||||
name: "stream-items",
|
||||
content_types: [
|
||||
"SN|Component", "SN|Theme", "SF|Extension",
|
||||
"Extension", "SF|MFA", "SN|Editor", "SN|ExtensionRepo"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
if(isDesktopApplication()) {
|
||||
item.content.local_url = window._extensions_manager_location;
|
||||
} else {
|
||||
item.content.hosted_url = window._extensions_manager_location;
|
||||
}
|
||||
|
||||
var component = this.modelManager.createItem(item);
|
||||
this.modelManager.addItem(component);
|
||||
|
||||
this.modelManager.setItemDirty(component, true);
|
||||
this.syncManager.sync();
|
||||
|
||||
this.systemExtensions.push(component.uuid);
|
||||
|
||||
valueCallback(component);
|
||||
});
|
||||
}
|
||||
// Handle addition of SN|ExtensionRepo permission
|
||||
const permission = extensionsManager.content.permissions.find((p) => p.name === STREAM_ITEMS_PERMISSION);
|
||||
if (!permission.content_types.includes(ContentTypes.ExtensionRepo)) {
|
||||
permission.content_types.push(ContentTypes.ExtensionRepo);
|
||||
needsSync = true;
|
||||
}
|
||||
if (needsSync) {
|
||||
this.application.saveItem({ item: extensionsManager });
|
||||
}
|
||||
}
|
||||
|
||||
resolveBatchManager() {
|
||||
|
||||
const contentTypePredicate = new SFPredicate("content_type", "=", "SN|Component");
|
||||
const packagePredicate = new SFPredicate("package_info.identifier", "=", this.batchManagerId);
|
||||
|
||||
this.singletonManager.registerSingleton([contentTypePredicate, packagePredicate], (resolvedSingleton) => {
|
||||
// Resolved Singleton
|
||||
this.systemExtensions.push(resolvedSingleton.uuid);
|
||||
|
||||
var needsSync = false;
|
||||
if(isDesktopApplication()) {
|
||||
if(!resolvedSingleton.local_url) {
|
||||
resolvedSingleton.local_url = window._batch_manager_location;
|
||||
needsSync = true;
|
||||
}
|
||||
} else {
|
||||
if(!resolvedSingleton.hosted_url) {
|
||||
resolvedSingleton.hosted_url = window._batch_manager_location;
|
||||
needsSync = true;
|
||||
batchManagerTemplatePayload() {
|
||||
const url = window._batch_manager_location;
|
||||
if (!url) {
|
||||
console.error('window._batch_manager_location must be set.');
|
||||
return;
|
||||
}
|
||||
const packageInfo = {
|
||||
name: 'Batch Manager',
|
||||
identifier: this.batchManagerId
|
||||
};
|
||||
const allContentTypes = dictToArray(ContentTypes);
|
||||
const content = {
|
||||
name: packageInfo.name,
|
||||
area: 'modal',
|
||||
package_info: packageInfo,
|
||||
permissions: [
|
||||
{
|
||||
name: STREAM_ITEMS_PERMISSION,
|
||||
content_types: allContentTypes
|
||||
}
|
||||
]
|
||||
};
|
||||
if (isDesktopApplication()) {
|
||||
content.local_url = window._batch_manager_location;
|
||||
} else {
|
||||
content.hosted_url = window._batch_manager_location;
|
||||
}
|
||||
const payload = CreateMaxPayloadFromAnyObject({
|
||||
object: {
|
||||
content_type: ContentTypes.Component,
|
||||
content: content
|
||||
}
|
||||
|
||||
if(needsSync) {
|
||||
this.modelManager.setItemDirty(resolvedSingleton, true);
|
||||
this.syncManager.sync();
|
||||
}
|
||||
}, (valueCallback) => {
|
||||
// Safe to create. Create and return object.
|
||||
const url = window._batch_manager_location;
|
||||
if(!url) {
|
||||
console.error("window._batch_manager_location must be set.");
|
||||
return;
|
||||
}
|
||||
|
||||
const packageInfo = {
|
||||
name: "Batch Manager",
|
||||
identifier: this.batchManagerId
|
||||
};
|
||||
|
||||
var item = {
|
||||
content_type: "SN|Component",
|
||||
content: {
|
||||
name: packageInfo.name,
|
||||
area: "modal",
|
||||
package_info: packageInfo,
|
||||
permissions: [
|
||||
{
|
||||
name: "stream-items",
|
||||
content_types: [
|
||||
"Note", "Tag", "SN|SmartTag",
|
||||
"SN|Component", "SN|Theme", "SN|UserPreferences",
|
||||
"SF|Extension", "Extension", "SF|MFA", "SN|Editor",
|
||||
"SN|FileSafe|Credentials", "SN|FileSafe|FileMetadata", "SN|FileSafe|Integration"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
if(isDesktopApplication()) {
|
||||
item.content.local_url = window._batch_manager_location;
|
||||
} else {
|
||||
item.content.hosted_url = window._batch_manager_location;
|
||||
}
|
||||
|
||||
var component = this.modelManager.createItem(item);
|
||||
this.modelManager.addItem(component);
|
||||
|
||||
this.modelManager.setItemDirty(component, true);
|
||||
this.syncManager.sync();
|
||||
|
||||
this.systemExtensions.push(component.uuid);
|
||||
|
||||
valueCallback(component);
|
||||
});
|
||||
return payload;
|
||||
}
|
||||
|
||||
async resolveBatchManager() {
|
||||
const contentTypePredicate = new SFPredicate('content_type', '=', ContentTypes.Component);
|
||||
const packagePredicate = new SFPredicate('package_info.identifier', '=', this.batchManagerId);
|
||||
const predicate = SFPredicate.CompoundPredicate([contentTypePredicate, packagePredicate]);
|
||||
const batchManager = await this.application.singletonManager.findOrCreateSingleton({
|
||||
predicate: predicate,
|
||||
createPayload: this.batchManagerTemplatePayload()
|
||||
});
|
||||
this.systemExtensions.push(batchManager.uuid);
|
||||
let needsSync = false;
|
||||
if (isDesktopApplication()) {
|
||||
if (!batchManager.local_url) {
|
||||
batchManager.local_url = window._batch_manager_location;
|
||||
needsSync = true;
|
||||
}
|
||||
} else {
|
||||
if (!batchManager.hosted_url) {
|
||||
batchManager.hosted_url = window._batch_manager_location;
|
||||
needsSync = true;
|
||||
}
|
||||
}
|
||||
// Handle addition of SN|ExtensionRepo permission
|
||||
const permission = batchManager.content.permissions.find((p) => p.name === STREAM_ITEMS_PERMISSION);
|
||||
if (!permission.content_types.includes(ContentTypes.ExtensionRepo)) {
|
||||
permission.content_types.push(ContentTypes.ExtensionRepo);
|
||||
needsSync = true;
|
||||
}
|
||||
if (needsSync) {
|
||||
this.application.saveItem({ item: batchManager });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
import { SFPredicate, SFItem } from 'snjs';
|
||||
import { SFPredicate, CreateMaxPayloadFromAnyObject } from 'snjs';
|
||||
import { AppStateEvents } from '../state';
|
||||
|
||||
export const PREF_TAGS_PANEL_WIDTH = 'tagsPanelWidth';
|
||||
export const PREF_NOTES_PANEL_WIDTH = 'notesPanelWidth';
|
||||
export const PREF_EDITOR_WIDTH = 'editorWidth';
|
||||
export const PREF_EDITOR_LEFT = 'editorLeft';
|
||||
export const PREF_EDITOR_MONOSPACE_ENABLED = 'monospaceFont';
|
||||
export const PREF_EDITOR_SPELLCHECK = 'spellcheck';
|
||||
export const PREF_EDITOR_RESIZERS_ENABLED = 'marginResizersEnabled';
|
||||
export const PREF_SORT_NOTES_BY = 'sortBy';
|
||||
export const PREF_SORT_NOTES_REVERSE = 'sortReverse';
|
||||
export const PREF_NOTES_SHOW_ARCHIVED = 'showArchived';
|
||||
export const PREF_NOTES_HIDE_PINNED = 'hidePinned';
|
||||
export const PREF_NOTES_HIDE_NOTE_PREVIEW = 'hideNotePreview';
|
||||
export const PREF_NOTES_HIDE_DATE = 'hideDate';
|
||||
export const PREF_NOTES_HIDE_TAGS = 'hideTags';
|
||||
export const PrefKeys = {
|
||||
TagsPanelWidth: 'tagsPanelWidth',
|
||||
NotesPanelWidth: 'notesPanelWidth',
|
||||
EditorWidth: 'editorWidth',
|
||||
EditorLeft: 'editorLeft',
|
||||
EditorMonospaceEnabled: 'monospaceFont',
|
||||
EditorSpellcheck: 'spellcheck',
|
||||
EditorResizersEnabled: 'marginResizersEnabled',
|
||||
SortNotesBy: 'sortBy',
|
||||
SortNotesReverse: 'sortReverse',
|
||||
NotesShowArchived: 'showArchived',
|
||||
NotesHidePinned: 'hidePinned',
|
||||
NotesHideNotePreview: 'hideNotePreview',
|
||||
NotesHideDate: 'hideDate',
|
||||
NotesHideTags: 'hideTags'
|
||||
};
|
||||
|
||||
export class PreferencesManager {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
modelManager,
|
||||
singletonManager,
|
||||
appState,
|
||||
syncManager
|
||||
application
|
||||
) {
|
||||
this.singletonManager = singletonManager;
|
||||
this.modelManager = modelManager;
|
||||
this.syncManager = syncManager;
|
||||
this.application = application;
|
||||
this.appState = appState;
|
||||
|
||||
this.modelManager.addItemSyncObserver(
|
||||
'user-prefs',
|
||||
'SN|UserPreferences',
|
||||
(allItems, validItems, deletedItems, source, sourceKey) => {
|
||||
this.preferencesDidChange();
|
||||
appState.addObserver(async (eventName) => {
|
||||
if (eventName === AppStateEvents.ApplicationReady) {
|
||||
await this.initialize();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
load() {
|
||||
const prefsContentType = 'SN|UserPreferences';
|
||||
const contentTypePredicate = new SFPredicate(
|
||||
'content_type',
|
||||
'=',
|
||||
prefsContentType
|
||||
);
|
||||
this.singletonManager.registerSingleton(
|
||||
[contentTypePredicate],
|
||||
(resolvedSingleton) => {
|
||||
this.userPreferences = resolvedSingleton;
|
||||
},
|
||||
(valueCallback) => {
|
||||
// Safe to create. Create and return object.
|
||||
const prefs = new SFItem({content_type: prefsContentType});
|
||||
this.modelManager.addItem(prefs);
|
||||
this.modelManager.setItemDirty(prefs);
|
||||
this.syncManager.sync();
|
||||
valueCallback(prefs);
|
||||
async initialize() {
|
||||
this.streamPreferences();
|
||||
await this.loadSingleton();
|
||||
}
|
||||
|
||||
streamPreferences() {
|
||||
this.application.streamItems({
|
||||
contentType: 'SN|UserPreferences',
|
||||
stream: () => {
|
||||
this.preferencesDidChange();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async loadSingleton() {
|
||||
const contentType = 'SN|UserPreferences';
|
||||
const predicate = new SFPredicate('content_type', '=', contentType);
|
||||
this.userPreferences = await this.application.singletonManager.findOrCreateSingleton({
|
||||
predicate: predicate,
|
||||
createPayload: CreateMaxPayloadFromAnyObject({
|
||||
object: {
|
||||
content_type: contentType
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
preferencesDidChange() {
|
||||
@@ -65,21 +65,20 @@ export class PreferencesManager {
|
||||
}
|
||||
|
||||
syncUserPreferences() {
|
||||
if(this.userPreferences) {
|
||||
this.modelManager.setItemDirty(this.userPreferences);
|
||||
this.syncManager.sync();
|
||||
if (this.userPreferences) {
|
||||
this.application.saveItem({item: this.userPreferences});
|
||||
}
|
||||
}
|
||||
|
||||
getValue(key, defaultValue) {
|
||||
if(!this.userPreferences) { return defaultValue; }
|
||||
if (!this.userPreferences) { return defaultValue; }
|
||||
const value = this.userPreferences.getAppDataItem(key);
|
||||
return (value !== undefined && value != null) ? value : defaultValue;
|
||||
}
|
||||
|
||||
setUserPrefValue(key, value, sync) {
|
||||
this.userPreferences.setAppDataItem(key, value);
|
||||
if(sync) {
|
||||
if (sync) {
|
||||
this.syncUserPreferences();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,28 @@
|
||||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
import { SNTheme, SFItemParams } from 'snjs';
|
||||
import { StorageManager } from './storageManager';
|
||||
import {
|
||||
APP_STATE_EVENT_DESKTOP_EXTS_READY
|
||||
} from '@/state';
|
||||
import { SNTheme, StorageValueModes, EncryptionIntents } from 'snjs';
|
||||
import { AppStateEvents } from '@/state';
|
||||
|
||||
const CACHED_THEMES_KEY = 'cachedThemes';
|
||||
|
||||
export class ThemeManager {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
componentManager,
|
||||
application,
|
||||
appState,
|
||||
desktopManager,
|
||||
storageManager,
|
||||
lockManager,
|
||||
appState
|
||||
) {
|
||||
this.componentManager = componentManager;
|
||||
this.storageManager = storageManager;
|
||||
this.application = application;
|
||||
this.appState = appState;
|
||||
this.desktopManager = desktopManager;
|
||||
this.activeThemes = [];
|
||||
|
||||
ThemeManager.CachedThemesKey = "cachedThemes";
|
||||
|
||||
this.registerObservers();
|
||||
|
||||
if (desktopManager.isDesktop) {
|
||||
appState.addObserver((eventName, data) => {
|
||||
if (eventName === APP_STATE_EVENT_DESKTOP_EXTS_READY) {
|
||||
this.activateCachedThemes();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (!desktopManager.isDesktop) {
|
||||
this.activateCachedThemes();
|
||||
}
|
||||
}
|
||||
|
||||
activateCachedThemes() {
|
||||
const cachedThemes = this.getCachedThemes();
|
||||
async activateCachedThemes() {
|
||||
const cachedThemes = await this.getCachedThemes();
|
||||
const writeToCache = false;
|
||||
for (const theme of cachedThemes) {
|
||||
this.activateTheme(theme, writeToCache);
|
||||
@@ -44,6 +30,11 @@ 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()) {
|
||||
@@ -54,9 +45,9 @@ export class ThemeManager {
|
||||
}
|
||||
});
|
||||
|
||||
this.componentManager.registerHandler({
|
||||
identifier: "themeManager",
|
||||
areas: ["themes"],
|
||||
this.application.componentManager.registerHandler({
|
||||
identifier: 'themeManager',
|
||||
areas: ['themes'],
|
||||
activationHandler: (component) => {
|
||||
if (component.active) {
|
||||
this.activateTheme(component);
|
||||
@@ -68,14 +59,14 @@ export class ThemeManager {
|
||||
}
|
||||
|
||||
hasActiveTheme() {
|
||||
return this.componentManager.getActiveThemes().length > 0;
|
||||
return this.application.componentManager.getActiveThemes().length > 0;
|
||||
}
|
||||
|
||||
deactivateAllThemes() {
|
||||
var activeThemes = this.componentManager.getActiveThemes();
|
||||
var activeThemes = this.application.componentManager.getActiveThemes();
|
||||
for (var theme of activeThemes) {
|
||||
if (theme) {
|
||||
this.componentManager.deactivateComponent(theme);
|
||||
this.application.componentManager.deactivateComponent(theme);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,25 +77,22 @@ export class ThemeManager {
|
||||
if (_.find(this.activeThemes, { uuid: theme.uuid })) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeThemes.push(theme);
|
||||
|
||||
var url = this.componentManager.urlForComponent(theme);
|
||||
var link = document.createElement("link");
|
||||
const url = this.application.componentManager.urlForComponent(theme);
|
||||
const link = document.createElement('link');
|
||||
link.href = url;
|
||||
link.type = "text/css";
|
||||
link.rel = "stylesheet";
|
||||
link.media = "screen,print";
|
||||
link.type = 'text/css';
|
||||
link.rel = 'stylesheet';
|
||||
link.media = 'screen,print';
|
||||
link.id = theme.uuid;
|
||||
document.getElementsByTagName("head")[0].appendChild(link);
|
||||
|
||||
document.getElementsByTagName('head')[0].appendChild(link);
|
||||
if (writeToCache) {
|
||||
this.cacheThemes();
|
||||
}
|
||||
}
|
||||
|
||||
deactivateTheme(theme) {
|
||||
var element = document.getElementById(theme.uuid);
|
||||
const element = document.getElementById(theme.uuid);
|
||||
if (element) {
|
||||
element.disabled = true;
|
||||
element.parentNode.removeChild(element);
|
||||
@@ -117,20 +105,33 @@ export class ThemeManager {
|
||||
|
||||
async cacheThemes() {
|
||||
const mapped = await Promise.all(this.activeThemes.map(async (theme) => {
|
||||
const transformer = new SFItemParams(theme);
|
||||
const params = await transformer.paramsForLocalStorage();
|
||||
return params;
|
||||
const payload = theme.payloadRepresentation();
|
||||
const processedPayload = await this.application.protocolService.payloadByEncryptingPayload({
|
||||
payload: payload,
|
||||
intent: EncryptionIntents.LocalStorageDecrypted
|
||||
});
|
||||
return processedPayload;
|
||||
}));
|
||||
const data = JSON.stringify(mapped);
|
||||
return this.storageManager.setItem(ThemeManager.CachedThemesKey, data, StorageManager.Fixed);
|
||||
return this.application.setValue(
|
||||
CACHED_THEMES_KEY,
|
||||
data,
|
||||
StorageValueModes.Nonwrapped
|
||||
);
|
||||
}
|
||||
|
||||
async decacheThemes() {
|
||||
return this.storageManager.removeItem(ThemeManager.CachedThemesKey, StorageManager.Fixed);
|
||||
return this.application.removeValue(
|
||||
CACHED_THEMES_KEY,
|
||||
StorageValueModes.Nonwrapped
|
||||
);
|
||||
}
|
||||
|
||||
getCachedThemes() {
|
||||
const cachedThemes = this.storageManager.getItemSync(ThemeManager.CachedThemesKey, StorageManager.Fixed);
|
||||
async getCachedThemes() {
|
||||
const cachedThemes = await this.application.getValue(
|
||||
CACHED_THEMES_KEY,
|
||||
StorageValueModes.Nonwrapped
|
||||
);
|
||||
if (cachedThemes) {
|
||||
const parsed = JSON.parse(cachedThemes);
|
||||
return parsed.map((theme) => {
|
||||
|
||||
@@ -1,32 +1,39 @@
|
||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import pull from 'lodash/pull';
|
||||
import { ProtectedActions } from 'snjs';
|
||||
|
||||
export const APP_STATE_EVENT_TAG_CHANGED = 1;
|
||||
export const APP_STATE_EVENT_NOTE_CHANGED = 2;
|
||||
export const APP_STATE_EVENT_PREFERENCES_CHANGED = 3;
|
||||
export const APP_STATE_EVENT_PANEL_RESIZED = 4;
|
||||
export const APP_STATE_EVENT_EDITOR_FOCUSED = 5;
|
||||
export const APP_STATE_EVENT_BEGAN_BACKUP_DOWNLOAD = 6;
|
||||
export const APP_STATE_EVENT_ENDED_BACKUP_DOWNLOAD = 7;
|
||||
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 AppStateEvents = {
|
||||
TagChanged: 1,
|
||||
NoteChanged: 2,
|
||||
PreferencesChanged: 3,
|
||||
PanelResized: 4,
|
||||
EditorFocused: 5,
|
||||
BeganBackupDownload: 6,
|
||||
EndedBackupDownload: 7,
|
||||
DesktopExtsReady: 8,
|
||||
WindowDidFocus: 9,
|
||||
WindowDidBlur: 10,
|
||||
/** Register observers and streamers on this event */
|
||||
ApplicationReady: 11
|
||||
};
|
||||
|
||||
export const EVENT_SOURCE_USER_INTERACTION = 1;
|
||||
export const EVENT_SOURCE_SCRIPT = 2;
|
||||
export const EventSources = {
|
||||
UserInteraction: 1,
|
||||
Script: 2
|
||||
};
|
||||
|
||||
export class AppState {
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
$timeout,
|
||||
$timeout,
|
||||
$rootScope,
|
||||
privilegesManager
|
||||
application,
|
||||
|
||||
) {
|
||||
this.$timeout = $timeout;
|
||||
this.$rootScope = $rootScope;
|
||||
this.privilegesManager = privilegesManager;
|
||||
this.application = application;
|
||||
this.observers = [];
|
||||
this.registerVisibilityObservers();
|
||||
}
|
||||
@@ -34,18 +41,18 @@ export class AppState {
|
||||
registerVisibilityObservers() {
|
||||
if (isDesktopApplication()) {
|
||||
this.$rootScope.$on('window-lost-focus', () => {
|
||||
this.notifyEvent(APP_STATE_EVENT_WINDOW_DID_BLUR);
|
||||
this.notifyEvent(AppStateEvents.WindowDidBlur);
|
||||
});
|
||||
this.$rootScope.$on('window-gained-focus', () => {
|
||||
this.notifyEvent(APP_STATE_EVENT_WINDOW_DID_FOCUS);
|
||||
this.notifyEvent(AppStateEvents.WindowDidFocus);
|
||||
});
|
||||
} 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;
|
||||
? AppStateEvents.WindowDidFocus
|
||||
: AppStateEvents.WindowDidBlur;
|
||||
this.notifyEvent(event);
|
||||
});
|
||||
}
|
||||
@@ -66,7 +73,7 @@ export class AppState {
|
||||
*/
|
||||
return new Promise((resolve) => {
|
||||
this.$timeout(async () => {
|
||||
for(const callback of this.observers) {
|
||||
for (const callback of this.observers) {
|
||||
await callback(eventName, data);
|
||||
}
|
||||
resolve();
|
||||
@@ -75,14 +82,14 @@ export class AppState {
|
||||
}
|
||||
|
||||
setSelectedTag(tag) {
|
||||
if(this.selectedTag === tag) {
|
||||
if (this.selectedTag === tag) {
|
||||
return;
|
||||
}
|
||||
const previousTag = this.selectedTag;
|
||||
this.selectedTag = tag;
|
||||
this.notifyEvent(
|
||||
APP_STATE_EVENT_TAG_CHANGED,
|
||||
{previousTag: previousTag}
|
||||
AppStateEvents.TagChanged,
|
||||
{ previousTag: previousTag }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -91,16 +98,16 @@ export class AppState {
|
||||
const previousNote = this.selectedNote;
|
||||
this.selectedNote = note;
|
||||
await this.notifyEvent(
|
||||
APP_STATE_EVENT_NOTE_CHANGED,
|
||||
AppStateEvents.NoteChanged,
|
||||
{ previousNote: previousNote }
|
||||
);
|
||||
};
|
||||
if (note && note.content.protected &&
|
||||
await this.privilegesManager.actionRequiresPrivilege(
|
||||
PrivilegesManager.ActionViewProtectedNotes
|
||||
await this.application.privilegesManager.actionRequiresPrivilege(
|
||||
ProtectedActions.ViewProtectedNotes
|
||||
)) {
|
||||
this.godService.presentPrivilegesModal(
|
||||
PrivilegesManager.ActionViewProtectedNotes,
|
||||
ProtectedActions.ViewProtectedNotes,
|
||||
run
|
||||
);
|
||||
} else {
|
||||
@@ -119,13 +126,13 @@ export class AppState {
|
||||
setUserPreferences(preferences) {
|
||||
this.userPreferences = preferences;
|
||||
this.notifyEvent(
|
||||
APP_STATE_EVENT_PREFERENCES_CHANGED
|
||||
AppStateEvents.PreferencesChanged
|
||||
);
|
||||
}
|
||||
|
||||
panelDidResize({name, collapsed}) {
|
||||
panelDidResize({ name, collapsed }) {
|
||||
this.notifyEvent(
|
||||
APP_STATE_EVENT_PANEL_RESIZED,
|
||||
AppStateEvents.PanelResized,
|
||||
{
|
||||
panel: name,
|
||||
collapsed: collapsed
|
||||
@@ -135,21 +142,21 @@ export class AppState {
|
||||
|
||||
editorDidFocus(eventSource) {
|
||||
this.notifyEvent(
|
||||
APP_STATE_EVENT_EDITOR_FOCUSED,
|
||||
{eventSource: eventSource}
|
||||
AppStateEvents.EditorFocused,
|
||||
{ eventSource: eventSource }
|
||||
);
|
||||
}
|
||||
|
||||
beganBackupDownload() {
|
||||
this.notifyEvent(
|
||||
APP_STATE_EVENT_BEGAN_BACKUP_DOWNLOAD
|
||||
AppStateEvents.BeganBackupDownload
|
||||
);
|
||||
}
|
||||
|
||||
endedBackupDownload({success}) {
|
||||
endedBackupDownload({ success }) {
|
||||
this.notifyEvent(
|
||||
APP_STATE_EVENT_ENDED_BACKUP_DOWNLOAD,
|
||||
{success: success}
|
||||
AppStateEvents.EndedBackupDownload,
|
||||
{ success: success }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -158,7 +165,7 @@ export class AppState {
|
||||
*/
|
||||
desktopExtensionsReady() {
|
||||
this.notifyEvent(
|
||||
APP_STATE_EVENT_DESKTOP_EXTS_READY
|
||||
AppStateEvents.DesktopExtsReady
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
/** @generic */
|
||||
export const STRING_SESSION_EXPIRED = "Your session has expired. New changes will not be pulled in. Please sign out and sign back in to refresh your session.";
|
||||
export const STRING_DEFAULT_FILE_ERROR = "Please use FileSafe or the Bold Editor to attach images and files. Learn more at standardnotes.org/filesafe.";
|
||||
export const STRING_GENERIC_SYNC_ERROR = "There was an error syncing. Please try again. If all else fails, try signing out and signing back in.";
|
||||
export const STRING_SESSION_EXPIRED = "Your session has expired. New changes will not be pulled in. Please sign out and sign back in to refresh your session.";
|
||||
export const STRING_DEFAULT_FILE_ERROR = "Please use FileSafe or the Bold Editor to attach images and files. Learn more at standardnotes.org/filesafe.";
|
||||
export const STRING_GENERIC_SYNC_ERROR = "There was an error syncing. Please try again. If all else fails, try signing out and signing back in.";
|
||||
export function StringSyncException(data) {
|
||||
return `There was an error while trying to save your items. Please contact support and share this message: ${data}.`;
|
||||
}
|
||||
|
||||
/** @footer */
|
||||
export const STRING_NEW_UPDATE_READY = "A new update is ready to install. Please use the top-level 'Updates' menu to manage installation.";
|
||||
export const STRING_NEW_UPDATE_READY = "A new update is ready to install. Please use the top-level 'Updates' menu to manage installation.";
|
||||
|
||||
/** @tags */
|
||||
export const STRING_DELETE_TAG = "Are you sure you want to delete this tag? Note: deleting a tag will not delete its notes.";
|
||||
export const STRING_DELETE_TAG = "Are you sure you want to delete this tag? Note: deleting a tag will not delete its notes.";
|
||||
|
||||
/** @editor */
|
||||
export const STRING_DELETED_NOTE = "The note you are attempting to edit has been deleted, and is awaiting sync. Changes you make will be disregarded.";
|
||||
export const STRING_INVALID_NOTE = "The note you are attempting to save can not be found or has been deleted. Changes you make will not be synced. Please copy this note's text and start a new note.";
|
||||
export const STRING_ELLIPSES = "...";
|
||||
export const STRING_GENERIC_SAVE_ERROR = "There was an error saving your note. Please try again.";
|
||||
export const STRING_DELETED_NOTE = "The note you are attempting to edit has been deleted, and is awaiting sync. Changes you make will be disregarded.";
|
||||
export const STRING_INVALID_NOTE = "The note you are attempting to save can not be found or has been deleted. Changes you make will not be synced. Please copy this note's text and start a new note.";
|
||||
export const STRING_ELLIPSES = "...";
|
||||
export const STRING_GENERIC_SAVE_ERROR = "There was an error saving your note. Please try again.";
|
||||
export const STRING_DELETE_PLACEHOLDER_ATTEMPT = "This note is a placeholder and cannot be deleted. To remove from your list, simply navigate to a different note.";
|
||||
export const STRING_DELETE_LOCKED_ATTEMPT = "This note is locked. If you'd like to delete it, unlock it, and try again.";
|
||||
export function StringDeleteNote({title, permanently}) {
|
||||
export const STRING_DELETE_LOCKED_ATTEMPT = "This note is locked. If you'd like to delete it, unlock it, and try again.";
|
||||
export function StringDeleteNote({ title, permanently }) {
|
||||
return permanently
|
||||
? `Are you sure you want to permanently delete ${title}?`
|
||||
: `Are you sure you want to move ${title} to the trash?`;
|
||||
}
|
||||
export function StringEmptyTrash({count}) {
|
||||
export function StringEmptyTrash({ count }) {
|
||||
return `Are you sure you want to permanently delete ${count} note(s)?`;
|
||||
}
|
||||
|
||||
/** @account */
|
||||
export const STRING_ACCOUNT_MENU_UNCHECK_MERGE = "Unchecking this option means any of the notes you have written while you were signed out will be deleted. Are you sure you want to discard these notes?";
|
||||
export const STRING_SIGN_OUT_CONFIRMATION = "Are you sure you want to end your session? This will delete all local items and extensions.";
|
||||
export const STRING_ERROR_DECRYPTING_IMPORT = "There was an error decrypting your items. Make sure the password you entered is correct and try again.";
|
||||
export const STRING_E2E_ENABLED = "End-to-end encryption is enabled. Your data is encrypted on your device first, then synced to your private cloud.";
|
||||
export const STRING_LOCAL_ENC_ENABLED = "Encryption is enabled. Your data is encrypted using your passcode before it is saved to your device storage.";
|
||||
export const STRING_ENC_NOT_ENABLED = "Encryption is not enabled. Sign in, register, or add a passcode lock to enable encryption.";
|
||||
export const STRING_IMPORT_SUCCESS = "Your data has been successfully imported.";
|
||||
export const STRING_REMOVE_PASSCODE_CONFIRMATION = "Are you sure you want to remove your local passcode?";
|
||||
export const STRING_REMOVE_PASSCODE_OFFLINE_ADDENDUM = " This will remove encryption from your local data.";
|
||||
export const STRING_NON_MATCHING_PASSCODES = "The two passcodes you entered do not match. Please try again.";
|
||||
export const STRING_NON_MATCHING_PASSWORDS = "The two passwords you entered do not match. Please try again.";
|
||||
export const STRING_GENERATING_LOGIN_KEYS = "Generating Login Keys...";
|
||||
export const STRING_GENERATING_REGISTER_KEYS = "Generating Account Keys...";
|
||||
export const STRING_INVALID_IMPORT_FILE = "Unable to open file. Ensure it is a proper JSON file and try again.";
|
||||
export function StringImportError({errorCount}) {
|
||||
export const STRING_ACCOUNT_MENU_UNCHECK_MERGE = "Unchecking this option means any of the notes you have written while you were signed out will be deleted. Are you sure you want to discard these notes?";
|
||||
export const STRING_SIGN_OUT_CONFIRMATION = "Are you sure you want to end your session? This will delete all local items and extensions.";
|
||||
export const STRING_ERROR_DECRYPTING_IMPORT = "There was an error decrypting your items. Make sure the password you entered is correct and try again.";
|
||||
export const STRING_E2E_ENABLED = "End-to-end encryption is enabled. Your data is encrypted on your device first, then synced to your private cloud.";
|
||||
export const STRING_LOCAL_ENC_ENABLED = "Encryption is enabled. Your data is encrypted using your passcode before it is saved to your device storage.";
|
||||
export const STRING_ENC_NOT_ENABLED = "Encryption is not enabled. Sign in, register, or add a passcode lock to enable encryption.";
|
||||
export const STRING_IMPORT_SUCCESS = "Your data has been successfully imported.";
|
||||
export const STRING_REMOVE_PASSCODE_CONFIRMATION = "Are you sure you want to remove your local passcode?";
|
||||
export const STRING_REMOVE_PASSCODE_OFFLINE_ADDENDUM = " This will remove encryption from your local data.";
|
||||
export const STRING_NON_MATCHING_PASSCODES = "The two passcodes you entered do not match. Please try again.";
|
||||
export const STRING_NON_MATCHING_PASSWORDS = "The two passwords you entered do not match. Please try again.";
|
||||
export const STRING_GENERATING_LOGIN_KEYS = "Generating Login Keys...";
|
||||
export const STRING_GENERATING_REGISTER_KEYS = "Generating Account Keys...";
|
||||
export const STRING_INVALID_IMPORT_FILE = "Unable to open file. Ensure it is a proper JSON file and try again.";
|
||||
export function StringImportError({ errorCount }) {
|
||||
return `Import complete. ${errorCount} items were not imported because there was an error decrypting them. Make sure the password is correct and try again.`;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ export function isNullOrUndefined(value) {
|
||||
return value === null || value === undefined;
|
||||
}
|
||||
|
||||
export function dictToArray(dict) {
|
||||
return Object.keys(dict).map((key) => dict[key]);
|
||||
}
|
||||
|
||||
export function getPlatformString() {
|
||||
try {
|
||||
const platform = navigator.platform.toLowerCase();
|
||||
|
||||
11440
dist/javascripts/app.js
vendored
11440
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