This commit is contained in:
Mo Bitar
2020-02-08 12:11:04 -06:00
parent 13dd41b9ab
commit f6ef4a39e2
26 changed files with 10885 additions and 2044 deletions

View File

@@ -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": {

View File

@@ -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);

View File

@@ -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';

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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 () => {

View File

@@ -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();
}
});
@@ -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();
@@ -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
@@ -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();

View File

@@ -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);

View File

@@ -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,
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
);
@@ -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,

View File

@@ -4,7 +4,6 @@ import { PureCtrl } from '@Controllers';
class ActionsMenuCtrl extends PureCtrl {
/* @ngInject */
constructor(
$scope,
$timeout,
actionsManager,
godService

View File

@@ -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()(
@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,11 +48,8 @@ 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(/^(.*?) \(/);
@@ -64,15 +59,19 @@ export class ArchiveManager {
return string;
}
async __itemsData(items, keys, authParams) {
const data = await this.modelManager.getJSONDataForItems(items, keys, authParams);
/** @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) {
/** @private */
async loadZip() {
if (window.zip) {
callback();
return;
}
@@ -81,14 +80,17 @@ export class ArchiveManager {
scriptTag.async = false;
var headTag = document.getElementsByTagName('head')[0];
headTag.appendChild(scriptTag);
return new Promise((resolve, reject) => {
scriptTag.onload = function () {
zip.workerScriptsPath = "assets/zip/";
callback();
resolve();
};
});
}
__downloadZippedItems(items) {
this.__loadZip(() => {
/** @private */
async downloadZippedItems(items) {
await this.loadZip();
zip.createWriter(new zip.BlobWriter("application/zip"), (zipWriter) => {
var index = 0;
@@ -120,7 +122,7 @@ export class ArchiveManager {
nextFile();
} else {
zipWriter.close((blob) => {
this.__downloadData(blob, `Standard Notes Backup - ${this.__formattedDate()}.zip`);
this.downloadData(blob, `Standard Notes Backup - ${this.formattedDate()}.zip`);
zipWriter = null;
});
}
@@ -129,27 +131,26 @@ export class ArchiveManager {
nextFile();
}, onerror);
});
}
__hrefForData(data) {
/** @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();

View File

@@ -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,32 +13,25 @@ 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) {
this.dataLoadHandler();
}
});
$rootScope.$on("major-data-change", () => {
$rootScope.$on('major-data-change', () => {
if (this.majorDataChangeHandler) {
this.majorDataChangeHandler();
}
@@ -56,12 +50,15 @@ 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
@@ -105,7 +102,7 @@ export class DesktopManager {
}
deregisterUpdateObserver(observer) {
_.pull(this.updateObservers, observer);
pull(this.updateObservers, observer);
}
// Pass null to cancel search
@@ -114,15 +111,15 @@ 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);
async desktop_onComponentInstallationComplete(componentData, error) {
const component = await this.application.findItem({ uuid: componentData.uuid });
if (!component) {
return;
}
@@ -139,18 +136,13 @@ export class DesktopManager {
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(() => {
this.application.saveItem({ item: component });
this.$timeout(() => {
for (const observer of this.updateObservers) {
observer.callback(component);
}
@@ -164,7 +156,7 @@ export class DesktopManager {
}
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(() => {
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();
@@ -201,22 +193,10 @@ export class DesktopManager {
}
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) {

View File

@@ -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';

View File

@@ -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;
@@ -48,7 +47,7 @@ export class KeyboardManager {
eventMatchesKeyAndModifiers(event, key, modifiers = []) {
const eventModifiers = this.modifiersForEvent(event);
if(eventModifiers.length != modifiers.length) {
if (eventModifiers.length !== modifiers.length) {
return false;
}
@@ -65,12 +64,12 @@ export class KeyboardManager {
// 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) {
if (observer.element && event.target !== observer.element) {
continue;
}
@@ -78,7 +77,7 @@ export class KeyboardManager {
continue;
}
if(observer.notElement && observer.notElement == event.target) {
if (observer.notElement && observer.notElement === event.target) {
continue;
}
@@ -87,7 +86,7 @@ export class KeyboardManager {
}
if (this.eventMatchesKeyAndModifiers(event, observer.key, observer.modifiers)) {
const callback = keyEventType == KeyboardManager.KeyEventDown ? observer.onKeyDown : observer.onKeyUp;
const callback = keyEventType === KeyboardKeyEvents.Down ? observer.onKeyDown : observer.onKeyUp;
if (callback) {
callback(event);
}
@@ -96,11 +95,11 @@ 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 }) {

View File

@@ -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);
}
});

View File

@@ -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() {
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;
}
async initialize() {
this.resolveExtensionsManager();
this.resolveBatchManager();
}
// 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");
needsSync = true;
}
if(needsSync) {
this.modelManager.setItemDirty(resolvedSingleton, true);
this.syncManager.sync();
}
}, (valueCallback) => {
// Safe to create. Create and return object.
extensionsManagerTemplatePayload() {
const url = window._extensions_manager_location;
if (!url) {
console.error("window._extensions_manager_location must be set.");
console.error('window._extensions_manager_location must be set.');
return;
}
const packageInfo = {
name: "Extensions",
name: 'Extensions',
identifier: this.extManagerId
};
var item = {
content_type: "SN|Component",
content: {
const content = {
name: packageInfo.name,
area: "rooms",
area: 'rooms',
package_info: packageInfo,
permissions: [
{
name: "stream-items",
name: STREAM_ITEMS_PERMISSION,
content_types: [
"SN|Component", "SN|Theme", "SF|Extension",
"Extension", "SF|MFA", "SN|Editor", "SN|ExtensionRepo"
ContentTypes.Component,
ContentTypes.Theme,
ContentTypes.ServerExtension,
ContentTypes.ActionsExtension,
ContentTypes.Mfa,
ContentTypes.Editor,
ContentTypes.ExtensionRepo
]
}
]
}
};
if (isDesktopApplication()) {
item.content.local_url = window._extensions_manager_location;
content.local_url = window._extensions_manager_location;
} else {
item.content.hosted_url = window._extensions_manager_location;
content.hosted_url = window._extensions_manager_location;
}
const payload = CreateMaxPayloadFromAnyObject({
object: {
content_type: ContentTypes.Component,
content: content
}
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;
}
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;
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(!resolvedSingleton.local_url) {
resolvedSingleton.local_url = window._batch_manager_location;
if (!extensionsManager.local_url) {
extensionsManager.local_url = window._extensions_manager_location;
needsSync = true;
}
} else {
if(!resolvedSingleton.hosted_url) {
resolvedSingleton.hosted_url = window._batch_manager_location;
if (!extensionsManager.hosted_url) {
extensionsManager.hosted_url = window._extensions_manager_location;
needsSync = true;
}
}
if(needsSync) {
this.modelManager.setItemDirty(resolvedSingleton, true);
this.syncManager.sync();
// 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;
}
}, (valueCallback) => {
// Safe to create. Create and return object.
if (needsSync) {
this.application.saveItem({ item: extensionsManager });
}
}
batchManagerTemplatePayload() {
const url = window._batch_manager_location;
if (!url) {
console.error("window._batch_manager_location must be set.");
console.error('window._batch_manager_location must be set.');
return;
}
const packageInfo = {
name: "Batch Manager",
name: 'Batch Manager',
identifier: this.batchManagerId
};
var item = {
content_type: "SN|Component",
content: {
const allContentTypes = dictToArray(ContentTypes);
const content = {
name: packageInfo.name,
area: "modal",
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"
]
name: STREAM_ITEMS_PERMISSION,
content_types: allContentTypes
}
]
}
};
if (isDesktopApplication()) {
item.content.local_url = window._batch_manager_location;
content.local_url = window._batch_manager_location;
} else {
item.content.hosted_url = window._batch_manager_location;
content.hosted_url = window._batch_manager_location;
}
const payload = CreateMaxPayloadFromAnyObject({
object: {
content_type: ContentTypes.Component,
content: content
}
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 });
}
}
}

View File

@@ -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;
appState.addObserver(async (eventName) => {
if (eventName === AppStateEvents.ApplicationReady) {
await this.initialize();
}
});
}
this.modelManager.addItemSyncObserver(
'user-prefs',
'SN|UserPreferences',
(allItems, validItems, deletedItems, source, sourceKey) => {
async initialize() {
this.streamPreferences();
await this.loadSingleton();
}
streamPreferences() {
this.application.streamItems({
contentType: 'SN|UserPreferences',
stream: () => {
this.preferencesDidChange();
}
);
});
}
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 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() {
@@ -66,8 +66,7 @@ export class PreferencesManager {
syncUserPreferences() {
if (this.userPreferences) {
this.modelManager.setItemDirty(this.userPreferences);
this.syncManager.sync();
this.application.saveItem({item: this.userPreferences});
}
}

View File

@@ -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) => {

View File

@@ -1,20 +1,26 @@
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 {
@@ -22,11 +28,12 @@ export class AppState {
constructor(
$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);
});
}
@@ -81,7 +88,7 @@ export class AppState {
const previousTag = this.selectedTag;
this.selectedTag = tag;
this.notifyEvent(
APP_STATE_EVENT_TAG_CHANGED,
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 }) {
this.notifyEvent(
APP_STATE_EVENT_PANEL_RESIZED,
AppStateEvents.PanelResized,
{
panel: name,
collapsed: collapsed
@@ -135,20 +142,20 @@ export class AppState {
editorDidFocus(eventSource) {
this.notifyEvent(
APP_STATE_EVENT_EDITOR_FOCUSED,
AppStateEvents.EditorFocused,
{ eventSource: eventSource }
);
}
beganBackupDownload() {
this.notifyEvent(
APP_STATE_EVENT_BEGAN_BACKUP_DOWNLOAD
AppStateEvents.BeganBackupDownload
);
}
endedBackupDownload({ success }) {
this.notifyEvent(
APP_STATE_EVENT_ENDED_BACKUP_DOWNLOAD,
AppStateEvents.EndedBackupDownload,
{ success: success }
);
}
@@ -158,7 +165,7 @@ export class AppState {
*/
desktopExtensionsReady() {
this.notifyEvent(
APP_STATE_EVENT_DESKTOP_EXTS_READY
AppStateEvents.DesktopExtsReady
);
}

View File

@@ -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();

11212
dist/javascripts/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long