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": { "rules": {
"standard/no-callback-literal": 0, // Disable this as we have too many callbacks relying on literals "standard/no-callback-literal": 0, // Disable this as we have too many callbacks relying on literals
"no-throw-literal": 0, "no-throw-literal": 0,
"no-console": "error", // "no-console": "error",
"semi": 1 "semi": 1
}, },
"env": { "env": {

View File

@@ -54,24 +54,13 @@ import {
import { trusted } from './filters'; import { trusted } from './filters';
import { import {
ActionsManager,
ArchiveManager, ArchiveManager,
AuthManager,
ComponentManager,
DatabaseManager, DatabaseManager,
DesktopManager, DesktopManager,
HttpManager,
KeyboardManager, KeyboardManager,
MigrationManager,
ModelManager,
NativeExtManager, NativeExtManager,
LockManager, LockManager,
PrivilegesManager,
SessionHistory,
SingletonManager,
StatusManager, StatusManager,
StorageManager,
SyncManager,
ThemeManager, ThemeManager,
AlertManager, AlertManager,
PreferencesManager PreferencesManager
@@ -148,23 +137,13 @@ angular
.service('application', Application) .service('application', Application)
.service('appState', AppState) .service('appState', AppState)
.service('preferencesManager', PreferencesManager) .service('preferencesManager', PreferencesManager)
.service('actionsManager', ActionsManager)
.service('archiveManager', ArchiveManager) .service('archiveManager', ArchiveManager)
.service('authManager', AuthManager)
.service('componentManager', ComponentManager)
.service('databaseManager', DatabaseManager) .service('databaseManager', DatabaseManager)
.service('desktopManager', DesktopManager) .service('desktopManager', DesktopManager)
.service('httpManager', HttpManager)
.service('keyboardManager', KeyboardManager) .service('keyboardManager', KeyboardManager)
.service('migrationManager', MigrationManager)
.service('modelManager', ModelManager)
.service('nativeExtManager', NativeExtManager) .service('nativeExtManager', NativeExtManager)
.service('lockManager', LockManager) .service('lockManager', LockManager)
.service('privilegesManager', PrivilegesManager)
.service('sessionHistory', SessionHistory)
.service('singletonManager', SingletonManager)
.service('statusManager', StatusManager) .service('statusManager', StatusManager)
.service('storageManager', StorageManager) .service('storageManager', StorageManager)
.service('syncManager', SyncManager)
.service('alertManager', AlertManager) .service('alertManager', AlertManager)
.service('themeManager', ThemeManager); .service('themeManager', ThemeManager);

View File

@@ -1,11 +1,10 @@
import { import {
SNApplication, SNApplication,
SNAlertManager SNAlertManager,
Platforms, Platforms
Environments
} from 'snjs'; } from 'snjs';
import angular from 'angular'; import angular from 'angular';
import { AlertManager } from '@/services/alertManager' import { AlertManager } from '@/services/alertManager';
import { WebDeviceInterface } from '@/web_device_interface'; import { WebDeviceInterface } from '@/web_device_interface';

View File

@@ -2,37 +2,24 @@ import angular from 'angular';
import { import {
ApplicationEvents, ApplicationEvents,
isPayloadSourceRetrieved, isPayloadSourceRetrieved,
CONTENT_TYPE_NOTE, ContentTypes,
CONTENT_TYPE_TAG,
CONTENT_TYPE_COMPONENT,
ProtectedActions ProtectedActions
} from 'snjs'; } from 'snjs';
import find from 'lodash/find';
import { isDesktopApplication } from '@/utils'; import { isDesktopApplication } from '@/utils';
import { KeyboardManager } from '@/services/keyboardManager';
import template from '%/editor.pug'; import template from '%/editor.pug';
import { PureCtrl } from '@Controllers'; import { PureCtrl } from '@Controllers';
import { import { AppStateEvents, EventSources } from '@/state';
APP_STATE_EVENT_NOTE_CHANGED,
APP_STATE_EVENT_PREFERENCES_CHANGED,
EVENT_SOURCE_SCRIPT
} from '@/state';
import { import {
STRING_DELETED_NOTE, STRING_DELETED_NOTE,
STRING_INVALID_NOTE, STRING_INVALID_NOTE,
STRING_ELLIPSES, STRING_ELLIPSES,
STRING_GENERIC_SAVE_ERROR,
STRING_DELETE_PLACEHOLDER_ATTEMPT, STRING_DELETE_PLACEHOLDER_ATTEMPT,
STRING_DELETE_LOCKED_ATTEMPT, STRING_DELETE_LOCKED_ATTEMPT,
StringDeleteNote, StringDeleteNote,
StringEmptyTrash StringEmptyTrash
} from '@/strings'; } from '@/strings';
import { import { PrefKeys } from '@/services/preferencesManager';
PREF_EDITOR_WIDTH,
PREF_EDITOR_LEFT,
PREF_EDITOR_MONOSPACE_ENABLED,
PREF_EDITOR_SPELLCHECK,
PREF_EDITOR_RESIZERS_ENABLED
} from '@/services/preferencesManager';
const NOTE_PREVIEW_CHAR_LIMIT = 80; const NOTE_PREVIEW_CHAR_LIMIT = 80;
const MINIMUM_STATUS_DURATION = 400; const MINIMUM_STATUS_DURATION = 400;
@@ -40,19 +27,23 @@ const SAVE_TIMEOUT_DEBOUNCE = 350;
const SAVE_TIMEOUT_NO_DEBOUNCE = 100; const SAVE_TIMEOUT_NO_DEBOUNCE = 100;
const EDITOR_DEBOUNCE = 200; const EDITOR_DEBOUNCE = 200;
const APP_DATA_KEY_PINNED = 'pinned'; const AppDataKeys = {
const APP_DATA_KEY_LOCKED = 'locked'; Pinned: 'pinned',
const APP_DATA_KEY_ARCHIVED = 'archived'; Locked: 'locked',
const APP_DATA_KEY_PREFERS_PLAIN_EDITOR = 'prefersPlainEditor'; Archived: 'archived',
PrefersPlainEditor: 'prefersPlainEditor'
const ELEMENT_ID_NOTE_TEXT_EDITOR = 'note-text-editor'; };
const ELEMENT_ID_NOTE_TITLE_EDITOR = 'note-title-editor'; const ElementIds = {
const ELEMENT_ID_EDITOR_CONTENT = 'editor-content'; NoteTextEditor: 'note-text-editor',
const ELEMENT_ID_NOTE_TAGS_COMPONENT_CONTAINER = 'note-tags-component-container'; NoteTitleEditor: 'note-title-editor',
EditorContent: 'editor-content',
const DESKTOP_MONOSPACE_FAMILY = `Menlo,Consolas,'DejaVu Sans Mono',monospace`; NoteTagsComponentContainer: 'note-tags-component-container'
const WEB_MONOSPACE_FAMILY = `monospace`; };
const SANS_SERIF_FAMILY = `inherit`; const Fonts = {
DesktopMonospaceFamily: `Menlo,Consolas,'DejaVu Sans Mono',monospace`,
WebMonospaceFamily: `monospace`,
SansSerifFamily: `inherit`
};
class EditorCtrl extends PureCtrl { class EditorCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
@@ -61,17 +52,14 @@ class EditorCtrl extends PureCtrl {
$rootScope, $rootScope,
appState, appState,
application, application,
actionsManager,
desktopManager, desktopManager,
keyboardManager, keyboardManager,
preferencesManager, preferencesManager,
sessionHistory /** Unused below, required to load globally */
) { ) {
super($timeout); super($timeout);
this.$rootScope = $rootScope; this.$rootScope = $rootScope;
this.application = application; this.application = application;
this.appState = appState; this.appState = appState;
this.actionsManager = actionsManager;
this.desktopManager = desktopManager; this.desktopManager = desktopManager;
this.keyboardManager = keyboardManager; this.keyboardManager = keyboardManager;
this.preferencesManager = preferencesManager; this.preferencesManager = preferencesManager;
@@ -97,19 +85,19 @@ class EditorCtrl extends PureCtrl {
this.registerKeyboardShortcuts(); this.registerKeyboardShortcuts();
/** Used by .pug template */ /** Used by .pug template */
this.prefKeyMonospace = PREF_EDITOR_MONOSPACE_ENABLED; this.prefKeyMonospace = PrefKeys.EditorMonospaceEnabled;
this.prefKeySpellcheck = PREF_EDITOR_SPELLCHECK; this.prefKeySpellcheck = PrefKeys.EditorSpellcheck;
this.prefKeyMarginResizers = PREF_EDITOR_RESIZERS_ENABLED; this.prefKeyMarginResizers = PrefKeys.EditorResizersEnabled;
} }
addAppStateObserver() { addAppStateObserver() {
this.appState.addObserver((eventName, data) => { this.appState.addObserver((eventName, data) => {
if (eventName === APP_STATE_EVENT_NOTE_CHANGED) { if (eventName === AppStateEvents.NoteChanged) {
this.handleNoteSelectionChange( this.handleNoteSelectionChange(
this.appState.getSelectedNote(), this.appState.getSelectedNote(),
data.previousNote data.previousNote
); );
} else if (eventName === APP_STATE_EVENT_PREFERENCES_CHANGED) { } else if (eventName === AppStateEvents.PreferencesChanged) {
this.loadPreferences(); this.loadPreferences();
} }
}); });
@@ -117,7 +105,7 @@ class EditorCtrl extends PureCtrl {
streamItems() { streamItems() {
this.application.streamItems({ this.application.streamItems({
contentType: CONTENT_TYPE_NOTE, contentType: ContentTypes.Note,
stream: async ({ items, source }) => { stream: async ({ items, source }) => {
if (!this.state.note) { if (!this.state.note) {
return; return;
@@ -139,7 +127,7 @@ class EditorCtrl extends PureCtrl {
}); });
this.application.streamItems({ this.application.streamItems({
contentType: CONTENT_TYPE_TAG, contentType: ContentTypes.Tag,
stream: async ({ items, source }) => { stream: async ({ items, source }) => {
if (!this.state.note) { if (!this.state.note) {
return; return;
@@ -158,7 +146,7 @@ class EditorCtrl extends PureCtrl {
}); });
this.application.streamItems({ this.application.streamItems({
contentType: CONTENT_TYPE_COMPONENT, contentType: ContentTypes.Component,
stream: async ({ items, source }) => { stream: async ({ items, source }) => {
if (!this.state.note) { if (!this.state.note) {
return; return;
@@ -267,7 +255,7 @@ class EditorCtrl extends PureCtrl {
addSyncStatusObserver() { addSyncStatusObserver() {
/** @todo */ /** @todo */
// this.syncStatusObserver = this.syncManager. // this.syncStatusObserver = syncManager.
// registerSyncStatusObserver((status) => { // registerSyncStatusObserver((status) => {
// if (status.localError) { // if (status.localError) {
// this.$timeout(() => { // this.$timeout(() => {
@@ -321,11 +309,11 @@ class EditorCtrl extends PureCtrl {
} }
if (editor) { if (editor) {
const prefersPlain = this.state.note.getAppDataItem( const prefersPlain = this.state.note.getAppDataItem(
APP_DATA_KEY_PREFERS_PLAIN_EDITOR AppDataKeys.PrefersPlainEditor
) === true; ) === true;
if (prefersPlain) { if (prefersPlain) {
this.state.note.setAppDataItem( this.state.note.setAppDataItem(
APP_DATA_KEY_PREFERS_PLAIN_EDITOR, AppDataKeys.PrefersPlainEditor,
false false
); );
this.application.setItemNeedsSync({ item: this.state.note }); this.application.setItemNeedsSync({ item: this.state.note });
@@ -333,9 +321,9 @@ class EditorCtrl extends PureCtrl {
this.associateComponentWithCurrentNote(editor); this.associateComponentWithCurrentNote(editor);
} else { } else {
/** Note prefers plain editor */ /** 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( this.state.note.setAppDataItem(
APP_DATA_KEY_PREFERS_PLAIN_EDITOR, AppDataKeys.PrefersPlainEditor,
true true
); );
this.application.setItemNeedsSync({ item: this.state.note }); this.application.setItemNeedsSync({ item: this.state.note });
@@ -356,7 +344,7 @@ class EditorCtrl extends PureCtrl {
} }
hasAvailableExtensions() { hasAvailableExtensions() {
return this.actionsManager.extensionsInContextOfItem(this.state.note).length > 0; return this.application.actionsManager.extensionsInContextOfItem(this.state.note).length > 0;
} }
performFirefoxPinnedTabFix() { performFirefoxPinnedTabFix() {
@@ -494,15 +482,15 @@ class EditorCtrl extends PureCtrl {
} }
focusEditor() { focusEditor() {
const element = document.getElementById(ELEMENT_ID_NOTE_TEXT_EDITOR); const element = document.getElementById(ElementIds.NoteTextEditor);
if (element) { if (element) {
this.lastEditorFocusEventSource = EVENT_SOURCE_SCRIPT; this.lastEditorFocusEventSource = EventSources.Script;
element.focus(); element.focus();
} }
} }
focusTitle() { focusTitle() {
document.getElementById(ELEMENT_ID_NOTE_TITLE_EDITOR).focus(); document.getElementById(ElementIds.NoteTitleEditor).focus();
} }
clickedTextArea() { clickedTextArea() {
@@ -627,7 +615,7 @@ class EditorCtrl extends PureCtrl {
togglePin() { togglePin() {
this.state.note.setAppDataItem( this.state.note.setAppDataItem(
APP_DATA_KEY_PINNED, AppDataKeys.Pinned,
!this.state.note.pinned !this.state.note.pinned
); );
this.saveNote({ this.saveNote({
@@ -638,7 +626,7 @@ class EditorCtrl extends PureCtrl {
toggleLockNote() { toggleLockNote() {
this.state.note.setAppDataItem( this.state.note.setAppDataItem(
APP_DATA_KEY_LOCKED, AppDataKeys.Locked,
!this.state.note.locked !this.state.note.locked
); );
this.saveNote({ this.saveNote({
@@ -674,7 +662,7 @@ class EditorCtrl extends PureCtrl {
toggleArchiveNote() { toggleArchiveNote() {
this.state.note.setAppDataItem( this.state.note.setAppDataItem(
APP_DATA_KEY_ARCHIVED, AppDataKeys.Archived,
!this.state.note.archived !this.state.note.archived
); );
this.saveNote({ this.saveNote({
@@ -734,7 +722,7 @@ class EditorCtrl extends PureCtrl {
this.application.setItemsNeedsSync({ items: toRemove }); this.application.setItemsNeedsSync({ items: toRemove });
const tags = []; const tags = [];
for (const tagString of strings) { for (const tagString of strings) {
const existingRelationship = _.find( const existingRelationship = find(
this.state.note.tags, this.state.note.tags,
{ title: tagString } { title: tagString }
); );
@@ -754,13 +742,13 @@ class EditorCtrl extends PureCtrl {
onPanelResizeFinish = (width, left, isMaxWidth) => { onPanelResizeFinish = (width, left, isMaxWidth) => {
if (isMaxWidth) { if (isMaxWidth) {
this.preferencesManager.setUserPrefValue( this.preferencesManager.setUserPrefValue(
PREF_EDITOR_WIDTH, PrefKeys.EditorWidth,
null null
); );
} else { } else {
if (width !== undefined && width !== null) { if (width !== undefined && width !== null) {
this.preferencesManager.setUserPrefValue( this.preferencesManager.setUserPrefValue(
PREF_EDITOR_WIDTH, PrefKeys.EditorWidth,
width width
); );
this.leftResizeControl.setWidth(width); this.leftResizeControl.setWidth(width);
@@ -768,7 +756,7 @@ class EditorCtrl extends PureCtrl {
} }
if (left !== undefined && left !== null) { if (left !== undefined && left !== null) {
this.preferencesManager.setUserPrefValue( this.preferencesManager.setUserPrefValue(
PREF_EDITOR_LEFT, PrefKeys.EditorLeft,
left left
); );
this.rightResizeControl.setLeft(left); this.rightResizeControl.setLeft(left);
@@ -778,15 +766,15 @@ class EditorCtrl extends PureCtrl {
loadPreferences() { loadPreferences() {
const monospaceEnabled = this.preferencesManager.getValue( const monospaceEnabled = this.preferencesManager.getValue(
PREF_EDITOR_MONOSPACE_ENABLED, PrefKeys.EditorMonospaceEnabled,
true true
); );
const spellcheck = this.preferencesManager.getValue( const spellcheck = this.preferencesManager.getValue(
PREF_EDITOR_SPELLCHECK, PrefKeys.EditorSpellcheck,
true true
); );
const marginResizersEnabled = this.preferencesManager.getValue( const marginResizersEnabled = this.preferencesManager.getValue(
PREF_EDITOR_RESIZERS_ENABLED, PrefKeys.EditorResizersEnabled,
true true
); );
this.setState({ this.setState({
@@ -795,7 +783,7 @@ class EditorCtrl extends PureCtrl {
marginResizersEnabled marginResizersEnabled
}); });
if (!document.getElementById(ELEMENT_ID_EDITOR_CONTENT)) { if (!document.getElementById(ElementIds.EditorContent)) {
/** Elements have not yet loaded due to ng-if around wrapper */ /** Elements have not yet loaded due to ng-if around wrapper */
return; return;
} }
@@ -804,7 +792,7 @@ class EditorCtrl extends PureCtrl {
if (this.state.marginResizersEnabled) { if (this.state.marginResizersEnabled) {
const width = this.preferencesManager.getValue( const width = this.preferencesManager.getValue(
PREF_EDITOR_WIDTH, PrefKeys.EditorWidth,
null null
); );
if (width != null) { if (width != null) {
@@ -812,7 +800,7 @@ class EditorCtrl extends PureCtrl {
this.rightResizeControl.setWidth(width); this.rightResizeControl.setWidth(width);
} }
const left = this.preferencesManager.getValue( const left = this.preferencesManager.getValue(
PREF_EDITOR_LEFT, PrefKeys.EditorLeft,
null null
); );
if (left != null) { if (left != null) {
@@ -824,19 +812,19 @@ class EditorCtrl extends PureCtrl {
reloadFont() { reloadFont() {
const editor = document.getElementById( const editor = document.getElementById(
ELEMENT_ID_NOTE_TEXT_EDITOR ElementIds.NoteTextEditor
); );
if (!editor) { if (!editor) {
return; return;
} }
if (this.state.monospaceEnabled) { if (this.state.monospaceEnabled) {
if (this.state.isDesktop) { if (this.state.isDesktop) {
editor.style.fontFamily = DESKTOP_MONOSPACE_FAMILY; editor.style.fontFamily = Fonts.DesktopMonospaceFamily;
} else { } else {
editor.style.fontFamily = WEB_MONOSPACE_FAMILY; editor.style.fontFamily = Fonts.WebMonospaceFamily;
} }
} else { } else {
editor.style.fontFamily = SANS_SERIF_FAMILY; editor.style.fontFamily = Fonts.SansSerifFamily;
} }
} }
@@ -849,7 +837,7 @@ class EditorCtrl extends PureCtrl {
); );
this.reloadFont(); this.reloadFont();
if (key === PREF_EDITOR_SPELLCHECK) { if (key === PrefKeys.EditorSpellcheck) {
/** Allows textarea to reload */ /** Allows textarea to reload */
await this.setState({ await this.setState({
noteReady: false noteReady: false
@@ -858,7 +846,7 @@ class EditorCtrl extends PureCtrl {
noteReady: true noteReady: true
}); });
this.reloadFont(); this.reloadFont();
} else if (key === PREF_EDITOR_RESIZERS_ENABLED && this[key] === true) { } else if (key === PrefKeys.EditorResizersEnabled && this[key] === true) {
this.$timeout(() => { this.$timeout(() => {
this.leftResizeControl.flash(); this.leftResizeControl.flash();
this.rightResizeControl.flash(); this.rightResizeControl.flash();
@@ -956,7 +944,7 @@ class EditorCtrl extends PureCtrl {
if (data.type === 'container') { if (data.type === 'container') {
if (component.area === 'note-tags') { if (component.area === 'note-tags') {
const container = document.getElementById( const container = document.getElementById(
ELEMENT_ID_NOTE_TAGS_COMPONENT_CONTAINER ElementIds.NoteTagsComponentContainer
); );
setSize(container, data); setSize(container, data);
} }
@@ -1055,7 +1043,7 @@ class EditorCtrl extends PureCtrl {
registerKeyboardShortcuts() { registerKeyboardShortcuts() {
this.altKeyObserver = this.keyboardManager.addKeyObserver({ this.altKeyObserver = this.keyboardManager.addKeyObserver({
modifiers: [ modifiers: [
KeyboardManager.KeyModifierAlt KeyboardModifiers.Alt
], ],
onKeyDown: () => { onKeyDown: () => {
this.setState({ this.setState({
@@ -1070,23 +1058,23 @@ class EditorCtrl extends PureCtrl {
}); });
this.trashKeyObserver = this.keyboardManager.addKeyObserver({ this.trashKeyObserver = this.keyboardManager.addKeyObserver({
key: KeyboardManager.KeyBackspace, key: KeyboardKeys.Backspace,
notElementIds: [ notElementIds: [
ELEMENT_ID_NOTE_TEXT_EDITOR, ElementIds.NoteTextEditor,
ELEMENT_ID_NOTE_TITLE_EDITOR ElementIds.NoteTitleEditor
], ],
modifiers: [KeyboardManager.KeyModifierMeta], modifiers: [KeyboardModifiers.Meta],
onKeyDown: () => { onKeyDown: () => {
this.deleteNote(); this.deleteNote();
}, },
}); });
this.deleteKeyObserver = this.keyboardManager.addKeyObserver({ this.deleteKeyObserver = this.keyboardManager.addKeyObserver({
key: KeyboardManager.KeyBackspace, key: KeyboardKeys.Backspace,
modifiers: [ modifiers: [
KeyboardManager.KeyModifierMeta, KeyboardModifiers.Meta,
KeyboardManager.KeyModifierShift, KeyboardModifiers.Shift,
KeyboardManager.KeyModifierAlt KeyboardModifiers.Alt
], ],
onKeyDown: (event) => { onKeyDown: (event) => {
event.preventDefault(); event.preventDefault();
@@ -1107,11 +1095,11 @@ class EditorCtrl extends PureCtrl {
* not fired. * not fired.
*/ */
const editor = document.getElementById( const editor = document.getElementById(
ELEMENT_ID_NOTE_TEXT_EDITOR ElementIds.NoteTextEditor
); );
this.tabObserver = this.keyboardManager.addKeyObserver({ this.tabObserver = this.keyboardManager.addKeyObserver({
element: editor, element: editor,
key: KeyboardManager.KeyTab, key: KeyboardKeys.Tab,
onKeyDown: (event) => { onKeyDown: (event) => {
if (this.state.note.locked || event.shiftKey) { if (this.state.note.locked || event.shiftKey) {
return; return;

View File

@@ -2,15 +2,11 @@ import { dateToLocalizedString } from '@/utils';
import { import {
ApplicationEvents, ApplicationEvents,
TIMING_STRATEGY_FORCE_SPAWN_NEW, TIMING_STRATEGY_FORCE_SPAWN_NEW,
ProtectedActions ProtectedActions,
ContentTypes
} from 'snjs'; } from 'snjs';
import template from '%/footer.pug'; import template from '%/footer.pug';
import { import { AppStateEvents, EventSources } from '@/state';
APP_STATE_EVENT_EDITOR_FOCUSED,
APP_STATE_EVENT_BEGAN_BACKUP_DOWNLOAD,
APP_STATE_EVENT_ENDED_BACKUP_DOWNLOAD,
EVENT_SOURCE_USER_INTERACTION
} from '@/state';
import { import {
STRING_GENERIC_SYNC_ERROR, STRING_GENERIC_SYNC_ERROR,
STRING_NEW_UPDATE_READY STRING_NEW_UPDATE_READY
@@ -74,16 +70,16 @@ class FooterCtrl {
addAppStateObserver() { addAppStateObserver() {
this.appState.addObserver((eventName, data) => { this.appState.addObserver((eventName, data) => {
if(eventName === APP_STATE_EVENT_EDITOR_FOCUSED) { if(eventName === AppStateEvents.EditorFocused) {
if (data.eventSource === EVENT_SOURCE_USER_INTERACTION) { if (data.eventSource === EventSources.UserInteraction) {
this.closeAllRooms(); this.closeAllRooms();
this.closeAccountMenu(); this.closeAccountMenu();
} }
} else if(eventName === APP_STATE_EVENT_BEGAN_BACKUP_DOWNLOAD) { } else if(eventName === AppStateEvents.BeganBackupDownload) {
this.backupStatus = this.statusManager.addStatusFromString( this.backupStatus = this.statusManager.addStatusFromString(
"Saving local backup..." "Saving local backup..."
); );
} else if(eventName === APP_STATE_EVENT_ENDED_BACKUP_DOWNLOAD) { } else if(eventName === AppStateEvents.EndedBackupDownload) {
if(data.success) { if(data.success) {
this.backupStatus = this.statusManager.replaceStatusWithString( this.backupStatus = this.statusManager.replaceStatusWithString(
this.backupStatus, this.backupStatus,
@@ -125,10 +121,10 @@ class FooterCtrl {
streamItems() { streamItems() {
this.application.streamItems({ this.application.streamItems({
contentType: CONTENT_TYPE_COMPONENT, contentType: ContentTypes.Component,
stream: async () => { stream: async () => {
this.rooms = this.application.getItems({ this.rooms = this.application.getItems({
contentType: CONTENT_TYPE_COMPONENT contentType: ContentTypes.Component
}).filter((candidate) => { }).filter((candidate) => {
return candidate.area === 'rooms' && !candidate.deleted; return candidate.area === 'rooms' && !candidate.deleted;
}); });
@@ -143,7 +139,7 @@ class FooterCtrl {
contentType: 'SN|Theme', contentType: 'SN|Theme',
stream: async () => { stream: async () => {
const themes = this.application.getDisplayableItems({ const themes = this.application.getDisplayableItems({
contentType: CONTENT_TYPE_THEME contentType: ContentTypes.Theme
}).filter((candidate) => { }).filter((candidate) => {
return ( return (
!candidate.deleted && !candidate.deleted &&
@@ -159,7 +155,7 @@ class FooterCtrl {
this.reloadDockShortcuts(); this.reloadDockShortcuts();
} }
} }
); });
} }
registerComponentHandler() { registerComponentHandler() {

View File

@@ -1,21 +1,16 @@
import template from '%/lock-screen.pug'; import template from '%/lock-screen.pug';
import { import { AppStateEvents } from '@/state';
APP_STATE_EVENT_WINDOW_DID_FOCUS
} from '@/state';
const ELEMENT_ID_PASSCODE_INPUT = 'passcode-input'; const ELEMENT_ID_PASSCODE_INPUT = 'passcode-input';
class LockScreenCtrl { class LockScreenCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$scope, $scope,
alertManager,
application, application,
appState appState
) { ) {
this.$scope = $scope; this.$scope = $scope;
this.alertManager = alertManager;
this.application = application; this.application = application;
this.appState = appState; this.appState = appState;
this.formData = {}; this.formData = {};
@@ -37,7 +32,7 @@ class LockScreenCtrl {
addVisibilityObserver() { addVisibilityObserver() {
this.unregisterObserver = this.appState.addObserver((eventName, data) => { this.unregisterObserver = this.appState.addObserver((eventName, data) => {
if (eventName === APP_STATE_EVENT_WINDOW_DID_FOCUS) { if (eventName === AppStateEvents.WindowDidFocus) {
const input = this.passcodeInput; const input = this.passcodeInput;
if(input) { if(input) {
input.focus(); input.focus();
@@ -56,7 +51,7 @@ class LockScreenCtrl {
this.passcodeInput.blur(); this.passcodeInput.blur();
const success = await this.onValue()(this.formData.passcode); const success = await this.onValue()(this.formData.passcode);
if(!success) { if(!success) {
this.alertManager.alert({ this.application.alertManager.alert({
text: "Invalid passcode. Please try again.", text: "Invalid passcode. Please try again.",
onClose: () => { onClose: () => {
this.passcodeInput.focus(); this.passcodeInput.focus();
@@ -70,7 +65,7 @@ class LockScreenCtrl {
} }
beginDeleteData() { beginDeleteData() {
this.alertManager.confirm({ this.application.alertManager.confirm({
text: "Are you sure you want to clear all local data?", text: "Are you sure you want to clear all local data?",
destructive: true, destructive: true,
onConfirm: async () => { onConfirm: async () => {

View File

@@ -1,24 +1,11 @@
import _ from 'lodash'; import _ from 'lodash';
import angular from 'angular'; import angular from 'angular';
import template from '%/notes.pug'; import template from '%/notes.pug';
import { ApplicationEvents, CONTENT_TYPE_NOTE, CONTENT_TYPE_TAG } from 'snjs'; import { ApplicationEvents, ContentTypes } from 'snjs';
import { KeyboardManager } from '@/services/keyboardManager';
import { PureCtrl } from '@Controllers'; import { PureCtrl } from '@Controllers';
import { AppStateEvents } from '@/state';
import { import {
APP_STATE_EVENT_NOTE_CHANGED, PrefKeys
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
} from '@/services/preferencesManager'; } from '@/services/preferencesManager';
import { import {
PANEL_NAME_NOTES PANEL_NAME_NOTES
@@ -37,8 +24,6 @@ import {
*/ */
const MIN_NOTE_CELL_HEIGHT = 51.0; const MIN_NOTE_CELL_HEIGHT = 51.0;
const DEFAULT_LIST_NUM_NOTES = 20; const DEFAULT_LIST_NUM_NOTES = 20;
const ELEMENT_ID_SEARCH_BAR = 'search-bar'; const ELEMENT_ID_SEARCH_BAR = 'search-bar';
const ELEMENT_ID_SCROLL_CONTAINER = 'notes-scrollable'; const ELEMENT_ID_SCROLL_CONTAINER = 'notes-scrollable';
@@ -96,14 +81,14 @@ class NotesCtrl extends PureCtrl {
addAppStateObserver() { addAppStateObserver() {
this.appState.addObserver((eventName, data) => { this.appState.addObserver((eventName, data) => {
if (eventName === APP_STATE_EVENT_TAG_CHANGED) { if (eventName === AppStateEvents.TagChanged) {
this.handleTagChange(this.appState.getSelectedTag(), data.previousTag); 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()); this.handleNoteSelection(this.appState.getSelectedNote());
} else if (eventName === APP_STATE_EVENT_PREFERENCES_CHANGED) { } else if (eventName === AppStateEvents.PreferencesChanged) {
this.reloadPreferences(); this.reloadPreferences();
this.reloadNotes(); this.reloadNotes();
} else if (eventName === APP_STATE_EVENT_EDITOR_FOCUSED) { } else if (eventName === AppStateEvents.EditorFocused) {
this.setShowMenuFalse(); this.setShowMenuFalse();
} }
}); });
@@ -129,7 +114,7 @@ class NotesCtrl extends PureCtrl {
if (this.state.notes.length === 0) { if (this.state.notes.length === 0) {
this.createNewNote(); this.createNewNote();
} }
} else if(eventName === ApplicationEvents.CompletedSync) { } else if (eventName === ApplicationEvents.CompletedSync) {
if (this.createDummyOnSynCompletionIfNoNotes && this.state.notes.length === 0) { if (this.createDummyOnSynCompletionIfNoNotes && this.state.notes.length === 0) {
this.createDummyOnSynCompletionIfNoNotes = false; this.createDummyOnSynCompletionIfNoNotes = false;
this.createNewNote(); this.createNewNote();
@@ -140,7 +125,7 @@ class NotesCtrl extends PureCtrl {
streamNotesAndTags() { streamNotesAndTags() {
this.application.streamItems({ this.application.streamItems({
contentType: [CONTENT_TYPE_NOTE, CONTENT_TYPE_TAG], contentType: [ContentTypes.Note, ContentTypes.Tag],
stream: async ({ items }) => { stream: async ({ items }) => {
await this.reloadNotes(); await this.reloadNotes();
const selectedNote = this.state.selectedNote; const selectedNote = this.state.selectedNote;
@@ -155,7 +140,7 @@ class NotesCtrl extends PureCtrl {
} }
/** Note has changed values, reset its flags */ /** 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) { for (const note of notes) {
this.loadFlagsForNote(note); this.loadFlagsForNote(note);
note.cachedCreatedAtString = note.createdAtString(); note.cachedCreatedAtString = note.createdAtString();
@@ -167,7 +152,7 @@ class NotesCtrl extends PureCtrl {
async handleTagChange(tag, previousTag) { async handleTagChange(tag, previousTag) {
if (this.state.selectedNote && this.state.selectedNote.dummy) { if (this.state.selectedNote && this.state.selectedNote.dummy) {
this.application.removeItemLocally({item: this.state.selectedNote}); this.application.removeItemLocally({ item: this.state.selectedNote });
if (previousTag) { if (previousTag) {
_.remove(previousTag.notes, this.state.selectedNote); _.remove(previousTag.notes, this.state.selectedNote);
} }
@@ -266,7 +251,7 @@ class NotesCtrl extends PureCtrl {
} }
const previousNote = this.state.selectedNote; const previousNote = this.state.selectedNote;
if (previousNote && previousNote.dummy) { if (previousNote && previousNote.dummy) {
this.application.removeItemLocally({previousNote}); this.application.removeItemLocally({ previousNote });
this.removeNoteFromList(previousNote); this.removeNoteFromList(previousNote);
} }
await this.setState({ await this.setState({
@@ -279,7 +264,7 @@ class NotesCtrl extends PureCtrl {
this.selectedIndex = Math.max(0, this.displayableNotes().indexOf(note)); this.selectedIndex = Math.max(0, this.displayableNotes().indexOf(note));
if (note.content.conflict_of) { if (note.content.conflict_of) {
note.content.conflict_of = null; note.content.conflict_of = null;
this.application.saveItem({item: note}); this.application.saveItem({ item: note });
} }
if (this.isFiltering()) { if (this.isFiltering()) {
this.desktopManager.searchText(this.state.noteFilter.text); this.desktopManager.searchText(this.state.noteFilter.text);
@@ -290,7 +275,7 @@ class NotesCtrl extends PureCtrl {
const viewOptions = {}; const viewOptions = {};
const prevSortValue = this.state.sortBy; const prevSortValue = this.state.sortBy;
let sortBy = this.preferencesManager.getValue( let sortBy = this.preferencesManager.getValue(
PREF_SORT_NOTES_BY, PrefKeys.SortNotesBy,
SORT_KEY_CREATED_AT SORT_KEY_CREATED_AT
); );
if (sortBy === SORT_KEY_UPDATED_AT) { if (sortBy === SORT_KEY_UPDATED_AT) {
@@ -299,27 +284,27 @@ class NotesCtrl extends PureCtrl {
} }
viewOptions.sortBy = sortBy; viewOptions.sortBy = sortBy;
viewOptions.sortReverse = this.preferencesManager.getValue( viewOptions.sortReverse = this.preferencesManager.getValue(
PREF_SORT_NOTES_REVERSE, PrefKeys.SortNotesReverse,
false false
); );
viewOptions.showArchived = this.preferencesManager.getValue( viewOptions.showArchived = this.preferencesManager.getValue(
PREF_NOTES_SHOW_ARCHIVED, PrefKeys.NotesShowArchived,
false false
); );
viewOptions.hidePinned = this.preferencesManager.getValue( viewOptions.hidePinned = this.preferencesManager.getValue(
PREF_NOTES_HIDE_PINNED, PrefKeys.NotesHidePinned,
false false
); );
viewOptions.hideNotePreview = this.preferencesManager.getValue( viewOptions.hideNotePreview = this.preferencesManager.getValue(
PREF_NOTES_HIDE_NOTE_PREVIEW, PrefKeys.NotesHideNotePreview,
false false
); );
viewOptions.hideDate = this.preferencesManager.getValue( viewOptions.hideDate = this.preferencesManager.getValue(
PREF_NOTES_HIDE_DATE, PrefKeys.NotesHideDate,
false false
); );
viewOptions.hideTags = this.preferencesManager.getValue( viewOptions.hideTags = this.preferencesManager.getValue(
PREF_NOTES_HIDE_TAGS, PrefKeys.NotesHideTags,
false false
); );
this.setState({ this.setState({
@@ -329,7 +314,7 @@ class NotesCtrl extends PureCtrl {
this.selectFirstNote(); this.selectFirstNote();
} }
const width = this.preferencesManager.getValue( const width = this.preferencesManager.getValue(
PREF_NOTES_PANEL_WIDTH PrefKeys.NotesPanelWidth
); );
if (width) { if (width) {
this.panelController.setWidth(width); this.panelController.setWidth(width);
@@ -344,7 +329,7 @@ class NotesCtrl extends PureCtrl {
onPanelResize = (newWidth, lastLeft, isAtMaxWidth, isCollapsed) => { onPanelResize = (newWidth, lastLeft, isAtMaxWidth, isCollapsed) => {
this.preferencesManager.setUserPrefValue( this.preferencesManager.setUserPrefValue(
PREF_NOTES_PANEL_WIDTH, PrefKeys.NotesPanelWidth,
newWidth newWidth
); );
this.preferencesManager.syncUserPreferences(); this.preferencesManager.syncUserPreferences();
@@ -523,7 +508,7 @@ class NotesCtrl extends PureCtrl {
} }
const title = "Note" + (this.state.notes ? (" " + (this.state.notes.length + 1)) : ""); const title = "Note" + (this.state.notes ? (" " + (this.state.notes.length + 1)) : "");
const newNote = this.application.createItem({ const newNote = this.application.createItem({
contentType: CONTENT_TYPE_NOTE, contentType: ContentTypes.Note,
content: { content: {
text: '', text: '',
title: title title: title
@@ -531,7 +516,7 @@ class NotesCtrl extends PureCtrl {
}); });
newNote.client_updated_at = new Date(); newNote.client_updated_at = new Date();
newNote.dummy = true; newNote.dummy = true;
this.application.setItemNeedsSync({item: newNote}); this.application.setItemNeedsSync({ item: newNote });
const selectedTag = this.appState.getSelectedTag(); const selectedTag = this.appState.getSelectedTag();
if (!selectedTag.isSmartTag()) { if (!selectedTag.isSmartTag()) {
selectedTag.addItemAsRelationship(newNote); selectedTag.addItemAsRelationship(newNote);
@@ -605,7 +590,7 @@ class NotesCtrl extends PureCtrl {
toggleReverseSort() { toggleReverseSort() {
this.selectedMenuItem(); this.selectedMenuItem();
this.preferencesManager.setUserPrefValue( this.preferencesManager.setUserPrefValue(
PREF_SORT_NOTES_REVERSE, PrefKeys.SortNotesReverse,
!this.state.sortReverse !this.state.sortReverse
); );
this.preferencesManager.syncUserPreferences(); this.preferencesManager.syncUserPreferences();
@@ -613,7 +598,7 @@ class NotesCtrl extends PureCtrl {
setSortBy(type) { setSortBy(type) {
this.preferencesManager.setUserPrefValue( this.preferencesManager.setUserPrefValue(
PREF_SORT_NOTES_BY, PrefKeys.SortNotesBy,
type type
); );
this.preferencesManager.syncUserPreferences(); this.preferencesManager.syncUserPreferences();
@@ -649,8 +634,8 @@ class NotesCtrl extends PureCtrl {
this.newNoteKeyObserver = this.keyboardManager.addKeyObserver({ this.newNoteKeyObserver = this.keyboardManager.addKeyObserver({
key: 'n', key: 'n',
modifiers: [ modifiers: [
KeyboardManager.KeyModifierMeta, KeyboardModifiers.Meta,
KeyboardManager.KeyModifierCtrl KeyboardModifiers.Ctrl
], ],
onKeyDown: (event) => { onKeyDown: (event) => {
event.preventDefault(); event.preventDefault();
@@ -659,7 +644,7 @@ class NotesCtrl extends PureCtrl {
}); });
this.nextNoteKeyObserver = this.keyboardManager.addKeyObserver({ this.nextNoteKeyObserver = this.keyboardManager.addKeyObserver({
key: KeyboardManager.KeyDown, key: KeyboardKeys.Down,
elements: [ elements: [
document.body, document.body,
this.getSearchBar() this.getSearchBar()
@@ -674,7 +659,7 @@ class NotesCtrl extends PureCtrl {
}); });
this.nextNoteKeyObserver = this.keyboardManager.addKeyObserver({ this.nextNoteKeyObserver = this.keyboardManager.addKeyObserver({
key: KeyboardManager.KeyUp, key: KeyboardKeys.Up,
element: document.body, element: document.body,
onKeyDown: (event) => { onKeyDown: (event) => {
this.selectPreviousNote(); this.selectPreviousNote();
@@ -684,8 +669,8 @@ class NotesCtrl extends PureCtrl {
this.searchKeyObserver = this.keyboardManager.addKeyObserver({ this.searchKeyObserver = this.keyboardManager.addKeyObserver({
key: "f", key: "f",
modifiers: [ modifiers: [
KeyboardManager.KeyModifierMeta, KeyboardModifiers.Meta,
KeyboardManager.KeyModifierShift KeyboardModifiers.Shift
], ],
onKeyDown: (event) => { onKeyDown: (event) => {
const searchBar = this.getSearchBar(); const searchBar = this.getSearchBar();

View File

@@ -2,10 +2,7 @@ import _ from 'lodash';
import { Challenges } from 'snjs'; import { Challenges } from 'snjs';
import { getPlatformString } from '@/utils'; import { getPlatformString } from '@/utils';
import template from '%/root.pug'; import template from '%/root.pug';
import { import { AppStateEvents } from '@/state';
APP_STATE_EVENT_PANEL_RESIZED,
APP_STATE_EVENT_WINDOW_DID_FOCUS
} from '@/state';
import { import {
PANEL_NAME_NOTES, PANEL_NAME_NOTES,
PANEL_NAME_TAGS PANEL_NAME_TAGS
@@ -15,6 +12,7 @@ import {
STRING_DEFAULT_FILE_ERROR, STRING_DEFAULT_FILE_ERROR,
StringSyncException StringSyncException
} from '@/strings'; } from '@/strings';
import { PureCtrl } from './abstract/pure_ctrl';
class RootCtrl extends PureCtrl { class RootCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
@@ -45,7 +43,6 @@ class RootCtrl extends PureCtrl {
needsUnlock: false, needsUnlock: false,
appClass: '' appClass: ''
}; };
this.loadApplication(); this.loadApplication();
this.handleAutoSignInFromParams(); this.handleAutoSignInFromParams();
this.addAppStateObserver(); this.addAppStateObserver();
@@ -53,7 +50,7 @@ class RootCtrl extends PureCtrl {
} }
async loadApplication() { async loadApplication() {
this.application.prepareForLaunch({ await this.application.prepareForLaunch({
callbacks: { callbacks: {
authChallengeResponses: async (challenges) => { authChallengeResponses: async (challenges) => {
if (challenges.includes(Challenges.LocalPasscode)) { if (challenges.includes(Challenges.LocalPasscode)) {
@@ -65,7 +62,7 @@ class RootCtrl extends PureCtrl {
await this.application.launch(); await this.application.launch();
this.setState({ needsUnlock: false }); this.setState({ needsUnlock: false });
await this.openDatabase(); await this.openDatabase();
this.preferencesManager.load(); await this.preferencesManager.initialize();
this.addSyncStatusObserver(); this.addSyncStatusObserver();
this.addSyncEventHandler(); this.addSyncEventHandler();
} }
@@ -76,7 +73,7 @@ class RootCtrl extends PureCtrl {
addAppStateObserver() { addAppStateObserver() {
this.appState.addObserver(async (eventName, data) => { this.appState.addObserver(async (eventName, data) => {
if (eventName === APP_STATE_EVENT_PANEL_RESIZED) { if (eventName === AppStateEvents.PanelResized) {
if (data.panel === PANEL_NAME_NOTES) { if (data.panel === PANEL_NAME_NOTES) {
this.notesCollapsed = data.collapsed; this.notesCollapsed = data.collapsed;
} }
@@ -87,7 +84,7 @@ class RootCtrl extends PureCtrl {
if (this.notesCollapsed) { appClass += "collapsed-notes"; } if (this.notesCollapsed) { appClass += "collapsed-notes"; }
if (this.tagsCollapsed) { appClass += " collapsed-tags"; } if (this.tagsCollapsed) { appClass += " collapsed-tags"; }
this.setState({ appClass }); this.setState({ appClass });
} else if (eventName === APP_STATE_EVENT_WINDOW_DID_FOCUS) { } else if (eventName === AppStateEvents.WindowDidFocus) {
if (!(await this.application.isPasscodeLocked())) { if (!(await this.application.isPasscodeLocked())) {
this.application.sync(); this.application.sync();
} }
@@ -110,7 +107,7 @@ class RootCtrl extends PureCtrl {
} }
// addSyncStatusObserver() { // addSyncStatusObserver() {
// this.syncStatusObserver = this.syncManager.registerSyncStatusObserver((status) => { // this.syncStatusObserver = syncManager.registerSyncStatusObserver((status) => {
// if (status.retrievedCount > 20) { // if (status.retrievedCount > 20) {
// const text = `Downloading ${status.retrievedCount} items. Keep app open.`; // const text = `Downloading ${status.retrievedCount} items. Keep app open.`;
// this.syncStatus = this.statusManager.replaceStatusWithString( // this.syncStatus = this.statusManager.replaceStatusWithString(
@@ -143,7 +140,7 @@ class RootCtrl extends PureCtrl {
// addSyncEventHandler() { // addSyncEventHandler() {
// let lastShownDate; // let lastShownDate;
// this.syncManager.addEventHandler((syncEvent, data) => { // syncManager.addEventHandler((syncEvent, data) => {
// this.$rootScope.$broadcast( // this.$rootScope.$broadcast(
// syncEvent, // syncEvent,
// data || {} // data || {}
@@ -183,14 +180,14 @@ class RootCtrl extends PureCtrl {
// status // status
// ); // );
// }; // };
// this.syncManager.loadLocalItems({ incrementalCallback }).then(() => { // syncManager.loadLocalItems({ incrementalCallback }).then(() => {
// this.$timeout(() => { // this.$timeout(() => {
// this.$rootScope.$broadcast("initial-data-loaded"); // this.$rootScope.$broadcast("initial-data-loaded");
// this.syncStatus = this.statusManager.replaceStatusWithString( // this.syncStatus = this.statusManager.replaceStatusWithString(
// this.syncStatus, // this.syncStatus,
// "Syncing..." // "Syncing..."
// ); // );
// this.syncManager.sync({ // syncManager.sync({
// checkIntegrity: true // checkIntegrity: true
// }).then(() => { // }).then(() => {
// this.syncStatus = this.statusManager.removeStatus(this.syncStatus); // 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 template from '%/tags.pug';
import { import { AppStateEvents } from '@/state';
APP_STATE_EVENT_PREFERENCES_CHANGED,
APP_STATE_EVENT_TAG_CHANGED
} from '@/state';
import { PANEL_NAME_TAGS } from '@/controllers/constants'; 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 { STRING_DELETE_TAG } from '@/strings';
import { PureCtrl } from '@Controllers'; import { PureCtrl } from '@Controllers';
@@ -40,11 +37,11 @@ class TagsPanelCtrl extends PureCtrl {
beginStreamingItems() { beginStreamingItems() {
this.application.streamItems({ this.application.streamItems({
contentType: CONTENT_TYPE_TAG, contentType: ContentTypes.Tag,
stream: async ({items}) => { stream: async ({ items }) => {
await this.setState({ await this.setState({
tags: this.application.getItems({contentType: CONTENT_TYPE_TAG}), tags: this.application.getItems({ contentType: ContentTypes.Tag }),
smartTags: this.application.getItems({ contentType: CONTENT_TYPE_SMART_TAG }), smartTags: this.application.getItems({ contentType: ContentTypes.SmartTag }),
}); });
this.reloadNoteCounts(); this.reloadNoteCounts();
if (this.state.selectedTag) { if (this.state.selectedTag) {
@@ -62,9 +59,9 @@ class TagsPanelCtrl extends PureCtrl {
addAppStateObserver() { addAppStateObserver() {
this.appState.addObserver((eventName, data) => { this.appState.addObserver((eventName, data) => {
if (eventName === APP_STATE_EVENT_PREFERENCES_CHANGED) { if (eventName === AppStateEvents.PreferencesChanged) {
this.loadPreferences(); this.loadPreferences();
} else if (eventName === APP_STATE_EVENT_TAG_CHANGED) { } else if (eventName === AppStateEvents.TagChanged) {
this.setState({ this.setState({
selectedTag: this.appState.getSelectedTag() selectedTag: this.appState.getSelectedTag()
}); });
@@ -93,7 +90,7 @@ class TagsPanelCtrl extends PureCtrl {
} }
loadPreferences() { loadPreferences() {
const width = this.preferencesManager.getValue(PREF_TAGS_PANEL_WIDTH); const width = this.preferencesManager.getValue(PrefKeys.TagsPanelWidth);
if (width) { if (width) {
this.panelController.setWidth(width); this.panelController.setWidth(width);
if (this.panelController.isCollapsed()) { if (this.panelController.isCollapsed()) {
@@ -107,7 +104,7 @@ class TagsPanelCtrl extends PureCtrl {
onPanelResize = (newWidth, lastLeft, isAtMaxWidth, isCollapsed) => { onPanelResize = (newWidth, lastLeft, isAtMaxWidth, isCollapsed) => {
this.preferencesManager.setUserPrefValue( this.preferencesManager.setUserPrefValue(
PREF_TAGS_PANEL_WIDTH, PrefKeys.TagsPanelWidth,
newWidth, newWidth,
true true
); );
@@ -130,7 +127,7 @@ class TagsPanelCtrl extends PureCtrl {
actionHandler: (component, action, data) => { actionHandler: (component, action, data) => {
if (action === 'select-item') { if (action === 'select-item') {
if (data.item.content_type === 'Tag') { if (data.item.content_type === 'Tag') {
const tag = this.application.findItem({uuid: data.item.uuid}); const tag = this.application.findItem({ uuid: data.item.uuid });
if (tag) { if (tag) {
this.selectTag(tag); this.selectTag(tag);
} }
@@ -157,7 +154,7 @@ class TagsPanelCtrl extends PureCtrl {
} }
if (tag.content.conflict_of) { if (tag.content.conflict_of) {
tag.content.conflict_of = null; tag.content.conflict_of = null;
this.application.saveItem({item: tag}); this.application.saveItem({ item: tag });
} }
this.appState.setSelectedTag(tag); this.appState.setSelectedTag(tag);
} }
@@ -167,7 +164,7 @@ class TagsPanelCtrl extends PureCtrl {
return; return;
} }
const newTag = this.application.createItem({ const newTag = this.application.createItem({
contentType: CONTENT_TYPE_TAG contentType: ContentTypes.Tag
}); });
this.setState({ this.setState({
previousTag: this.state.selectedTag, previousTag: this.state.selectedTag,
@@ -177,7 +174,7 @@ class TagsPanelCtrl extends PureCtrl {
}); });
/** @todo Should not be accessing internal function */ /** @todo Should not be accessing internal function */
/** Rely on local state instead of adding to global state */ /** Rely on local state instead of adding to global state */
this.application.modelManager.insertItems({items: [newTag]}); this.application.modelManager.insertItems({ items: [newTag] });
} }
tagTitleDidChange(tag) { tagTitleDidChange(tag) {
@@ -188,14 +185,14 @@ class TagsPanelCtrl extends PureCtrl {
async saveTag($event, tag) { async saveTag($event, tag) {
$event.target.blur(); $event.target.blur();
await this.setState({ await this.setState({
editingTag: null editingTag: null
}); });
if (!tag.title || tag.title.length === 0) { if (!tag.title || tag.title.length === 0) {
if (this.state.editingTag) { if (this.state.editingTag) {
tag.title = this.editingOriginalName; tag.title = this.editingOriginalName;
this.editingOriginalName = null; this.editingOriginalName = null;
} else if(this.state.newTag) { } else if (this.state.newTag) {
/** @todo Should not be accessing internal function */ /** @todo Should not be accessing internal function */
/** Rely on local state instead of adding to global state */ /** Rely on local state instead of adding to global state */
this.application.modelManager.removeItemLocally(tag); this.application.modelManager.removeItemLocally(tag);
@@ -206,10 +203,10 @@ class TagsPanelCtrl extends PureCtrl {
this.setState({ newTag: null }); this.setState({ newTag: null });
return; return;
} }
this.editingOriginalName = null; this.editingOriginalName = null;
const matchingTag = this.application.findTag({title: tag.title}); const matchingTag = this.application.findTag({ title: tag.title });
const alreadyExists = matchingTag && matchingTag !== tag; const alreadyExists = matchingTag && matchingTag !== tag;
if (this.state.newTag === tag && alreadyExists) { if (this.state.newTag === tag && alreadyExists) {
this.application.alertManager.alert({ this.application.alertManager.alert({
@@ -222,7 +219,7 @@ class TagsPanelCtrl extends PureCtrl {
return; return;
} }
this.application.saveItem({item: tag}); this.application.saveItem({ item: tag });
this.selectTag(tag); this.selectTag(tag);
this.setState({ this.setState({
newTag: null newTag: null
@@ -247,7 +244,7 @@ class TagsPanelCtrl extends PureCtrl {
text: STRING_DELETE_TAG, text: STRING_DELETE_TAG,
destructive: true, destructive: true,
onConfirm: () => { onConfirm: () => {
this.application.deleteItem({item: tag}); this.application.deleteItem({ item: tag });
} }
}); });
} }

View File

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

View File

@@ -2,22 +2,25 @@ import angular from 'angular';
import template from '%/directives/panel-resizer.pug'; import template from '%/directives/panel-resizer.pug';
import { debounce } from '@/utils'; import { debounce } from '@/utils';
const PANEL_SIDE_RIGHT = 'right'; const PanelSides = {
const PANEL_SIDE_LEFT = 'left'; Right: 'right',
Left: 'left'
const MOUSE_EVENT_MOVE = 'mousemove'; };
const MOUSE_EVENT_DOWN = 'mousedown'; const MouseEvents = {
const MOUSE_EVENT_UP = 'mouseup'; 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 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 { class PanelResizerCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
@@ -78,14 +81,14 @@ class PanelResizerCtrl {
this.appFrame = null; this.appFrame = null;
this.widthBeforeLastDblClick = 0; this.widthBeforeLastDblClick = 0;
if (this.property === PANEL_SIDE_RIGHT) { if (this.property === PanelSides.Right) {
this.configureRightPanel(); this.configureRightPanel();
} }
if (this.alwaysVisible) { if (this.alwaysVisible) {
this.resizerColumn.classList.add(PANEL_CSS_CLASS_ALWAYS_VISIBLE); this.resizerColumn.classList.add(CssClasses.AlwaysVisible);
} }
if (this.hoverable) { if (this.hoverable) {
this.resizerColumn.classList.add(PANEL_CSS_CLASS_HOVERABLE); this.resizerColumn.classList.add(CssClasses.Hoverable);
} }
} }
@@ -140,26 +143,26 @@ class PanelResizerCtrl {
} }
addMouseDownListener() { addMouseDownListener() {
this.resizerColumn.addEventListener(MOUSE_EVENT_DOWN, (event) => { this.resizerColumn.addEventListener(MouseEvents.Down, (event) => {
this.addInvisibleOverlay(); this.addInvisibleOverlay();
this.pressed = true; this.pressed = true;
this.lastDownX = event.clientX; this.lastDownX = event.clientX;
this.startWidth = this.panel.scrollWidth; this.startWidth = this.panel.scrollWidth;
this.startLeft = this.panel.offsetLeft; this.startLeft = this.panel.offsetLeft;
this.panel.classList.add(PANEL_CSS_CLASS_NO_SELECTION); this.panel.classList.add(CssClasses.NoSelection);
if (this.hoverable) { if (this.hoverable) {
this.resizerColumn.classList.add(PANEL_CSS_CLASS_DRAGGING); this.resizerColumn.classList.add(CssClasses.Dragging);
} }
}); });
} }
addMouseMoveListener() { addMouseMoveListener() {
document.addEventListener(MOUSE_EVENT_MOVE, (event) => { document.addEventListener(MouseEvents.Move, (event) => {
if (!this.pressed) { if (!this.pressed) {
return; return;
} }
event.preventDefault(); event.preventDefault();
if (this.property && this.property === PANEL_SIDE_LEFT) { if (this.property && this.property === PanelSides.Left) {
this.handleLeftEvent(event); this.handleLeftEvent(event);
} else { } else {
this.handleWidthEvent(event); this.handleWidthEvent(event);
@@ -210,12 +213,12 @@ class PanelResizerCtrl {
} }
addMouseUpListener() { addMouseUpListener() {
document.addEventListener(MOUSE_EVENT_UP, event => { document.addEventListener(MouseEvents.Up, event => {
this.removeInvisibleOverlay(); this.removeInvisibleOverlay();
if (this.pressed) { if (this.pressed) {
this.pressed = false; this.pressed = false;
this.resizerColumn.classList.remove(PANEL_CSS_CLASS_DRAGGING); this.resizerColumn.classList.remove(CssClasses.Dragging);
this.panel.classList.remove(PANEL_CSS_CLASS_NO_SELECTION); this.panel.classList.remove(CssClasses.NoSelection);
const isMaxWidth = this.isAtMaxWidth(); const isMaxWidth = this.isAtMaxWidth();
if (this.onResizeFinish) { if (this.onResizeFinish) {
this.onResizeFinish()( this.onResizeFinish()(
@@ -232,7 +235,7 @@ class PanelResizerCtrl {
isAtMaxWidth() { isAtMaxWidth() {
return ( return (
Math.round(this.lastWidth + this.lastLeft) === Math.round(this.lastWidth + this.lastLeft) ===
Math.round(this.getParentRect().width) Math.round(this.getParentRect().width)
); );
} }
@@ -279,9 +282,9 @@ class PanelResizerCtrl {
this.collapsed = this.isCollapsed(); this.collapsed = this.isCollapsed();
if (this.collapsed) { if (this.collapsed) {
this.resizerColumn.classList.add(PANEL_CSS_CLASS_COLLAPSED); this.resizerColumn.classList.add(CssClasses.Collapsed);
} else { } else {
this.resizerColumn.classList.remove(PANEL_CSS_CLASS_COLLAPSED); this.resizerColumn.classList.remove(CssClasses.Collapsed);
} }
} }
@@ -308,9 +311,9 @@ class PanelResizerCtrl {
flash() { flash() {
const FLASH_DURATION = 3000; const FLASH_DURATION = 3000;
this.resizerColumn.classList.add(PANEL_CSS_CLASS_ANIMATE_OPACITY); this.resizerColumn.classList.add(CssClasses.AnimateOpacity);
this.$timeout(() => { this.$timeout(() => {
this.resizerColumn.classList.remove(PANEL_CSS_CLASS_ANIMATE_OPACITY); this.resizerColumn.classList.remove(CssClasses.AnimateOpacity);
}, FLASH_DURATION); }, FLASH_DURATION);
} }
} }

View File

@@ -1,5 +1,5 @@
import { PrivilegesManager } from '@/services/privilegesManager';
import template from '%/directives/privileges-management-modal.pug'; import template from '%/directives/privileges-management-modal.pug';
import { PrivilegeCredentials } from 'snjs';
class PrivilegesManagementModalCtrl { class PrivilegesManagementModalCtrl {
/* @ngInject */ /* @ngInject */
@@ -18,9 +18,9 @@ class PrivilegesManagementModalCtrl {
displayInfoForCredential(credential) { displayInfoForCredential(credential) {
const info = this.application.privilegesManager.displayInfoForCredential(credential); const info = this.application.privilegesManager.displayInfoForCredential(credential);
if (credential === PrivilegesManager.CredentialLocalPasscode) { if (credential === PrivilegeCredentials.LocalPasscode) {
info.availability = this.hasPasscode; info.availability = this.hasPasscode;
} else if (credential === PrivilegesManager.CredentialAccountPassword) { } else if (credential === PrivilegeCredentials.AccountPassword) {
info.availability = this.hasAccount; info.availability = this.hasAccount;
} else { } else {
info.availability = true; info.availability = true;

View File

@@ -1,7 +1,6 @@
import { import {
PAYLOAD_SOURCE_REMOTE_ACTION_RETRIEVED, PAYLOAD_SOURCE_REMOTE_ACTION_RETRIEVED,
CONTENT_TYPE_NOTE, ContentTypes
CONTENT_TYPE_COMPONENT
} from 'snjs'; } from 'snjs';
import template from '%/directives/revision-preview-modal.pug'; import template from '%/directives/revision-preview-modal.pug';
@@ -25,7 +24,7 @@ class RevisionPreviewModalCtrl {
async configure() { async configure() {
this.note = await this.application.createItem({ this.note = await this.application.createItem({
contentType: CONTENT_TYPE_NOTE, contentType: ContentTypes.Note,
content: this.content content: this.content
}); });
@@ -43,7 +42,7 @@ class RevisionPreviewModalCtrl {
* editor object has non-copyable properties like .window, which cannot be transfered * editor object has non-copyable properties like .window, which cannot be transfered
*/ */
const editorCopy = await this.application.createItem({ const editorCopy = await this.application.createItem({
contentType: CONTENT_TYPE_COMPONENT, contentType: ContentTypes.Component,
content: editorForNote.content content: editorForNote.content
}); });
editorCopy.readonly = true; editorCopy.readonly = true;

View File

@@ -1,48 +1,46 @@
import { PrivilegesManager } from '@/services/privilegesManager'; import { EncryptionIntents, ProtectedActions } from 'snjs';
export class ArchiveManager { export class ArchiveManager {
/* @ngInject */ /* @ngInject */
constructor(lockManager, authManager, modelManager, privilegesManager) { constructor(lockManager, application, authManager, modelManager, privilegesManager) {
this.lockManager = lockManager; this.lockManager = lockManager;
this.authManager = authManager; this.authManager = authManager;
this.modelManager = modelManager; modelManager = modelManager;
this.privilegesManager = privilegesManager; this.privilegesManager = privilegesManager;
this.application = application;
} }
/* /*
Public Public
*/ */
/** @public */
async downloadBackup(encrypted) { async downloadBackup(encrypted) {
return this.downloadBackupOfItems(this.modelManager.allItems, encrypted); return this.downloadBackupOfItems(modelManager.allItems, encrypted);
} }
/** @public */
async downloadBackupOfItems(items, encrypted) { async downloadBackupOfItems(items, encrypted) {
const run = async () => { const run = async () => {
// download in Standard Notes format // download in Standard Notes format
let keys, authParams; const intent = encrypted
if(encrypted) { ? EncryptionIntents.FileEncrypted
if(this.authManager.offline() && this.lockManager.hasPasscode()) { : EncryptionIntents.FileDecrypted;
keys = this.lockManager.keys(); this.itemsData(items, intent).then((data) => {
authParams = this.lockManager.passcodeAuthParams();
} else {
keys = await this.authManager.keys();
authParams = await this.authManager.getAuthParams();
}
}
this.__itemsData(items, keys, authParams).then((data) => {
const modifier = encrypted ? "Encrypted" : "Decrypted"; 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 // download as zipped plain text files
if(!keys) { if (!encrypted) {
this.__downloadZippedItems(items); this.downloadZippedItems(items);
} }
}); });
}; };
if(await this.privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageBackups)) { if (await this.privilegesManager.actionRequiresPrivilege(ProtectedActions.ManageBackups)) {
this.godService.presentPrivilegesModal(PrivilegesManager.ActionManageBackups, () => { this.godService.presentPrivilegesModal(ProtectedActions.ManageBackups, () => {
run(); run();
}); });
} else { } else {
@@ -50,29 +48,30 @@ export class ArchiveManager {
} }
} }
/* /** @private */
Private formattedDate() {
*/
__formattedDate() {
var string = `${new Date()}`; var string = `${new Date()}`;
// Match up to the first parenthesis, i.e do not include '(Central Standard Time)' // Match up to the first parenthesis, i.e do not include '(Central Standard Time)'
var matches = string.match(/^(.*?) \(/); var matches = string.match(/^(.*?) \(/);
if(matches.length >= 2) { if (matches.length >= 2) {
return matches[1]; return matches[1];
} }
return string; return string;
} }
async __itemsData(items, keys, authParams) { /** @private */
const data = await this.modelManager.getJSONDataForItems(items, keys, authParams); async itemsData(items, intent) {
const blobData = new Blob([data], {type: 'text/json'}); const data = await this.application.protocolService.createBackupFile({
subItems: items,
intent: intent
});
const blobData = new Blob([data], { type: 'text/json' });
return blobData; return blobData;
} }
__loadZip(callback) { /** @private */
if(window.zip) { async loadZip() {
callback(); if (window.zip) {
return; return;
} }
@@ -81,75 +80,77 @@ export class ArchiveManager {
scriptTag.async = false; scriptTag.async = false;
var headTag = document.getElementsByTagName('head')[0]; var headTag = document.getElementsByTagName('head')[0];
headTag.appendChild(scriptTag); headTag.appendChild(scriptTag);
scriptTag.onload = function() { return new Promise((resolve, reject) => {
zip.workerScriptsPath = "assets/zip/"; scriptTag.onload = function () {
callback(); zip.workerScriptsPath = "assets/zip/";
}; resolve();
} };
__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);
}); });
} }
/** @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 // If we are replacing a previously generated file we need to
// manually revoke the object URL to avoid memory leaks. // manually revoke the object URL to avoid memory leaks.
if (this.textFile !== null) { if (this.textFile !== null) {
window.URL.revokeObjectURL(this.textFile); window.URL.revokeObjectURL(this.textFile);
} }
this.textFile = window.URL.createObjectURL(data); this.textFile = window.URL.createObjectURL(data);
// returns a URL you can use as a href // returns a URL you can use as a href
return this.textFile; return this.textFile;
} }
__downloadData(data, fileName) { /** @private */
downloadData(data, fileName) {
var link = document.createElement('a'); var link = document.createElement('a');
link.setAttribute('download', fileName); link.setAttribute('download', fileName);
link.href = this.__hrefForData(data); link.href = this.hrefForData(data);
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click();
link.remove(); link.remove();

View File

@@ -1,7 +1,8 @@
/* eslint-disable camelcase */
// An interface used by the Desktop app to interact with SN // An interface used by the Desktop app to interact with SN
import _ from 'lodash'; import pull from 'lodash/pull';
import { isDesktopApplication } from '@/utils'; import { isDesktopApplication } from '@/utils';
import { SFItemParams, SFModelManager } from 'snjs'; import { EncryptionIntents } from 'snjs';
const COMPONENT_DATA_KEY_INSTALL_ERROR = 'installError'; const COMPONENT_DATA_KEY_INSTALL_ERROR = 'installError';
const COMPONENT_CONTENT_KEY_PACKAGE_INFO = 'package_info'; const COMPONENT_CONTENT_KEY_PACKAGE_INFO = 'package_info';
@@ -12,33 +13,26 @@ export class DesktopManager {
constructor( constructor(
$rootScope, $rootScope,
$timeout, $timeout,
modelManager, application,
syncManager, appState,
authManager,
lockManager,
appState
) { ) {
this.lockManager = lockManager;
this.modelManager = modelManager;
this.authManager = authManager;
this.syncManager = syncManager;
this.$rootScope = $rootScope; this.$rootScope = $rootScope;
this.$timeout = $timeout;
this.appState = appState; this.appState = appState;
this.timeout = $timeout; this.application = application;
this.updateObservers = [];
this.componentActivationObservers = []; this.componentActivationObservers = [];
this.updateObservers = [];
this.isDesktop = isDesktopApplication(); this.isDesktop = isDesktopApplication();
$rootScope.$on("initial-data-loaded", () => { $rootScope.$on('initial-data-loaded', () => {
this.dataLoaded = true; this.dataLoaded = true;
if(this.dataLoadHandler) { if (this.dataLoadHandler) {
this.dataLoadHandler(); this.dataLoadHandler();
} }
}); });
$rootScope.$on("major-data-change", () => { $rootScope.$on('major-data-change', () => {
if(this.majorDataChangeHandler) { if (this.majorDataChangeHandler) {
this.majorDataChangeHandler(); this.majorDataChangeHandler();
} }
}); });
@@ -56,17 +50,20 @@ export class DesktopManager {
return this.extServerHost; return this.extServerHost;
} }
/* /**
Sending a component in its raw state is really slow for the desktop app * 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 * Keys are not passed into ItemParams, so the result is not encrypted
*/ */
async convertComponentForTransmission(component) { 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 // All `components` should be installed
syncComponentsInstallation(components) { syncComponentsInstallation(components) {
if(!this.isDesktop) { if (!this.isDesktop) {
return; return;
} }
Promise.all(components.map((component) => { Promise.all(components.map((component) => {
@@ -91,21 +88,21 @@ export class DesktopManager {
} }
searchText(text) { searchText(text) {
if(!this.isDesktop) { if (!this.isDesktop) {
return; return;
} }
this.lastSearchedText = text; this.lastSearchedText = text;
this.searchHandler && this.searchHandler(text); this.searchHandler && this.searchHandler(text);
} }
redoSearch() { redoSearch() {
if(this.lastSearchedText) { if (this.lastSearchedText) {
this.searchText(this.lastSearchedText); this.searchText(this.lastSearchedText);
} }
} }
deregisterUpdateObserver(observer) { deregisterUpdateObserver(observer) {
_.pull(this.updateObservers, observer); pull(this.updateObservers, observer);
} }
// Pass null to cancel search // Pass null to cancel search
@@ -114,19 +111,19 @@ export class DesktopManager {
} }
desktop_windowGainedFocus() { desktop_windowGainedFocus() {
this.$rootScope.$broadcast("window-gained-focus"); this.$rootScope.$broadcast('window-gained-focus');
} }
desktop_windowLostFocus() { desktop_windowLostFocus() {
this.$rootScope.$broadcast("window-lost-focus"); this.$rootScope.$broadcast('window-lost-focus');
} }
desktop_onComponentInstallationComplete(componentData, error) { async desktop_onComponentInstallationComplete(componentData, error) {
const component = this.modelManager.findItem(componentData.uuid); const component = await this.application.findItem({ uuid: componentData.uuid });
if(!component) { if (!component) {
return; return;
} }
if(error) { if (error) {
component.setAppDataItem( component.setAppDataItem(
COMPONENT_DATA_KEY_INSTALL_ERROR, COMPONENT_DATA_KEY_INSTALL_ERROR,
error error
@@ -136,35 +133,30 @@ export class DesktopManager {
COMPONENT_CONTENT_KEY_PACKAGE_INFO, COMPONENT_CONTENT_KEY_PACKAGE_INFO,
COMPONENT_CONTENT_KEY_LOCAL_URL COMPONENT_CONTENT_KEY_LOCAL_URL
]; ];
for(const key of permissableKeys) { for (const key of permissableKeys) {
component[key] = componentData.content[key]; component[key] = componentData.content[key];
} }
this.modelManager.notifySyncObserversOfModels(
[component],
SFModelManager.MappingSourceDesktopInstalled
);
component.setAppDataItem( component.setAppDataItem(
COMPONENT_DATA_KEY_INSTALL_ERROR, COMPONENT_DATA_KEY_INSTALL_ERROR,
null null
); );
} }
this.modelManager.setItemDirty(component); this.application.saveItem({ item: component });
this.syncManager.sync(); this.$timeout(() => {
this.timeout(() => { for (const observer of this.updateObservers) {
for(const observer of this.updateObservers) {
observer.callback(component); observer.callback(component);
} }
}); });
} }
desktop_registerComponentActivationObserver(callback) { desktop_registerComponentActivationObserver(callback) {
const observer = {id: Math.random, callback: callback}; const observer = { id: Math.random, callback: callback };
this.componentActivationObservers.push(observer); this.componentActivationObservers.push(observer);
return observer; return observer;
} }
desktop_deregisterComponentActivationObserver(observer) { desktop_deregisterComponentActivationObserver(observer) {
_.pull(this.componentActivationObservers, observer); pull(this.componentActivationObservers, observer);
} }
/* Notify observers that a component has been registered/activated */ /* Notify observers that a component has been registered/activated */
@@ -172,14 +164,14 @@ export class DesktopManager {
const serializedComponent = await this.convertComponentForTransmission( const serializedComponent = await this.convertComponentForTransmission(
component component
); );
this.timeout(() => { this.$timeout(() => {
for(const observer of this.componentActivationObservers) { for (const observer of this.componentActivationObservers) {
observer.callback(serializedComponent); observer.callback(serializedComponent);
} }
}); });
} }
/* Used to resolve "sn://" */ /* Used to resolve 'sn://' */
desktop_setExtServerHost(host) { desktop_setExtServerHost(host) {
this.extServerHost = host; this.extServerHost = host;
this.appState.desktopExtensionsReady(); this.appState.desktopExtensionsReady();
@@ -195,28 +187,16 @@ export class DesktopManager {
desktop_setInitialDataLoadHandler(handler) { desktop_setInitialDataLoadHandler(handler) {
this.dataLoadHandler = handler; this.dataLoadHandler = handler;
if(this.dataLoaded) { if (this.dataLoaded) {
this.dataLoadHandler(); this.dataLoadHandler();
} }
} }
async desktop_requestBackupFile(callback) { async desktop_requestBackupFile(callback) {
let keys, authParams; const data = await this.application.protocolService.createBackupFile({
if(this.authManager.offline() && this.lockManager.hasPasscode()) { returnIfEmpty: true
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);
}); });
callback(data);
} }
desktop_setMajorDataChangeHandler(handler) { desktop_setMajorDataChangeHandler(handler) {

View File

@@ -1,21 +1,10 @@
export { ActionsManager } from './actionsManager';
export { ArchiveManager } from './archiveManager'; export { ArchiveManager } from './archiveManager';
export { AuthManager } from './authManager';
export { ComponentManager } from './componentManager';
export { DatabaseManager } from './databaseManager'; export { DatabaseManager } from './databaseManager';
export { DesktopManager } from './desktopManager'; export { DesktopManager } from './desktopManager';
export { HttpManager } from './httpManager';
export { KeyboardManager } from './keyboardManager'; export { KeyboardManager } from './keyboardManager';
export { MigrationManager } from './migrationManager';
export { ModelManager } from './modelManager';
export { NativeExtManager } from './nativeExtManager'; export { NativeExtManager } from './nativeExtManager';
export { LockManager } from './lockManager'; export { LockManager } from './lockManager';
export { PrivilegesManager } from './privilegesManager';
export { SessionHistory } from './sessionHistory';
export { SingletonManager } from './singletonManager';
export { StatusManager } from './statusManager'; export { StatusManager } from './statusManager';
export { StorageManager } from './storageManager';
export { SyncManager } from './syncManager';
export { ThemeManager } from './themeManager'; export { ThemeManager } from './themeManager';
export { AlertManager } from './alertManager'; export { AlertManager } from './alertManager';
export { PreferencesManager } from './preferencesManager'; 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() { constructor() {
this.observers = []; 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('keydown', this.handleKeyDown.bind(this));
window.addEventListener('keyup', this.handleKeyUp.bind(this)); window.addEventListener('keyup', this.handleKeyUp.bind(this));
} }
modifiersForEvent(event) { 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. // 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. // That's because on keyup, event.ctrlKey would be false, but event.key == Control would be true.
const matches = ( const matches = (
((event.ctrlKey || event.key == KeyboardManager.KeyModifierCtrl) && modifier === KeyboardManager.KeyModifierCtrl) || ((event.ctrlKey || event.key === KeyboardModifiers.Ctrl) && modifier === KeyboardModifiers.Ctrl) ||
((event.metaKey || event.key == KeyboardManager.KeyModifierMeta) && modifier === KeyboardManager.KeyModifierMeta) || ((event.metaKey || event.key === KeyboardModifiers.Meta) && modifier === KeyboardModifiers.Meta) ||
((event.altKey || event.key == KeyboardManager.KeyModifierAlt) && modifier === KeyboardManager.KeyModifierAlt) || ((event.altKey || event.key === KeyboardModifiers.Alt) && modifier === KeyboardModifiers.Alt) ||
((event.shiftKey || event.key == KeyboardManager.KeyModifierShift) && modifier === KeyboardManager.KeyModifierShift) ((event.shiftKey || event.key === KeyboardModifiers.Shift) && modifier === KeyboardModifiers.Shift)
); );
return matches; return matches;
@@ -45,50 +44,50 @@ export class KeyboardManager {
return eventModifiers; return eventModifiers;
} }
eventMatchesKeyAndModifiers(event, key, modifiers = []) { eventMatchesKeyAndModifiers(event, key, modifiers = []) {
const eventModifiers = this.modifiersForEvent(event); const eventModifiers = this.modifiersForEvent(event);
if(eventModifiers.length != modifiers.length) { if (eventModifiers.length !== modifiers.length) {
return false; return false;
} }
for(const modifier of modifiers) { for (const modifier of modifiers) {
if(!eventModifiers.includes(modifier)) { if (!eventModifiers.includes(modifier)) {
return false; return false;
} }
} }
// Modifers match, check key // Modifers match, check key
if(!key) { if (!key) {
return true; return true;
} }
// In the browser, shift + f results in key 'f', but in Electron, shift + f results in 'F' // 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. // 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) { notifyObserver(event, keyEventType) {
for(const observer of this.observers) { for (const observer of this.observers) {
if(observer.element && event.target != observer.element) { if (observer.element && event.target !== observer.element) {
continue; continue;
} }
if(observer.elements && !observer.elements.includes(event.target)) { if (observer.elements && !observer.elements.includes(event.target)) {
continue; continue;
} }
if(observer.notElement && observer.notElement == event.target) { if (observer.notElement && observer.notElement === event.target) {
continue; continue;
} }
if(observer.notElementIds && observer.notElementIds.includes(event.target.id)) { if (observer.notElementIds && observer.notElementIds.includes(event.target.id)) {
continue; continue;
} }
if(this.eventMatchesKeyAndModifiers(event, observer.key, observer.modifiers)) { 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) { if (callback) {
callback(event); callback(event);
} }
} }
@@ -96,15 +95,15 @@ export class KeyboardManager {
} }
handleKeyDown(event) { handleKeyDown(event) {
this.notifyObserver(event, KeyboardManager.KeyEventDown); this.notifyObserver(event, KeyboardKeyEvents.Down);
} }
handleKeyUp(event) { handleKeyUp(event) {
this.notifyObserver(event, KeyboardManager.KeyEventUp); this.notifyObserver(event, KeyboardKeyEvents.Up);
} }
addKeyObserver({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}; const observer = { key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds };
this.observers.push(observer); this.observers.push(observer);
return observer; return observer;
} }

View File

@@ -1,9 +1,5 @@
import _ from 'lodash';
import { isDesktopApplication } from '@/utils'; import { isDesktopApplication } from '@/utils';
import { import { AppStateEvents } from '../state';
APP_STATE_EVENT_WINDOW_DID_BLUR,
APP_STATE_EVENT_WINDOW_DID_FOCUS
} from '../state';
const MILLISECONDS_PER_SECOND = 1000; const MILLISECONDS_PER_SECOND = 1000;
const FOCUS_POLL_INTERVAL = 1 * MILLISECONDS_PER_SECOND; const FOCUS_POLL_INTERVAL = 1 * MILLISECONDS_PER_SECOND;
@@ -26,9 +22,9 @@ export class LockManager {
observeVisibility() { observeVisibility() {
this.appState.addObserver((eventName, data) => { this.appState.addObserver((eventName, data) => {
if(eventName === APP_STATE_EVENT_WINDOW_DID_BLUR) { if(eventName === AppStateEvents.WindowDidBlur) {
this.documentVisibilityChanged(false); this.documentVisibilityChanged(false);
} else if(eventName === APP_STATE_EVENT_WINDOW_DID_FOCUS) { } else if(eventName === AppStateEvents.WindowDidFocus) {
this.documentVisibilityChanged(true); 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'; const STREAM_ITEMS_PERMISSION = 'stream-items';
import { SFPredicate } from 'snjs';
/** A class for handling installation of system extensions */
export class NativeExtManager { export class NativeExtManager {
/* @ngInject */ /* @ngInject */
constructor(modelManager, syncManager, singletonManager) { constructor(application, appState) {
this.modelManager = modelManager; this.application = application;
this.syncManager = syncManager; this.extManagerId = 'org.standardnotes.extensions-manager';
this.singletonManager = singletonManager; this.batchManagerId = 'org.standardnotes.batch-manager';
this.extManagerId = "org.standardnotes.extensions-manager";
this.batchManagerId = "org.standardnotes.batch-manager";
this.systemExtensions = []; this.systemExtensions = [];
this.resolveExtensionsManager(); this.resolveExtensionsManager();
this.resolveBatchManager(); this.resolveBatchManager();
appState.addObserver(async (eventName) => {
if (eventName === AppStateEvents.ApplicationReady) {
await this.initialize();
}
});
} }
isSystemExtension(extension) { isSystemExtension(extension) {
return this.systemExtensions.includes(extension.uuid); return this.systemExtensions.includes(extension.uuid);
} }
resolveExtensionsManager() { async initialize() {
this.resolveExtensionsManager();
this.resolveBatchManager();
}
const contentTypePredicate = new SFPredicate("content_type", "=", "SN|Component"); extensionsManagerTemplatePayload() {
const packagePredicate = new SFPredicate("package_info.identifier", "=", this.extManagerId); const url = window._extensions_manager_location;
if (!url) {
this.singletonManager.registerSingleton([contentTypePredicate, packagePredicate], (resolvedSingleton) => { console.error('window._extensions_manager_location must be set.');
// Resolved Singleton return;
this.systemExtensions.push(resolvedSingleton.uuid); }
const packageInfo = {
var needsSync = false; name: 'Extensions',
if(isDesktopApplication()) { identifier: this.extManagerId
if(!resolvedSingleton.local_url) { };
resolvedSingleton.local_url = window._extensions_manager_location; const content = {
needsSync = true; name: packageInfo.name,
} area: 'rooms',
} else { package_info: packageInfo,
if(!resolvedSingleton.hosted_url) { permissions: [
resolvedSingleton.hosted_url = window._extensions_manager_location; {
needsSync = true; 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 async resolveExtensionsManager() {
const permission = resolvedSingleton.content.permissions.find((p) => p.name == "stream-items"); const contentTypePredicate = new SFPredicate('content_type', '=', ContentTypes.Component);
if(!permission.content_types.includes("SN|ExtensionRepo")) { const packagePredicate = new SFPredicate('package_info.identifier', '=', this.extManagerId);
permission.content_types.push("SN|ExtensionRepo"); 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; needsSync = true;
} }
} else {
if(needsSync) { if (!extensionsManager.hosted_url) {
this.modelManager.setItemDirty(resolvedSingleton, true); extensionsManager.hosted_url = window._extensions_manager_location;
this.syncManager.sync(); needsSync = true;
} }
}, (valueCallback) => { }
// Safe to create. Create and return object. // Handle addition of SN|ExtensionRepo permission
const url = window._extensions_manager_location; const permission = extensionsManager.content.permissions.find((p) => p.name === STREAM_ITEMS_PERMISSION);
if(!url) { if (!permission.content_types.includes(ContentTypes.ExtensionRepo)) {
console.error("window._extensions_manager_location must be set."); permission.content_types.push(ContentTypes.ExtensionRepo);
return; needsSync = true;
} }
if (needsSync) {
const packageInfo = { this.application.saveItem({ item: extensionsManager });
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);
});
} }
resolveBatchManager() { batchManagerTemplatePayload() {
const url = window._batch_manager_location;
const contentTypePredicate = new SFPredicate("content_type", "=", "SN|Component"); if (!url) {
const packagePredicate = new SFPredicate("package_info.identifier", "=", this.batchManagerId); console.error('window._batch_manager_location must be set.');
return;
this.singletonManager.registerSingleton([contentTypePredicate, packagePredicate], (resolvedSingleton) => { }
// Resolved Singleton const packageInfo = {
this.systemExtensions.push(resolvedSingleton.uuid); name: 'Batch Manager',
identifier: this.batchManagerId
var needsSync = false; };
if(isDesktopApplication()) { const allContentTypes = dictToArray(ContentTypes);
if(!resolvedSingleton.local_url) { const content = {
resolvedSingleton.local_url = window._batch_manager_location; name: packageInfo.name,
needsSync = true; area: 'modal',
} package_info: packageInfo,
} else { permissions: [
if(!resolvedSingleton.hosted_url) { {
resolvedSingleton.hosted_url = window._batch_manager_location; name: STREAM_ITEMS_PERMISSION,
needsSync = true; 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 });
}
} }
} }

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 PrefKeys = {
export const PREF_NOTES_PANEL_WIDTH = 'notesPanelWidth'; TagsPanelWidth: 'tagsPanelWidth',
export const PREF_EDITOR_WIDTH = 'editorWidth'; NotesPanelWidth: 'notesPanelWidth',
export const PREF_EDITOR_LEFT = 'editorLeft'; EditorWidth: 'editorWidth',
export const PREF_EDITOR_MONOSPACE_ENABLED = 'monospaceFont'; EditorLeft: 'editorLeft',
export const PREF_EDITOR_SPELLCHECK = 'spellcheck'; EditorMonospaceEnabled: 'monospaceFont',
export const PREF_EDITOR_RESIZERS_ENABLED = 'marginResizersEnabled'; EditorSpellcheck: 'spellcheck',
export const PREF_SORT_NOTES_BY = 'sortBy'; EditorResizersEnabled: 'marginResizersEnabled',
export const PREF_SORT_NOTES_REVERSE = 'sortReverse'; SortNotesBy: 'sortBy',
export const PREF_NOTES_SHOW_ARCHIVED = 'showArchived'; SortNotesReverse: 'sortReverse',
export const PREF_NOTES_HIDE_PINNED = 'hidePinned'; NotesShowArchived: 'showArchived',
export const PREF_NOTES_HIDE_NOTE_PREVIEW = 'hideNotePreview'; NotesHidePinned: 'hidePinned',
export const PREF_NOTES_HIDE_DATE = 'hideDate'; NotesHideNotePreview: 'hideNotePreview',
export const PREF_NOTES_HIDE_TAGS = 'hideTags'; NotesHideDate: 'hideDate',
NotesHideTags: 'hideTags'
};
export class PreferencesManager { export class PreferencesManager {
/* @ngInject */ /* @ngInject */
constructor( constructor(
modelManager,
singletonManager,
appState, appState,
syncManager application
) { ) {
this.singletonManager = singletonManager; this.application = application;
this.modelManager = modelManager;
this.syncManager = syncManager;
this.appState = appState; this.appState = appState;
appState.addObserver(async (eventName) => {
this.modelManager.addItemSyncObserver( if (eventName === AppStateEvents.ApplicationReady) {
'user-prefs', await this.initialize();
'SN|UserPreferences',
(allItems, validItems, deletedItems, source, sourceKey) => {
this.preferencesDidChange();
} }
); });
} }
load() { async initialize() {
const prefsContentType = 'SN|UserPreferences'; this.streamPreferences();
const contentTypePredicate = new SFPredicate( await this.loadSingleton();
'content_type', }
'=',
prefsContentType streamPreferences() {
); this.application.streamItems({
this.singletonManager.registerSingleton( contentType: 'SN|UserPreferences',
[contentTypePredicate], stream: () => {
(resolvedSingleton) => { this.preferencesDidChange();
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() { preferencesDidChange() {
@@ -65,21 +65,20 @@ export class PreferencesManager {
} }
syncUserPreferences() { syncUserPreferences() {
if(this.userPreferences) { if (this.userPreferences) {
this.modelManager.setItemDirty(this.userPreferences); this.application.saveItem({item: this.userPreferences});
this.syncManager.sync();
} }
} }
getValue(key, defaultValue) { getValue(key, defaultValue) {
if(!this.userPreferences) { return defaultValue; } if (!this.userPreferences) { return defaultValue; }
const value = this.userPreferences.getAppDataItem(key); const value = this.userPreferences.getAppDataItem(key);
return (value !== undefined && value != null) ? value : defaultValue; return (value !== undefined && value != null) ? value : defaultValue;
} }
setUserPrefValue(key, value, sync) { setUserPrefValue(key, value, sync) {
this.userPreferences.setAppDataItem(key, value); this.userPreferences.setAppDataItem(key, value);
if(sync) { if (sync) {
this.syncUserPreferences(); this.syncUserPreferences();
} }
} }

View File

@@ -1,42 +1,28 @@
import _ from 'lodash'; import _ from 'lodash';
import angular from 'angular'; import { SNTheme, StorageValueModes, EncryptionIntents } from 'snjs';
import { SNTheme, SFItemParams } from 'snjs'; import { AppStateEvents } from '@/state';
import { StorageManager } from './storageManager';
import { const CACHED_THEMES_KEY = 'cachedThemes';
APP_STATE_EVENT_DESKTOP_EXTS_READY
} from '@/state';
export class ThemeManager { export class ThemeManager {
/* @ngInject */ /* @ngInject */
constructor( constructor(
componentManager, application,
appState,
desktopManager, desktopManager,
storageManager,
lockManager,
appState
) { ) {
this.componentManager = componentManager; this.application = application;
this.storageManager = storageManager; this.appState = appState;
this.desktopManager = desktopManager; this.desktopManager = desktopManager;
this.activeThemes = []; this.activeThemes = [];
ThemeManager.CachedThemesKey = "cachedThemes";
this.registerObservers(); this.registerObservers();
if (!desktopManager.isDesktop) {
if (desktopManager.isDesktop) {
appState.addObserver((eventName, data) => {
if (eventName === APP_STATE_EVENT_DESKTOP_EXTS_READY) {
this.activateCachedThemes();
}
});
} else {
this.activateCachedThemes(); this.activateCachedThemes();
} }
} }
activateCachedThemes() { async activateCachedThemes() {
const cachedThemes = this.getCachedThemes(); const cachedThemes = await this.getCachedThemes();
const writeToCache = false; const writeToCache = false;
for (const theme of cachedThemes) { for (const theme of cachedThemes) {
this.activateTheme(theme, writeToCache); this.activateTheme(theme, writeToCache);
@@ -44,6 +30,11 @@ export class ThemeManager {
} }
registerObservers() { registerObservers() {
this.appState.addObserver((eventName, data) => {
if (eventName === AppStateEvents.DesktopExtsReady) {
this.activateCachedThemes();
}
});
this.desktopManager.registerUpdateObserver((component) => { this.desktopManager.registerUpdateObserver((component) => {
// Reload theme if active // Reload theme if active
if (component.active && component.isTheme()) { if (component.active && component.isTheme()) {
@@ -54,9 +45,9 @@ export class ThemeManager {
} }
}); });
this.componentManager.registerHandler({ this.application.componentManager.registerHandler({
identifier: "themeManager", identifier: 'themeManager',
areas: ["themes"], areas: ['themes'],
activationHandler: (component) => { activationHandler: (component) => {
if (component.active) { if (component.active) {
this.activateTheme(component); this.activateTheme(component);
@@ -68,14 +59,14 @@ export class ThemeManager {
} }
hasActiveTheme() { hasActiveTheme() {
return this.componentManager.getActiveThemes().length > 0; return this.application.componentManager.getActiveThemes().length > 0;
} }
deactivateAllThemes() { deactivateAllThemes() {
var activeThemes = this.componentManager.getActiveThemes(); var activeThemes = this.application.componentManager.getActiveThemes();
for (var theme of activeThemes) { for (var theme of activeThemes) {
if (theme) { 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 })) { if (_.find(this.activeThemes, { uuid: theme.uuid })) {
return; return;
} }
this.activeThemes.push(theme); this.activeThemes.push(theme);
const url = this.application.componentManager.urlForComponent(theme);
var url = this.componentManager.urlForComponent(theme); const link = document.createElement('link');
var link = document.createElement("link");
link.href = url; link.href = url;
link.type = "text/css"; link.type = 'text/css';
link.rel = "stylesheet"; link.rel = 'stylesheet';
link.media = "screen,print"; link.media = 'screen,print';
link.id = theme.uuid; link.id = theme.uuid;
document.getElementsByTagName("head")[0].appendChild(link); document.getElementsByTagName('head')[0].appendChild(link);
if (writeToCache) { if (writeToCache) {
this.cacheThemes(); this.cacheThemes();
} }
} }
deactivateTheme(theme) { deactivateTheme(theme) {
var element = document.getElementById(theme.uuid); const element = document.getElementById(theme.uuid);
if (element) { if (element) {
element.disabled = true; element.disabled = true;
element.parentNode.removeChild(element); element.parentNode.removeChild(element);
@@ -117,20 +105,33 @@ export class ThemeManager {
async cacheThemes() { async cacheThemes() {
const mapped = await Promise.all(this.activeThemes.map(async (theme) => { const mapped = await Promise.all(this.activeThemes.map(async (theme) => {
const transformer = new SFItemParams(theme); const payload = theme.payloadRepresentation();
const params = await transformer.paramsForLocalStorage(); const processedPayload = await this.application.protocolService.payloadByEncryptingPayload({
return params; payload: payload,
intent: EncryptionIntents.LocalStorageDecrypted
});
return processedPayload;
})); }));
const data = JSON.stringify(mapped); 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() { async decacheThemes() {
return this.storageManager.removeItem(ThemeManager.CachedThemesKey, StorageManager.Fixed); return this.application.removeValue(
CACHED_THEMES_KEY,
StorageValueModes.Nonwrapped
);
} }
getCachedThemes() { async getCachedThemes() {
const cachedThemes = this.storageManager.getItemSync(ThemeManager.CachedThemesKey, StorageManager.Fixed); const cachedThemes = await this.application.getValue(
CACHED_THEMES_KEY,
StorageValueModes.Nonwrapped
);
if (cachedThemes) { if (cachedThemes) {
const parsed = JSON.parse(cachedThemes); const parsed = JSON.parse(cachedThemes);
return parsed.map((theme) => { return parsed.map((theme) => {

View File

@@ -1,32 +1,39 @@
import { PrivilegesManager } from '@/services/privilegesManager';
import { isDesktopApplication } from '@/utils'; import { isDesktopApplication } from '@/utils';
import pull from 'lodash/pull'; import pull from 'lodash/pull';
import { ProtectedActions } from 'snjs';
export const APP_STATE_EVENT_TAG_CHANGED = 1; export const AppStateEvents = {
export const APP_STATE_EVENT_NOTE_CHANGED = 2; TagChanged: 1,
export const APP_STATE_EVENT_PREFERENCES_CHANGED = 3; NoteChanged: 2,
export const APP_STATE_EVENT_PANEL_RESIZED = 4; PreferencesChanged: 3,
export const APP_STATE_EVENT_EDITOR_FOCUSED = 5; PanelResized: 4,
export const APP_STATE_EVENT_BEGAN_BACKUP_DOWNLOAD = 6; EditorFocused: 5,
export const APP_STATE_EVENT_ENDED_BACKUP_DOWNLOAD = 7; BeganBackupDownload: 6,
export const APP_STATE_EVENT_DESKTOP_EXTS_READY = 8; EndedBackupDownload: 7,
export const APP_STATE_EVENT_WINDOW_DID_FOCUS = 9; DesktopExtsReady: 8,
export const APP_STATE_EVENT_WINDOW_DID_BLUR = 10; WindowDidFocus: 9,
WindowDidBlur: 10,
/** Register observers and streamers on this event */
ApplicationReady: 11
};
export const EVENT_SOURCE_USER_INTERACTION = 1; export const EventSources = {
export const EVENT_SOURCE_SCRIPT = 2; UserInteraction: 1,
Script: 2
};
export class AppState { export class AppState {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$timeout, $timeout,
$rootScope, $rootScope,
privilegesManager application,
) { ) {
this.$timeout = $timeout; this.$timeout = $timeout;
this.$rootScope = $rootScope; this.$rootScope = $rootScope;
this.privilegesManager = privilegesManager; this.application = application;
this.observers = []; this.observers = [];
this.registerVisibilityObservers(); this.registerVisibilityObservers();
} }
@@ -34,18 +41,18 @@ export class AppState {
registerVisibilityObservers() { registerVisibilityObservers() {
if (isDesktopApplication()) { if (isDesktopApplication()) {
this.$rootScope.$on('window-lost-focus', () => { 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.$rootScope.$on('window-gained-focus', () => {
this.notifyEvent(APP_STATE_EVENT_WINDOW_DID_FOCUS); this.notifyEvent(AppStateEvents.WindowDidFocus);
}); });
} else { } else {
/* Tab visibility listener, web only */ /* Tab visibility listener, web only */
document.addEventListener('visibilitychange', (e) => { document.addEventListener('visibilitychange', (e) => {
const visible = document.visibilityState === "visible"; const visible = document.visibilityState === "visible";
const event = visible const event = visible
? APP_STATE_EVENT_WINDOW_DID_FOCUS ? AppStateEvents.WindowDidFocus
: APP_STATE_EVENT_WINDOW_DID_BLUR; : AppStateEvents.WindowDidBlur;
this.notifyEvent(event); this.notifyEvent(event);
}); });
} }
@@ -66,7 +73,7 @@ export class AppState {
*/ */
return new Promise((resolve) => { return new Promise((resolve) => {
this.$timeout(async () => { this.$timeout(async () => {
for(const callback of this.observers) { for (const callback of this.observers) {
await callback(eventName, data); await callback(eventName, data);
} }
resolve(); resolve();
@@ -75,14 +82,14 @@ export class AppState {
} }
setSelectedTag(tag) { setSelectedTag(tag) {
if(this.selectedTag === tag) { if (this.selectedTag === tag) {
return; return;
} }
const previousTag = this.selectedTag; const previousTag = this.selectedTag;
this.selectedTag = tag; this.selectedTag = tag;
this.notifyEvent( this.notifyEvent(
APP_STATE_EVENT_TAG_CHANGED, AppStateEvents.TagChanged,
{previousTag: previousTag} { previousTag: previousTag }
); );
} }
@@ -91,16 +98,16 @@ export class AppState {
const previousNote = this.selectedNote; const previousNote = this.selectedNote;
this.selectedNote = note; this.selectedNote = note;
await this.notifyEvent( await this.notifyEvent(
APP_STATE_EVENT_NOTE_CHANGED, AppStateEvents.NoteChanged,
{ previousNote: previousNote } { previousNote: previousNote }
); );
}; };
if (note && note.content.protected && if (note && note.content.protected &&
await this.privilegesManager.actionRequiresPrivilege( await this.application.privilegesManager.actionRequiresPrivilege(
PrivilegesManager.ActionViewProtectedNotes ProtectedActions.ViewProtectedNotes
)) { )) {
this.godService.presentPrivilegesModal( this.godService.presentPrivilegesModal(
PrivilegesManager.ActionViewProtectedNotes, ProtectedActions.ViewProtectedNotes,
run run
); );
} else { } else {
@@ -119,13 +126,13 @@ export class AppState {
setUserPreferences(preferences) { setUserPreferences(preferences) {
this.userPreferences = preferences; this.userPreferences = preferences;
this.notifyEvent( this.notifyEvent(
APP_STATE_EVENT_PREFERENCES_CHANGED AppStateEvents.PreferencesChanged
); );
} }
panelDidResize({name, collapsed}) { panelDidResize({ name, collapsed }) {
this.notifyEvent( this.notifyEvent(
APP_STATE_EVENT_PANEL_RESIZED, AppStateEvents.PanelResized,
{ {
panel: name, panel: name,
collapsed: collapsed collapsed: collapsed
@@ -135,21 +142,21 @@ export class AppState {
editorDidFocus(eventSource) { editorDidFocus(eventSource) {
this.notifyEvent( this.notifyEvent(
APP_STATE_EVENT_EDITOR_FOCUSED, AppStateEvents.EditorFocused,
{eventSource: eventSource} { eventSource: eventSource }
); );
} }
beganBackupDownload() { beganBackupDownload() {
this.notifyEvent( this.notifyEvent(
APP_STATE_EVENT_BEGAN_BACKUP_DOWNLOAD AppStateEvents.BeganBackupDownload
); );
} }
endedBackupDownload({success}) { endedBackupDownload({ success }) {
this.notifyEvent( this.notifyEvent(
APP_STATE_EVENT_ENDED_BACKUP_DOWNLOAD, AppStateEvents.EndedBackupDownload,
{success: success} { success: success }
); );
} }
@@ -158,7 +165,7 @@ export class AppState {
*/ */
desktopExtensionsReady() { desktopExtensionsReady() {
this.notifyEvent( this.notifyEvent(
APP_STATE_EVENT_DESKTOP_EXTS_READY AppStateEvents.DesktopExtsReady
); );
} }

View File

@@ -1,49 +1,49 @@
/** @generic */ /** @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_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_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_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) { export function StringSyncException(data) {
return `There was an error while trying to save your items. Please contact support and share this message: ${data}.`; return `There was an error while trying to save your items. Please contact support and share this message: ${data}.`;
} }
/** @footer */ /** @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 */ /** @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 */ /** @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_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_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_ELLIPSES = "...";
export const STRING_GENERIC_SAVE_ERROR = "There was an error saving your note. Please try again."; 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_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 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 function StringDeleteNote({ title, permanently }) {
return permanently return permanently
? `Are you sure you want to permanently delete ${title}?` ? `Are you sure you want to permanently delete ${title}?`
: `Are you sure you want to move ${title} to the trash?`; : `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)?`; return `Are you sure you want to permanently delete ${count} note(s)?`;
} }
/** @account */ /** @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_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_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_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_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_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_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_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_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_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_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_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_LOGIN_KEYS = "Generating Login Keys...";
export const STRING_GENERATING_REGISTER_KEYS = "Generating Account 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 const STRING_INVALID_IMPORT_FILE = "Unable to open file. Ensure it is a proper JSON file and try again.";
export function StringImportError({errorCount}) { 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.`; return `Import complete. ${errorCount} items were not imported because there was an error decrypting them. Make sure the password is correct and try again.`;
} }

View File

@@ -20,6 +20,10 @@ export function isNullOrUndefined(value) {
return value === null || value === undefined; return value === null || value === undefined;
} }
export function dictToArray(dict) {
return Object.keys(dict).map((key) => dict[key]);
}
export function getPlatformString() { export function getPlatformString() {
try { try {
const platform = navigator.platform.toLowerCase(); const platform = navigator.platform.toLowerCase();

11440
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