ApplicationManager and better memory management

This commit is contained in:
Mo Bitar
2020-03-23 19:59:55 -05:00
parent 7dc3dab90b
commit ee7cb1fce6
55 changed files with 36786 additions and 3046 deletions

View File

@@ -4,20 +4,16 @@ import angular from 'angular';
import { configRoutes } from './routes'; import { configRoutes } from './routes';
import { import {
Application ApplicationManager
} from './application'; } from './applicationManager';
import {
AppState
} from './state';
import { import {
Root, Root,
ApplicationView,
TagsPanel, TagsPanel,
NotesPanel, NotesPanel,
EditorPanel, EditorPanel,
Footer, Footer
LockScreen
} from './controllers'; } from './controllers';
import { import {
@@ -54,18 +50,6 @@ import {
import { trusted } from './filters'; import { trusted } from './filters';
import {
ArchiveManager,
DesktopManager,
KeyboardManager,
GodService,
LockManager,
NativeExtManager,
PreferencesManager,
StatusManager,
ThemeManager,
} from './services';
angular.module('app', ['ngSanitize']); angular.module('app', ['ngSanitize']);
// Config // Config
@@ -78,11 +62,12 @@ angular
angular angular
.module('app') .module('app')
.directive('root', () => new Root()) .directive('root', () => new Root())
.directive('applicationView', () => new ApplicationView())
.directive('tagsPanel', () => new TagsPanel()) .directive('tagsPanel', () => new TagsPanel())
.directive('notesPanel', () => new NotesPanel()) .directive('notesPanel', () => new NotesPanel())
.directive('editorPanel', () => new EditorPanel()) .directive('editorPanel', () => new EditorPanel())
.directive('footer', () => new Footer()) .directive('footer', () => new Footer())
.directive('lockScreen', () => new LockScreen()); // .directive('lockScreen', () => new LockScreen());
// Directives - Functional // Directives - Functional
angular angular
@@ -93,9 +78,6 @@ angular
.directive('elemReady', elemReady) .directive('elemReady', elemReady)
.directive('fileChange', fileChange) .directive('fileChange', fileChange)
.directive('infiniteScroll', [ .directive('infiniteScroll', [
'$rootScope',
'$window',
'$timeout',
infiniteScroll infiniteScroll
]) ])
.directive('lowercase', lowercase) .directive('lowercase', lowercase)
@@ -134,16 +116,4 @@ angular
.filter('trusted', ['$sce', trusted]); .filter('trusted', ['$sce', trusted]);
// Services // Services
angular angular.module('app').service('applicationManager', ApplicationManager);
.module('app')
.service('appState', AppState)
.service('application', Application)
.service('archiveManager', ArchiveManager)
.service('desktopManager', DesktopManager)
.service('godService', GodService)
.service('keyboardManager', KeyboardManager)
.service('lockManager', LockManager)
.service('nativeExtManager', NativeExtManager)
.service('preferencesManager', PreferencesManager)
.service('statusManager', StatusManager)
.service('themeManager', ThemeManager);

View File

@@ -4,13 +4,14 @@ import {
Environments, Environments,
platformFromString platformFromString
} from 'snjs'; } from 'snjs';
import angular from 'angular';
import { getPlatformString } from '@/utils'; import { getPlatformString } from '@/utils';
import { AlertService } from '@/services/alertService'; import { AlertService } from '@/services/alertService';
import { WebDeviceInterface } from '@/web_device_interface'; import { WebDeviceInterface } from '@/web_device_interface';
export class Application extends SNApplication { export class WebApplication extends SNApplication {
/* @ngInject */ /* @ngInject */
constructor($timeout) { constructor($compile, $timeout, scope, onDeinit) {
const deviceInterface = new WebDeviceInterface({ timeout: $timeout }); const deviceInterface = new WebDeviceInterface({ timeout: $timeout });
super({ super({
environment: Environments.Web, environment: Environments.Web,
@@ -25,6 +26,189 @@ export class Application extends SNApplication {
} }
] ]
}); });
this.$compile = $compile;
this.scope = scope;
this.onDeinit = onDeinit;
deviceInterface.setApplication(this); deviceInterface.setApplication(this);
} }
/** @override */
deinit() {
for (const key of Object.keys(this.webServices)) {
const service = this.webServices[key];
if(service.deinit) {
service.deinit();
}
service.application = null;
delete this.webServices[key];
}
this.webServices = {};
this.onDeinit(this);
this.onDeinit = null;
this.$compile = null;
this.$timeout = null;
this.scope.application = null;
this.scope.$destroy();
this.scope = null;
super.deinit();
}
/**
* @access public
* @param {object} services
*/
setWebServices(services) {
this.webServices = services;
}
/** @access public */
getAppState() {
return this.webServices.appState;
}
/** @access public */
getDesktopService() {
return this.webServices.desktopService;
}
/** @access public */
getLockService() {
return this.webServices.lockService;
}
/** @access public */
getArchiveService() {
return this.webServices.archiveService;
}
/** @access public */
getNativeExtService() {
return this.webServices.nativeExtService;
}
/** @access public */
getStatusService() {
return this.webServices.statusService;
}
/** @access public */
getThemeService() {
return this.webServices.themeService;
}
/** @access public */
getPrefsService() {
return this.webServices.prefsService;
}
/** @access public */
getKeyboardService() {
return this.webServices.keyboardService;
}
async checkForSecurityUpdate() {
return this.protocolUpgradeAvailable();
}
presentPasswordWizard(type) {
const scope = this.scope.$new(true);
scope.type = type;
scope.application = this;
const el = this.$compile("<password-wizard application='application' type='type'></password-wizard>")(scope);
angular.element(document.body).append(el);
}
promptForChallenge(challenge, orchestrator) {
const scope = this.scope.$new(true);
scope.challenge = challenge;
scope.orchestrator = orchestrator;
scope.application = this;
const el = this.$compile(
"<challenge-modal " +
"class='sk-modal' application='application' challenge='challenge' orchestrator='orchestrator'>" +
"</challenge-modal>"
)(scope);
angular.element(document.body).append(el);
}
async performProtocolUpgrade() {
const errors = await this.upgradeProtocolVersion();
if (errors.length === 0) {
this.alertService.alert({
text: "Success! Your encryption version has been upgraded." +
" You'll be asked to enter your credentials again on other devices you're signed into."
});
} else {
this.alertService.alert({
text: "Unable to upgrade encryption version. Please try again."
});
}
}
async presentPrivilegesModal(action, onSuccess, onCancel) {
if (this.authenticationInProgress()) {
onCancel && onCancel();
return;
}
const customSuccess = async () => {
onSuccess && await onSuccess();
this.currentAuthenticationElement = null;
};
const customCancel = async () => {
onCancel && await onCancel();
this.currentAuthenticationElement = null;
};
const scope = this.scope.$new(true);
scope.action = action;
scope.onSuccess = customSuccess;
scope.onCancel = customCancel;
scope.application = this;
const el = this.$compile(`
<privileges-auth-modal application='application' action='action' on-success='onSuccess'
on-cancel='onCancel' class='sk-modal'></privileges-auth-modal>
`)(scope);
angular.element(document.body).append(el);
this.currentAuthenticationElement = el;
}
presentPrivilegesManagementModal() {
const scope = this.scope.$new(true);
scope.application = this;
const el = this.$compile("<privileges-management-modal application='application' class='sk-modal'></privileges-management-modal>")(scope);
angular.element(document.body).append(el);
}
authenticationInProgress() {
return this.currentAuthenticationElement != null;
}
presentPasswordModal(callback) {
const scope = this.scope.$new(true);
scope.type = "password";
scope.title = "Decryption Assistance";
scope.message = `Unable to decrypt this item with your current keys.
Please enter your account password at the time of this revision.`;
scope.callback = callback;
const el = this.$compile(
`<input-modal type='type' message='message'
title='title' callback='callback'></input-modal>`
)(scope);
angular.element(document.body).append(el);
}
presentRevisionPreviewModal(uuid, content) {
const scope = this.scope.$new(true);
scope.uuid = uuid;
scope.content = content;
scope.application = this;
const el = this.$compile(
`<revision-preview-modal application='application' uuid='uuid' content='content'
class='sk-modal'></revision-preview-modal>`
)(scope);
angular.element(document.body).append(el);
}
} }

View File

@@ -0,0 +1,125 @@
import { WebApplication } from './application';
import { removeFromArray } from 'snjs';
import {
ArchiveManager,
DesktopManager,
KeyboardManager,
LockManager,
NativeExtManager,
PreferencesManager,
StatusManager,
ThemeManager,
AppState
} from './services';
export class ApplicationManager {
/* @ngInject */
constructor($compile, $rootScope, $timeout) {
this.$compile = $compile;
this.$timeout = $timeout;
this.$rootScope = $rootScope;
this.applications = [];
this.changeObservers = [];
this.onApplicationDeinit = this.onApplicationDeinit.bind(this);
this.createDefaultApplication();
}
/** @access private */
createDefaultApplication() {
this.activeApplication = this.createNewApplication();
this.applications.push(this.activeApplication);
this.notifyObserversOfAppChange();
}
/** @callback */
onApplicationDeinit(application) {
removeFromArray(this.applications, application);
if(this.activeApplication === application) {
this.activeApplication = null;
}
if (this.applications.length === 0) {
this.createDefaultApplication();
}
this.notifyObserversOfAppChange();
}
/** @access private */
createNewApplication() {
const scope = this.$rootScope.$new(true);
const application = new WebApplication(
this.$compile,
this.$timeout,
scope,
this.onApplicationDeinit
);
const appState = new AppState(
this.$rootScope,
this.$timeout,
application
);
const archiveService = new ArchiveManager(
application
);
const desktopService = new DesktopManager(
this.$rootScope,
this.$timeout,
application
);
const keyboardService = new KeyboardManager();
const lockService = new LockManager(
application
);
const nativeExtService = new NativeExtManager(
application
);
const prefsService = new PreferencesManager(
application
);
const statusService = new StatusManager();
const themeService = new ThemeManager(
application,
);
application.setWebServices({
appState,
archiveService,
desktopService,
keyboardService,
lockService,
nativeExtService,
prefsService,
statusService,
themeService
});
return application;
}
get application() {
return this.activeApplication;
}
/** @access public */
getApplications() {
return this.applications.slice();
}
/**
* Notifies observer when the active application has changed.
* Any application which is no longer active is destroyed, and
* must be removed from the interface.
* @access public
* @param {function} callback
*/
addApplicationChangeObserver(callback) {
this.changeObservers.push(callback);
if (this.application) {
callback();
}
}
notifyObserversOfAppChange() {
for (const observer of this.changeObservers) {
observer();
}
}
}

View File

@@ -1,34 +1,38 @@
import { ApplicationService } from 'snjs'; import { ApplicationEvents } from 'snjs';
export class PureCtrl extends ApplicationService { export class PureCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor($timeout) {
$scope, if(!$timeout) {
$timeout, throw Error('$timeout must not be null');
application,
appState
) {
if (!$scope || !$timeout || !application || !appState) {
throw 'Invalid PureCtrl construction.';
} }
super(application);
this.$scope = $scope;
this.$timeout = $timeout; this.$timeout = $timeout;
this.appState = appState;
this.props = {}; this.props = {};
this.state = {}; this.state = {};
/* Allow caller constructor to finish setting instance variables */ /* Allow caller constructor to finish setting instance variables */
setImmediate(() => { setImmediate(() => {
this.state = this.getInitialState(); this.state = this.getInitialState();
}); });
$scope.$on('$destroy', () => { }
this.unsubState();
this.deinit(); $onInit() {
}); this.addAppEventObserver();
this.addAppStateObserver();
} }
$onInit() { deinit() {
this.addAppStateObserver(); this.unsubApp();
this.unsubState();
this.unsubApp = null;
this.unsubState = null;
this.application = null;
if (this.stateTimeout) {
this.$timeout.cancel(this.stateTimeout);
}
}
$onDestroy() {
this.deinit();
} }
/** @private */ /** @private */
@@ -43,8 +47,11 @@ export class PureCtrl extends ApplicationService {
} }
async setState(state) { async setState(state) {
if(!this.$timeout) {
return;
}
return new Promise((resolve) => { return new Promise((resolve) => {
this.$timeout(() => { this.stateTimeout = this.$timeout(() => {
this.state = Object.freeze(Object.assign({}, this.state, state)); this.state = Object.freeze(Object.assign({}, this.state, state));
resolve(); resolve();
}); });
@@ -59,7 +66,7 @@ export class PureCtrl extends ApplicationService {
} }
addAppStateObserver() { addAppStateObserver() {
this.unsubState = this.appState.addObserver((eventName, data) => { this.unsubState = this.application.getAppState().addObserver((eventName, data) => {
this.onAppStateEvent(eventName, data); this.onAppStateEvent(eventName, data);
}); });
} }
@@ -68,9 +75,46 @@ export class PureCtrl extends ApplicationService {
/** Optional override */ /** Optional override */
} }
addAppEventObserver() {
if (this.application.isStarted()) {
this.onAppStart();
}
if (this.application.isLaunched()) {
this.onAppLaunch();
}
this.unsubApp = this.application.addEventObserver(async (eventName) => {
this.onAppEvent(eventName);
if (eventName === ApplicationEvents.Started) {
await this.onAppStart();
} else if (eventName === ApplicationEvents.Launched) {
await this.onAppLaunch();
} else if (eventName === ApplicationEvents.CompletedSync) {
this.onAppSync();
} else if (eventName === ApplicationEvents.KeyStatusChanged) {
this.onAppKeyChange();
}
});
}
onAppEvent(eventName) {
/** Optional override */
}
/** @override */ /** @override */
async onAppStart() { async onAppStart() {
await this.resetState(); await this.resetState();
return super.onAppStart();
} }
async onAppLaunch() {
/** Optional override */
}
async onAppKeyChange() {
/** Optional override */
}
onAppSync() {
/** Optional override */
}
} }

View File

@@ -0,0 +1,281 @@
import { getPlatformString } from '@/utils';
import template from '%/application-view.pug';
import { AppStateEvents } from '@/services/state';
import { ApplicationEvents } from 'snjs';
import angular from 'angular';
import {
PANEL_NAME_NOTES,
PANEL_NAME_TAGS
} from '@/controllers/constants';
import {
STRING_SESSION_EXPIRED,
STRING_DEFAULT_FILE_ERROR
} from '@/strings';
import { PureCtrl } from './abstract/pure_ctrl';
class ApplicationViewCtrl extends PureCtrl {
/* @ngInject */
constructor(
$compile,
$location,
$rootScope,
$timeout
) {
super($timeout);
this.$location = $location;
this.$rootScope = $rootScope;
this.$compile = $compile;
this.platformString = getPlatformString();
this.state = { appClass: '' };
this.onDragDrop = this.onDragDrop.bind(this);
this.onDragOver = this.onDragOver.bind(this);
this.openModalComponent = this.openModalComponent.bind(this);
this.presentPermissionsDialog = this.presentPermissionsDialog.bind(this);
this.addDragDropHandlers();
}
deinit() {
this.$location = null;
this.$rootScope = null;
this.$compile = null;
this.application = null;
this.lockScreenPuppet = null;
window.removeEventListener('dragover', this.onDragOver, true);
window.removeEventListener('drop', this.onDragDrop, true);
this.onDragDrop = null;
this.onDragOver = null;
this.openModalComponent = null;
this.presentPermissionsDialog = null;
super.deinit();
}
$onInit() {
super.$onInit();
this.loadApplication();
}
async loadApplication() {
await this.application.prepareForLaunch({
callbacks: {
receiveChallenge: async (challenge, orchestrator) => {
this.application.promptForChallenge(challenge, orchestrator);
}
}
});
await this.application.launch();
}
onAppStart() {
super.onAppStart();
this.overrideComponentManagerFunctions();
this.application.componentManager.setDesktopManager(this.application.getDesktopService());
this.setState({
ready: true,
needsUnlock: this.application.hasPasscode()
});
}
onAppLaunch() {
super.onAppLaunch();
this.setState({ needsUnlock: false });
this.handleAutoSignInFromParams();
}
onUpdateAvailable() {
this.$rootScope.$broadcast('new-update-available');
};
/** @override */
async onAppEvent(eventName) {
super.onAppEvent(eventName);
if (eventName === ApplicationEvents.LocalDataIncrementalLoad) {
this.updateLocalDataStatus();
} else if (eventName === ApplicationEvents.SyncStatusChanged) {
this.updateSyncStatus();
} else if (eventName === ApplicationEvents.LocalDataLoaded) {
this.updateLocalDataStatus();
} else if (eventName === ApplicationEvents.WillSync) {
if (!this.completedInitialSync) {
this.syncStatus = this.application.getStatusService().replaceStatusWithString(
this.syncStatus,
"Syncing..."
);
}
} else if (eventName === ApplicationEvents.CompletedSync) {
if (!this.completedInitialSync) {
this.syncStatus = this.application.getStatusService().removeStatus(this.syncStatus);
this.completedInitialSync = true;
}
} else if (eventName === ApplicationEvents.InvalidSyncSession) {
this.showInvalidSessionAlert();
}
}
/** @override */
async onAppStateEvent(eventName, data) {
if (eventName === AppStateEvents.PanelResized) {
if (data.panel === PANEL_NAME_NOTES) {
this.notesCollapsed = data.collapsed;
}
if (data.panel === PANEL_NAME_TAGS) {
this.tagsCollapsed = data.collapsed;
}
let appClass = "";
if (this.notesCollapsed) { appClass += "collapsed-notes"; }
if (this.tagsCollapsed) { appClass += " collapsed-tags"; }
this.setState({ appClass });
} else if (eventName === AppStateEvents.WindowDidFocus) {
if (!(await this.application.isLocked())) {
this.application.sync();
}
}
}
updateLocalDataStatus() {
const syncStatus = this.application.getSyncStatus();
const stats = syncStatus.getStats();
const encryption = this.application.isEncryptionAvailable();
if (stats.localDataDone) {
this.syncStatus = this.application.getStatusService().removeStatus(this.syncStatus);
return;
}
const notesString = `${stats.localDataCurrent}/${stats.localDataTotal} items...`;
const loadingStatus = encryption
? `Decrypting ${notesString}`
: `Loading ${notesString}`;
this.syncStatus = this.application.getStatusService().replaceStatusWithString(
this.syncStatus,
loadingStatus
);
}
updateSyncStatus() {
const syncStatus = this.application.getSyncStatus();
const stats = syncStatus.getStats();
if (stats.downloadCount > 20) {
const text = `Downloading ${stats.downloadCount} items. Keep app open.`;
this.syncStatus = this.application.getStatusService().replaceStatusWithString(
this.syncStatus,
text
);
this.showingDownloadStatus = true;
} else if (this.showingDownloadStatus) {
this.showingDownloadStatus = false;
const text = "Download Complete.";
this.syncStatus = this.application.getStatusService().replaceStatusWithString(
this.syncStatus,
text
);
setTimeout(() => {
this.syncStatus = this.application.getStatusService().removeStatus(this.syncStatus);
}, 2000);
} else if (stats.uploadTotalCount > 20) {
this.uploadSyncStatus = this.application.getStatusService().replaceStatusWithString(
this.uploadSyncStatus,
`Syncing ${stats.uploadCompletionCount}/${stats.uploadTotalCount} items...`
);
} else if (this.uploadSyncStatus) {
this.uploadSyncStatus = this.application.getStatusService().removeStatus(
this.uploadSyncStatus
);
}
}
openModalComponent(component) {
const scope = this.$rootScope.$new(true);
scope.component = component;
const el = this.$compile("<component-modal component='component' class='sk-modal'></component-modal>")(scope);
angular.element(document.body).append(el);
}
presentPermissionsDialog(dialog) {
const scope = this.$rootScope.$new(true);
scope.permissionsString = dialog.permissionsString;
scope.component = dialog.component;
scope.callback = dialog.callback;
const el = this.$compile("<permissions-modal component='component' permissions-string='permissionsString' callback='callback' class='sk-modal'></permissions-modal>")(scope);
angular.element(document.body).append(el);
}
overrideComponentManagerFunctions() {
this.application.componentManager.openModalComponent = this.openModalComponent;
this.application.componentManager.presentPermissionsDialog = this.presentPermissionsDialog;
}
showInvalidSessionAlert() {
/** Don't show repeatedly; at most 30 seconds in between */
const SHOW_INTERVAL = 30;
const lastShownSeconds = (new Date() - this.lastShownDate) / 1000;
if (!this.lastShownDate || lastShownSeconds > SHOW_INTERVAL) {
this.lastShownDate = new Date();
setTimeout(() => {
this.alertService.alert({
text: STRING_SESSION_EXPIRED
});
}, 500);
}
}
addDragDropHandlers() {
/**
* Disable dragging and dropping of files (but allow text) into main SN interface.
* both 'dragover' and 'drop' are required to prevent dropping of files.
* This will not prevent extensions from receiving drop events.
*/
window.addEventListener('dragover', this.onDragOver, true);
window.addEventListener('drop', this.onDragDrop, true);
}
onDragOver(event) {
if (event.dataTransfer.files.length > 0) {
event.preventDefault();
}
}
onDragDrop(event) {
if (event.dataTransfer.files.length > 0) {
event.preventDefault();
this.application.alertService.alert({
text: STRING_DEFAULT_FILE_ERROR
});
}
}
async handleAutoSignInFromParams() {
const params = this.$location.search();
const server = params.server;
const email = params.email;
const password = params.pw;
if (!server || !email || !password) return;
const user = this.application.getUser();
if (user) {
if (user.email === email && await this.application.getHost() === server) {
/** Already signed in, return */
return;
} else {
/** Sign out */
await this.application.signOut();
await this.application.restart();
}
}
await this.application.setHost(server);
this.application.signIn({
email: email,
password: password,
});
}
}
export class ApplicationView {
constructor() {
this.template = template;
this.controller = ApplicationViewCtrl;
this.replace = true;
this.controllerAs = 'self';
this.bindToController = {
application: '='
};
}
}

View File

@@ -10,7 +10,7 @@ import { isDesktopApplication } from '@/utils';
import { KeyboardModifiers, KeyboardKeys } from '@/services/keyboardManager'; import { KeyboardModifiers, KeyboardKeys } from '@/services/keyboardManager';
import template from '%/editor.pug'; import template from '%/editor.pug';
import { PureCtrl } from '@Controllers'; import { PureCtrl } from '@Controllers';
import { AppStateEvents, EventSources } from '@/state'; import { AppStateEvents, EventSources } from '@/services/state';
import { import {
STRING_DELETED_NOTE, STRING_DELETED_NOTE,
STRING_INVALID_NOTE, STRING_INVALID_NOTE,
@@ -48,35 +48,34 @@ const Fonts = {
class EditorCtrl extends PureCtrl { class EditorCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor($timeout) {
$scope, super($timeout);
$timeout,
$rootScope,
application,
appState,
desktopManager,
keyboardManager,
preferencesManager,
) {
super($scope, $timeout, application, appState);
this.$rootScope = $rootScope;
this.desktopManager = desktopManager;
this.keyboardManager = keyboardManager;
this.preferencesManager = preferencesManager;
this.leftPanelPuppet = { this.leftPanelPuppet = {
onReady: () => this.reloadPreferences() onReady: () => this.reloadPreferences()
}; };
this.rightPanelPuppet = { this.rightPanelPuppet = {
onReady: () => this.reloadPreferences() onReady: () => this.reloadPreferences()
}; };
this.addSyncStatusObserver();
this.registerKeyboardShortcuts();
/** Used by .pug template */ /** Used by .pug template */
this.prefKeyMonospace = PrefKeys.EditorMonospaceEnabled; this.prefKeyMonospace = PrefKeys.EditorMonospaceEnabled;
this.prefKeySpellcheck = PrefKeys.EditorSpellcheck; this.prefKeySpellcheck = PrefKeys.EditorSpellcheck;
this.prefKeyMarginResizers = PrefKeys.EditorResizersEnabled; this.prefKeyMarginResizers = PrefKeys.EditorResizersEnabled;
} }
deinit() {
this.removeTabObserver();
this.leftPanelPuppet = null;
this.rightPanelPuppet = null;
this.onEditorLoad = null;
super.deinit();
}
$onInit() {
super.$onInit();
this.addSyncStatusObserver();
this.registerKeyboardShortcuts();
}
/** @override */ /** @override */
getInitialState() { getInitialState() {
return { return {
@@ -100,7 +99,7 @@ class EditorCtrl extends PureCtrl {
onAppStateEvent(eventName, data) { onAppStateEvent(eventName, data) {
if (eventName === AppStateEvents.NoteChanged) { if (eventName === AppStateEvents.NoteChanged) {
this.handleNoteSelectionChange( this.handleNoteSelectionChange(
this.appState.getSelectedNote(), this.application.getAppState().getSelectedNote(),
data.previousNote data.previousNote
); );
} else if (eventName === AppStateEvents.PreferencesChanged) { } else if (eventName === AppStateEvents.PreferencesChanged) {
@@ -209,7 +208,7 @@ class EditorCtrl extends PureCtrl {
async handleNoteSelectionChange(note, previousNote) { async handleNoteSelectionChange(note, previousNote) {
this.setState({ this.setState({
note: this.appState.getSelectedNote(), note: this.application.getAppState().getSelectedNote(),
showExtensions: false, showExtensions: false,
showOptionsMenu: false, showOptionsMenu: false,
altKeyDown: false, altKeyDown: false,
@@ -504,7 +503,7 @@ class EditorCtrl extends PureCtrl {
} }
onContentFocus() { onContentFocus() {
this.appState.editorDidFocus(this.lastEditorFocusEventSource); this.application.getAppState().editorDidFocus(this.lastEditorFocusEventSource);
this.lastEditorFocusEventSource = null; this.lastEditorFocusEventSource = null;
} }
@@ -552,7 +551,7 @@ class EditorCtrl extends PureCtrl {
dontUpdatePreviews: true dontUpdatePreviews: true
}); });
} }
this.appState.setSelectedNote(null); this.application.getAppState().setSelectedNote(null);
this.setMenuState('showOptionsMenu', false); this.setMenuState('showOptionsMenu', false);
} }
}); });
@@ -561,7 +560,7 @@ class EditorCtrl extends PureCtrl {
ProtectedActions.DeleteNote ProtectedActions.DeleteNote
); );
if (requiresPrivilege) { if (requiresPrivilege) {
this.godService.presentPrivilegesModal( this.application.presentPrivilegesModal(
ProtectedActions.DeleteNote, ProtectedActions.DeleteNote,
() => { () => {
run(); run();
@@ -592,7 +591,7 @@ class EditorCtrl extends PureCtrl {
bypassDebouncer: true, bypassDebouncer: true,
dontUpdatePreviews: true dontUpdatePreviews: true
}); });
this.appState.setSelectedNote(null); this.application.getAppState().setSelectedNote(null);
} }
deleteNotePermanantely() { deleteNotePermanantely() {
@@ -649,7 +648,7 @@ class EditorCtrl extends PureCtrl {
ProtectedActions.ViewProtectedNotes ProtectedActions.ViewProtectedNotes
).then((configured) => { ).then((configured) => {
if (!configured) { if (!configured) {
this.godService.presentPrivilegesManagementModal(); this.application.presentPrivilegesManagementModal();
} }
}); });
} }
@@ -743,13 +742,13 @@ class EditorCtrl extends PureCtrl {
onPanelResizeFinish = (width, left, isMaxWidth) => { onPanelResizeFinish = (width, left, isMaxWidth) => {
if (isMaxWidth) { if (isMaxWidth) {
this.preferencesManager.setUserPrefValue( this.application.getPrefsService().setUserPrefValue(
PrefKeys.EditorWidth, PrefKeys.EditorWidth,
null null
); );
} else { } else {
if (width !== undefined && width !== null) { if (width !== undefined && width !== null) {
this.preferencesManager.setUserPrefValue( this.application.getPrefsService().setUserPrefValue(
PrefKeys.EditorWidth, PrefKeys.EditorWidth,
width width
); );
@@ -757,25 +756,25 @@ class EditorCtrl extends PureCtrl {
} }
} }
if (left !== undefined && left !== null) { if (left !== undefined && left !== null) {
this.preferencesManager.setUserPrefValue( this.application.getPrefsService().setUserPrefValue(
PrefKeys.EditorLeft, PrefKeys.EditorLeft,
left left
); );
this.rightPanelPuppet.setLeft(left); this.rightPanelPuppet.setLeft(left);
} }
this.preferencesManager.syncUserPreferences(); this.application.getPrefsService().syncUserPreferences();
} }
reloadPreferences() { reloadPreferences() {
const monospaceEnabled = this.preferencesManager.getValue( const monospaceEnabled = this.application.getPrefsService().getValue(
PrefKeys.EditorMonospaceEnabled, PrefKeys.EditorMonospaceEnabled,
true true
); );
const spellcheck = this.preferencesManager.getValue( const spellcheck = this.application.getPrefsService().getValue(
PrefKeys.EditorSpellcheck, PrefKeys.EditorSpellcheck,
true true
); );
const marginResizersEnabled = this.preferencesManager.getValue( const marginResizersEnabled = this.application.getPrefsService().getValue(
PrefKeys.EditorResizersEnabled, PrefKeys.EditorResizersEnabled,
true true
); );
@@ -797,7 +796,7 @@ class EditorCtrl extends PureCtrl {
this.leftPanelPuppet.ready && this.leftPanelPuppet.ready &&
this.rightPanelPuppet.ready this.rightPanelPuppet.ready
) { ) {
const width = this.preferencesManager.getValue( const width = this.application.getPrefsService().getValue(
PrefKeys.EditorWidth, PrefKeys.EditorWidth,
null null
); );
@@ -805,7 +804,7 @@ class EditorCtrl extends PureCtrl {
this.leftPanelPuppet.setWidth(width); this.leftPanelPuppet.setWidth(width);
this.rightPanelPuppet.setWidth(width); this.rightPanelPuppet.setWidth(width);
} }
const left = this.preferencesManager.getValue( const left = this.application.getPrefsService().getValue(
PrefKeys.EditorLeft, PrefKeys.EditorLeft,
null null
); );
@@ -836,7 +835,7 @@ class EditorCtrl extends PureCtrl {
async toggleKey(key) { async toggleKey(key) {
this[key] = !this[key]; this[key] = !this[key];
this.preferencesManager.setUserPrefValue( this.application.getPrefsService().setUserPrefValue(
key, key,
this[key], this[key],
true true
@@ -863,7 +862,7 @@ class EditorCtrl extends PureCtrl {
/** @components */ /** @components */
onEditorLoad = (editor) => { onEditorLoad = (editor) => {
this.desktopManager.redoSearch(); this.application.getDesktopService().redoSearch();
} }
registerComponentHandler() { registerComponentHandler() {
@@ -1047,7 +1046,7 @@ class EditorCtrl extends PureCtrl {
} }
registerKeyboardShortcuts() { registerKeyboardShortcuts() {
this.altKeyObserver = this.keyboardManager.addKeyObserver({ this.altKeyObserver = this.application.getKeyboardService().addKeyObserver({
modifiers: [ modifiers: [
KeyboardModifiers.Alt KeyboardModifiers.Alt
], ],
@@ -1063,7 +1062,7 @@ class EditorCtrl extends PureCtrl {
} }
}); });
this.trashKeyObserver = this.keyboardManager.addKeyObserver({ this.trashKeyObserver = this.application.getKeyboardService().addKeyObserver({
key: KeyboardKeys.Backspace, key: KeyboardKeys.Backspace,
notElementIds: [ notElementIds: [
ElementIds.NoteTextEditor, ElementIds.NoteTextEditor,
@@ -1075,7 +1074,7 @@ class EditorCtrl extends PureCtrl {
}, },
}); });
this.deleteKeyObserver = this.keyboardManager.addKeyObserver({ this.deleteKeyObserver = this.application.getKeyboardService().addKeyObserver({
key: KeyboardKeys.Backspace, key: KeyboardKeys.Backspace,
modifiers: [ modifiers: [
KeyboardModifiers.Meta, KeyboardModifiers.Meta,
@@ -1090,10 +1089,9 @@ class EditorCtrl extends PureCtrl {
} }
onSystemEditorLoad() { onSystemEditorLoad() {
if (this.loadedTabListener) { if (this.tabObserver) {
return; return;
} }
this.loadedTabListener = true;
/** /**
* Insert 4 spaces when a tab key is pressed, * Insert 4 spaces when a tab key is pressed,
* only used when inside of the text editor. * only used when inside of the text editor.
@@ -1103,7 +1101,7 @@ class EditorCtrl extends PureCtrl {
const editor = document.getElementById( const editor = document.getElementById(
ElementIds.NoteTextEditor ElementIds.NoteTextEditor
); );
this.tabObserver = this.keyboardManager.addKeyObserver({ this.tabObserver = this.application.getKeyboardService().addKeyObserver({
element: editor, element: editor,
key: KeyboardKeys.Tab, key: KeyboardKeys.Tab,
onKeyDown: (event) => { onKeyDown: (event) => {
@@ -1144,19 +1142,29 @@ class EditorCtrl extends PureCtrl {
* Handles when the editor is destroyed, * Handles when the editor is destroyed,
* (and not when our controller is destroyed.) * (and not when our controller is destroyed.)
*/ */
angular.element(editor).on('$destroy', () => { angular.element(editor).one('$destroy', () => {
if (this.tabObserver) { this.removeTabObserver();
this.keyboardManager.removeKeyObserver(this.tabObserver);
this.loadedTabListener = false;
}
}); });
}; }
removeTabObserver() {
if (!this.application) {
return;
}
const keyboardService = this.application.getKeyboardService();
if (this.tabObserver && keyboardService) {
keyboardService.removeKeyObserver(this.tabObserver);
this.tabObserver = null;
}
}
} }
export class EditorPanel { export class EditorPanel {
constructor() { constructor() {
this.restrict = 'E'; this.restrict = 'E';
this.scope = {}; this.scope = {
application: '='
};
this.template = template; this.template = template;
this.replace = true; this.replace = true;
this.controller = EditorCtrl; this.controller = EditorCtrl;

View File

@@ -6,7 +6,7 @@ import {
ContentTypes ContentTypes
} from 'snjs'; } from 'snjs';
import template from '%/footer.pug'; import template from '%/footer.pug';
import { AppStateEvents, EventSources } from '@/state'; import { AppStateEvents, EventSources } from '@/services/state';
import { import {
STRING_GENERIC_SYNC_ERROR, STRING_GENERIC_SYNC_ERROR,
STRING_NEW_UPDATE_READY STRING_NEW_UPDATE_READY
@@ -17,25 +17,32 @@ class FooterCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$scope,
$rootScope, $rootScope,
$timeout, $timeout,
application,
appState,
nativeExtManager,
statusManager,
godService
) { ) {
super($scope, $timeout, application, appState); super($timeout);
this.$rootScope = $rootScope; this.$rootScope = $rootScope;
this.nativeExtManager = nativeExtManager;
this.statusManager = statusManager;
this.godService = godService;
this.rooms = []; this.rooms = [];
this.themesWithIcons = []; this.themesWithIcons = [];
this.showSyncResolution = false; this.showSyncResolution = false;
this.addRootScopeListeners(); this.addRootScopeListeners();
this.statusManager.addStatusObserver((string) => { }
deinit() {
this.rooms.length = 0;
this.themesWithIcons.length = 0;
this.rootScopeListener1();
this.rootScopeListener2();
this.rootScopeListener1 = null;
this.rootScopeListener2 = null;
this.closeAccountMenu = null;
this.toggleSyncResolutionMenu = null;
super.deinit();
}
$onInit() {
super.$onInit();
this.application.getStatusService().addStatusObserver((string) => {
this.$timeout(() => { this.$timeout(() => {
this.arbitraryStatusMessage = string; this.arbitraryStatusMessage = string;
}); });
@@ -49,7 +56,7 @@ class FooterCtrl extends PureCtrl {
} }
reloadUpgradeStatus() { reloadUpgradeStatus() {
this.godService.checkForSecurityUpdate().then((available) => { this.application.checkForSecurityUpdate().then((available) => {
this.setState({ this.setState({
dataUpgradeAvailable: available dataUpgradeAvailable: available
}); });
@@ -72,10 +79,10 @@ class FooterCtrl extends PureCtrl {
} }
addRootScopeListeners() { addRootScopeListeners() {
this.$rootScope.$on("reload-ext-data", () => { this.rootScopeListener1 = this.$rootScope.$on("reload-ext-data", () => {
this.reloadExtendedData(); this.reloadExtendedData();
}); });
this.$rootScope.$on("new-update-available", () => { this.rootScopeListener2 = this.$rootScope.$on("new-update-available", () => {
this.$timeout(() => { this.$timeout(() => {
this.onNewUpdateAvailable(); this.onNewUpdateAvailable();
}); });
@@ -90,23 +97,23 @@ class FooterCtrl extends PureCtrl {
this.closeAccountMenu(); this.closeAccountMenu();
} }
} else if (eventName === AppStateEvents.BeganBackupDownload) { } else if (eventName === AppStateEvents.BeganBackupDownload) {
this.backupStatus = this.statusManager.addStatusFromString( this.backupStatus = this.application.getStatusService().addStatusFromString(
"Saving local backup..." "Saving local backup..."
); );
} else if (eventName === AppStateEvents.EndedBackupDownload) { } else if (eventName === AppStateEvents.EndedBackupDownload) {
if (data.success) { if (data.success) {
this.backupStatus = this.statusManager.replaceStatusWithString( this.backupStatus = this.application.getStatusService().replaceStatusWithString(
this.backupStatus, this.backupStatus,
"Successfully saved backup." "Successfully saved backup."
); );
} else { } else {
this.backupStatus = this.statusManager.replaceStatusWithString( this.backupStatus = this.application.getStatusService().replaceStatusWithString(
this.backupStatus, this.backupStatus,
"Unable to save local backup." "Unable to save local backup."
); );
} }
this.$timeout(() => { this.$timeout(() => {
this.backupStatus = this.statusManager.removeStatus(this.backupStatus); this.backupStatus = this.application.getStatusService().removeStatus(this.backupStatus);
}, 2000); }, 2000);
} }
} }
@@ -205,7 +212,7 @@ class FooterCtrl extends PureCtrl {
* then closing it after a short delay. * then closing it after a short delay.
*/ */
const extWindow = this.rooms.find((room) => { const extWindow = this.rooms.find((room) => {
return room.package_info.identifier === this.nativeExtManager.extManagerId; return room.package_info.identifier === this.application.getNativeExtService().extManagerId;
}); });
if (!extWindow) { if (!extWindow) {
this.queueExtReload = true; this.queueExtReload = true;
@@ -225,7 +232,7 @@ class FooterCtrl extends PureCtrl {
} }
openSecurityUpdate() { openSecurityUpdate() {
this.godService.performProtocolUpgrade(); this.application.performProtocolUpgrade();
} }
findErrors() { findErrors() {
@@ -347,7 +354,7 @@ class FooterCtrl extends PureCtrl {
ProtectedActions.ManageExtensions ProtectedActions.ManageExtensions
); );
if (requiresPrivilege) { if (requiresPrivilege) {
this.godService.presentPrivilegesModal( this.application.presentPrivilegesModal(
ProtectedActions.ManageExtensions, ProtectedActions.ManageExtensions,
run run
); );
@@ -360,7 +367,7 @@ class FooterCtrl extends PureCtrl {
} }
clickOutsideAccountMenu() { clickOutsideAccountMenu() {
if (this.godService.authenticationInProgress()) { if (this.application && this.application.authenticationInProgress()) {
return; return;
} }
this.showAccountMenu = false; this.showAccountMenu = false;
@@ -370,11 +377,12 @@ class FooterCtrl extends PureCtrl {
export class Footer { export class Footer {
constructor() { constructor() {
this.restrict = 'E'; this.restrict = 'E';
this.scope = {};
this.template = template; this.template = template;
this.controller = FooterCtrl; this.controller = FooterCtrl;
this.replace = true; this.replace = true;
this.controllerAs = 'ctrl'; this.controllerAs = 'ctrl';
this.bindToController = true; this.bindToController = {
application: '='
};
} }
} }

View File

@@ -4,4 +4,4 @@ export { Footer } from './footer';
export { NotesPanel } from './notes/notes'; export { NotesPanel } from './notes/notes';
export { TagsPanel } from './tags'; export { TagsPanel } from './tags';
export { Root } from './root'; export { Root } from './root';
export { LockScreen } from './lockScreen'; export { ApplicationView } from './applicationView';

View File

@@ -1,5 +1,5 @@
import template from '%/lock-screen.pug'; import template from '%/lock-screen.pug';
import { AppStateEvents } from '@/state'; import { AppStateEvents } from '@/services/state';
import { PureCtrl } from './abstract/pure_ctrl'; import { PureCtrl } from './abstract/pure_ctrl';
const ELEMENT_ID_PASSCODE_INPUT = 'passcode-input'; const ELEMENT_ID_PASSCODE_INPUT = 'passcode-input';
@@ -7,12 +7,9 @@ const ELEMENT_ID_PASSCODE_INPUT = 'passcode-input';
class LockScreenCtrl extends PureCtrl { class LockScreenCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$scope,
$timeout, $timeout,
application,
appState
) { ) {
super($scope, $timeout, application, appState); super($timeout);
this.formData = {}; this.formData = {};
} }

View File

@@ -2,7 +2,7 @@ import angular from 'angular';
import template from '%/notes.pug'; import template from '%/notes.pug';
import { ApplicationEvents, ContentTypes, removeFromArray } from 'snjs'; import { ApplicationEvents, ContentTypes, removeFromArray } from 'snjs';
import { PureCtrl } from '@Controllers'; import { PureCtrl } from '@Controllers';
import { AppStateEvents } from '@/state'; import { AppStateEvents } from '@/services/state';
import { KeyboardModifiers, KeyboardKeys } from '@/services/keyboardManager'; import { KeyboardModifiers, KeyboardKeys } from '@/services/keyboardManager';
import { import {
PrefKeys PrefKeys
@@ -31,24 +31,10 @@ class NotesCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$scope,
$timeout, $timeout,
$rootScope,
application,
appState,
desktopManager,
keyboardManager,
preferencesManager,
) { ) {
super($scope, $timeout, application, appState); super($timeout);
this.$rootScope = $rootScope;
this.application = application;
this.appState = appState;
this.desktopManager = desktopManager;
this.keyboardManager = keyboardManager;
this.preferencesManager = preferencesManager;
this.resetPagination(); this.resetPagination();
this.registerKeyboardShortcuts();
} }
$onInit() { $onInit() {
@@ -59,11 +45,24 @@ class NotesCtrl extends PureCtrl {
this.panelPuppet = { this.panelPuppet = {
onReady: () => this.reloadPreferences() onReady: () => this.reloadPreferences()
}; };
window.onresize = (event) => { this.onWindowResize = this.onWindowResize.bind(this);
this.resetPagination({ window.addEventListener('resize', this.onWindowResize, true);
keepCurrentIfLarger: true this.registerKeyboardShortcuts();
}); }
};
onWindowResize() {
this.resetPagination({
keepCurrentIfLarger: true
});
}
deinit() {
this.panelPuppet.onReady = null;
this.panelPuppet = null;
window.removeEventListener('resize', this.onWindowResize, true);
this.onWindowResize = null;
this.onPanelResize = null;
super.deinit();
} }
getInitialState() { getInitialState() {
@@ -91,9 +90,9 @@ class NotesCtrl extends PureCtrl {
/** @override */ /** @override */
onAppStateEvent(eventName, data) { onAppStateEvent(eventName, data) {
if (eventName === AppStateEvents.TagChanged) { if (eventName === AppStateEvents.TagChanged) {
this.handleTagChange(this.appState.getSelectedTag(), data.previousTag); this.handleTagChange(this.application.getAppState().getSelectedTag(), data.previousTag);
} else if (eventName === AppStateEvents.NoteChanged) { } else if (eventName === AppStateEvents.NoteChanged) {
this.handleNoteSelection(this.appState.getSelectedNote()); this.handleNoteSelection(this.application.getAppState().getSelectedNote());
} else if (eventName === AppStateEvents.PreferencesChanged) { } else if (eventName === AppStateEvents.PreferencesChanged) {
this.reloadPreferences(); this.reloadPreferences();
this.reloadNotes(); this.reloadNotes();
@@ -129,8 +128,8 @@ class NotesCtrl extends PureCtrl {
* @access private * @access private
*/ */
async createPlaceholderNote() { async createPlaceholderNote() {
const selectedTag = this.appState.getSelectedTag(); const selectedTag = this.application.getAppState().getSelectedTag();
if(selectedTag.isSmartTag() && !selectedTag.content.isAllTag) { if (selectedTag.isSmartTag() && !selectedTag.content.isAllTag) {
return; return;
} }
return this.createNewNote(); return this.createNewNote();
@@ -163,11 +162,11 @@ class NotesCtrl extends PureCtrl {
} }
async selectNote(note) { async selectNote(note) {
this.appState.setSelectedNote(note); this.application.getAppState().setSelectedNote(note);
} }
async createNewNote() { async createNewNote() {
const selectedTag = this.appState.getSelectedTag(); const selectedTag = this.application.getAppState().getSelectedTag();
if (!selectedTag) { if (!selectedTag) {
throw 'Attempting to create note with no selected tag'; throw 'Attempting to create note with no selected tag';
} }
@@ -209,11 +208,11 @@ class NotesCtrl extends PureCtrl {
await this.selectNote(null); await this.selectNote(null);
} }
await this.setState({ tag: tag }); await this.setState({ tag: tag });
this.resetScrollPosition(); this.resetScrollPosition();
this.setShowMenuFalse(); this.setShowMenuFalse();
await this.setNoteFilterText(''); await this.setNoteFilterText('');
this.desktopManager.searchText(); this.application.getDesktopService().searchText();
this.resetPagination(); this.resetPagination();
/* Capture db load state before beginning reloadNotes, since this status may change during reload */ /* Capture db load state before beginning reloadNotes, since this status may change during reload */
@@ -307,14 +306,14 @@ class NotesCtrl extends PureCtrl {
this.application.saveItem({ item: note }); this.application.saveItem({ item: note });
} }
if (this.isFiltering()) { if (this.isFiltering()) {
this.desktopManager.searchText(this.state.noteFilter.text); this.application.getDesktopService().searchText(this.state.noteFilter.text);
} }
} }
reloadPreferences() { reloadPreferences() {
const viewOptions = {}; const viewOptions = {};
const prevSortValue = this.state.sortBy; const prevSortValue = this.state.sortBy;
let sortBy = this.preferencesManager.getValue( let sortBy = this.application.getPrefsService().getValue(
PrefKeys.SortNotesBy, PrefKeys.SortNotesBy,
SORT_KEY_CREATED_AT SORT_KEY_CREATED_AT
); );
@@ -323,27 +322,27 @@ class NotesCtrl extends PureCtrl {
sortBy = SORT_KEY_CLIENT_UPDATED_AT; sortBy = SORT_KEY_CLIENT_UPDATED_AT;
} }
viewOptions.sortBy = sortBy; viewOptions.sortBy = sortBy;
viewOptions.sortReverse = this.preferencesManager.getValue( viewOptions.sortReverse = this.application.getPrefsService().getValue(
PrefKeys.SortNotesReverse, PrefKeys.SortNotesReverse,
false false
); );
viewOptions.showArchived = this.preferencesManager.getValue( viewOptions.showArchived = this.application.getPrefsService().getValue(
PrefKeys.NotesShowArchived, PrefKeys.NotesShowArchived,
false false
); );
viewOptions.hidePinned = this.preferencesManager.getValue( viewOptions.hidePinned = this.application.getPrefsService().getValue(
PrefKeys.NotesHidePinned, PrefKeys.NotesHidePinned,
false false
); );
viewOptions.hideNotePreview = this.preferencesManager.getValue( viewOptions.hideNotePreview = this.application.getPrefsService().getValue(
PrefKeys.NotesHideNotePreview, PrefKeys.NotesHideNotePreview,
false false
); );
viewOptions.hideDate = this.preferencesManager.getValue( viewOptions.hideDate = this.application.getPrefsService().getValue(
PrefKeys.NotesHideDate, PrefKeys.NotesHideDate,
false false
); );
viewOptions.hideTags = this.preferencesManager.getValue( viewOptions.hideTags = this.application.getPrefsService().getValue(
PrefKeys.NotesHideTags, PrefKeys.NotesHideTags,
false false
); );
@@ -353,13 +352,13 @@ class NotesCtrl extends PureCtrl {
if (prevSortValue && prevSortValue !== sortBy) { if (prevSortValue && prevSortValue !== sortBy) {
this.selectFirstNote(); this.selectFirstNote();
} }
const width = this.preferencesManager.getValue( const width = this.application.getPrefsService().getValue(
PrefKeys.NotesPanelWidth PrefKeys.NotesPanelWidth
); );
if (width && this.panelPuppet.ready) { if (width && this.panelPuppet.ready) {
this.panelPuppet.setWidth(width); this.panelPuppet.setWidth(width);
if (this.panelPuppet.isCollapsed()) { if (this.panelPuppet.isCollapsed()) {
this.appState.panelDidResize({ this.application.getAppState().panelDidResize({
name: PANEL_NAME_NOTES, name: PANEL_NAME_NOTES,
collapsed: this.panelPuppet.isCollapsed() collapsed: this.panelPuppet.isCollapsed()
}); });
@@ -368,12 +367,12 @@ class NotesCtrl extends PureCtrl {
} }
onPanelResize = (newWidth, lastLeft, isAtMaxWidth, isCollapsed) => { onPanelResize = (newWidth, lastLeft, isAtMaxWidth, isCollapsed) => {
this.preferencesManager.setUserPrefValue( this.application.getPrefsService().setUserPrefValue(
PrefKeys.NotesPanelWidth, PrefKeys.NotesPanelWidth,
newWidth newWidth
); );
this.preferencesManager.syncUserPreferences(); this.application.getPrefsService().syncUserPreferences();
this.appState.panelDidResize({ this.application.getAppState().panelDidResize({
name: PANEL_NAME_NOTES, name: PANEL_NAME_NOTES,
collapsed: isCollapsed collapsed: isCollapsed
}); });
@@ -383,7 +382,7 @@ class NotesCtrl extends PureCtrl {
this.notesToDisplay += this.pageSize; this.notesToDisplay += this.pageSize;
this.reloadNotes(); this.reloadNotes();
if (this.searchSubmitted) { if (this.searchSubmitted) {
this.desktopManager.searchText(this.state.noteFilter.text); this.application.getDesktopService().searchText(this.state.noteFilter.text);
} }
} }
@@ -584,7 +583,7 @@ class NotesCtrl extends PureCtrl {
* enter before highlighting desktop search results. * enter before highlighting desktop search results.
*/ */
this.searchSubmitted = true; this.searchSubmitted = true;
this.desktopManager.searchText(this.state.noteFilter.text); this.application.getDesktopService().searchText(this.state.noteFilter.text);
} }
selectedMenuItem() { selectedMenuItem() {
@@ -592,8 +591,8 @@ class NotesCtrl extends PureCtrl {
} }
togglePrefKey(key) { togglePrefKey(key) {
this.preferencesManager.setUserPrefValue(key, !this.state[key]); this.application.getPrefsService().setUserPrefValue(key, !this.state[key]);
this.preferencesManager.syncUserPreferences(); this.application.getPrefsService().syncUserPreferences();
} }
selectedSortByCreated() { selectedSortByCreated() {
@@ -610,19 +609,19 @@ class NotesCtrl extends PureCtrl {
toggleReverseSort() { toggleReverseSort() {
this.selectedMenuItem(); this.selectedMenuItem();
this.preferencesManager.setUserPrefValue( this.application.getPrefsService().setUserPrefValue(
PrefKeys.SortNotesReverse, PrefKeys.SortNotesReverse,
!this.state.sortReverse !this.state.sortReverse
); );
this.preferencesManager.syncUserPreferences(); this.application.getPrefsService().syncUserPreferences();
} }
setSortBy(type) { setSortBy(type) {
this.preferencesManager.setUserPrefValue( this.application.getPrefsService().setUserPrefValue(
PrefKeys.SortNotesBy, PrefKeys.SortNotesBy,
type type
); );
this.preferencesManager.syncUserPreferences(); this.application.getPrefsService().syncUserPreferences();
} }
shouldShowTagsForNote(note) { shouldShowTagsForNote(note) {
@@ -652,7 +651,7 @@ class NotesCtrl extends PureCtrl {
* use Control modifier as well. These rules don't apply to desktop, but * use Control modifier as well. These rules don't apply to desktop, but
* probably better to be consistent. * probably better to be consistent.
*/ */
this.newNoteKeyObserver = this.keyboardManager.addKeyObserver({ this.newNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
key: 'n', key: 'n',
modifiers: [ modifiers: [
KeyboardModifiers.Meta, KeyboardModifiers.Meta,
@@ -664,7 +663,7 @@ class NotesCtrl extends PureCtrl {
} }
}); });
this.nextNoteKeyObserver = this.keyboardManager.addKeyObserver({ this.nextNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
key: KeyboardKeys.Down, key: KeyboardKeys.Down,
elements: [ elements: [
document.body, document.body,
@@ -679,7 +678,7 @@ class NotesCtrl extends PureCtrl {
} }
}); });
this.nextNoteKeyObserver = this.keyboardManager.addKeyObserver({ this.nextNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
key: KeyboardKeys.Up, key: KeyboardKeys.Up,
element: document.body, element: document.body,
onKeyDown: (event) => { onKeyDown: (event) => {
@@ -687,7 +686,7 @@ class NotesCtrl extends PureCtrl {
} }
}); });
this.searchKeyObserver = this.keyboardManager.addKeyObserver({ this.searchKeyObserver = this.application.getKeyboardService().addKeyObserver({
key: "f", key: "f",
modifiers: [ modifiers: [
KeyboardModifiers.Meta, KeyboardModifiers.Meta,
@@ -703,11 +702,13 @@ class NotesCtrl extends PureCtrl {
export class NotesPanel { export class NotesPanel {
constructor() { constructor() {
this.scope = {};
this.template = template; this.template = template;
this.replace = true; this.replace = true;
this.controller = NotesCtrl; this.controller = NotesCtrl;
this.controllerAs = 'self'; this.controllerAs = 'self';
this.bindToController = true; this.bindToController = true;
this.scope = {
application: '='
};
} }
} }

View File

@@ -1,258 +1,16 @@
import { getPlatformString } from '@/utils';
import template from '%/root.pug'; import template from '%/root.pug';
import { AppStateEvents } from '@/state';
import { ApplicationEvents } from 'snjs';
import angular from 'angular';
import {
PANEL_NAME_NOTES,
PANEL_NAME_TAGS
} from '@/controllers/constants';
import {
STRING_SESSION_EXPIRED,
STRING_DEFAULT_FILE_ERROR,
} from '@/strings';
import { PureCtrl } from './abstract/pure_ctrl';
class RootCtrl extends PureCtrl { class RootCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(applicationManager) {
$compile, this.applicationManager = applicationManager;
$location, this.applicationManager.addApplicationChangeObserver(() => {
$scope, this.reload();
$rootScope,
$timeout,
application,
appState,
desktopManager,
godService,
lockManager,
preferencesManager /** Unused below, required to load globally */,
themeManager,
statusManager,
) {
super($scope, $timeout, application, appState);
this.$location = $location;
this.$rootScope = $rootScope;
this.$compile = $compile;
this.desktopManager = desktopManager;
this.godService = godService;
this.lockManager = lockManager;
this.statusManager = statusManager;
this.themeManager = themeManager;
this.platformString = getPlatformString();
this.state = { appClass: '' };
this.loadApplication();
this.addDragDropHandlers();
this.lockScreenPuppet = {
focusInput: () => { }
};
}
async loadApplication() {
await this.application.prepareForLaunch({
callbacks: {
receiveChallenge: async (challenge, orchestrator) => {
this.godService.promptForChallenge(challenge, orchestrator);
}
}
});
await this.application.launch();
}
onAppStart() {
super.onAppStart();
this.overrideComponentManagerFunctions();
this.application.componentManager.setDesktopManager(this.desktopManager);
this.setState({
ready: true,
needsUnlock: this.application.hasPasscode()
}); });
} }
onAppLaunch() { reload() {
super.onAppLaunch(); this.applications = this.applicationManager.getApplications();
this.setState({ needsUnlock: false });
this.handleAutoSignInFromParams();
}
onUpdateAvailable() {
this.$rootScope.$broadcast('new-update-available');
};
/** @override */
async onAppEvent(eventName) {
super.onAppEvent(eventName);
if (eventName === ApplicationEvents.LocalDataIncrementalLoad) {
this.updateLocalDataStatus();
} else if (eventName === ApplicationEvents.SyncStatusChanged) {
this.updateSyncStatus();
} else if (eventName === ApplicationEvents.LocalDataLoaded) {
this.updateLocalDataStatus();
} else if (eventName === ApplicationEvents.WillSync) {
if (!this.completedInitialSync) {
this.syncStatus = this.statusManager.replaceStatusWithString(
this.syncStatus,
"Syncing..."
);
}
} else if (eventName === ApplicationEvents.CompletedSync) {
if (!this.completedInitialSync) {
this.syncStatus = this.statusManager.removeStatus(this.syncStatus);
this.completedInitialSync = true;
}
} else if (eventName === ApplicationEvents.InvalidSyncSession) {
this.showInvalidSessionAlert();
}
}
/** @override */
async onAppStateEvent(eventName, data) {
if (eventName === AppStateEvents.PanelResized) {
if (data.panel === PANEL_NAME_NOTES) {
this.notesCollapsed = data.collapsed;
}
if (data.panel === PANEL_NAME_TAGS) {
this.tagsCollapsed = data.collapsed;
}
let appClass = "";
if (this.notesCollapsed) { appClass += "collapsed-notes"; }
if (this.tagsCollapsed) { appClass += " collapsed-tags"; }
this.setState({ appClass });
} else if (eventName === AppStateEvents.WindowDidFocus) {
if (!(await this.application.isLocked())) {
this.application.sync();
}
}
}
updateLocalDataStatus() {
const syncStatus = this.application.getSyncStatus();
const stats = syncStatus.getStats();
const encryption = this.application.isEncryptionAvailable();
if (stats.localDataDone) {
this.syncStatus = this.statusManager.removeStatus(this.syncStatus);
return;
}
const notesString = `${stats.localDataCurrent}/${stats.localDataTotal} items...`;
const loadingStatus = encryption
? `Decrypting ${notesString}`
: `Loading ${notesString}`;
this.syncStatus = this.statusManager.replaceStatusWithString(
this.syncStatus,
loadingStatus
);
}
updateSyncStatus() {
const syncStatus = this.application.getSyncStatus();
const stats = syncStatus.getStats();
if (stats.downloadCount > 20) {
const text = `Downloading ${stats.downloadCount} items. Keep app open.`;
this.syncStatus = this.statusManager.replaceStatusWithString(
this.syncStatus,
text
);
this.showingDownloadStatus = true;
} else if (this.showingDownloadStatus) {
this.showingDownloadStatus = false;
const text = "Download Complete.";
this.syncStatus = this.statusManager.replaceStatusWithString(
this.syncStatus,
text
);
setTimeout(() => {
this.syncStatus = this.statusManager.removeStatus(this.syncStatus);
}, 2000);
} else if (stats.uploadTotalCount > 20) {
this.uploadSyncStatus = this.statusManager.replaceStatusWithString(
this.uploadSyncStatus,
`Syncing ${stats.uploadCompletionCount}/${stats.uploadTotalCount} items...`
);
} else if (this.uploadSyncStatus) {
this.uploadSyncStatus = this.statusManager.removeStatus(
this.uploadSyncStatus
);
}
}
overrideComponentManagerFunctions() {
function openModalComponent(component) {
const scope = this.$rootScope.$new(true);
scope.component = component;
const el = this.$compile("<component-modal component='component' class='sk-modal'></component-modal>")(scope);
angular.element(document.body).append(el);
}
function presentPermissionsDialog(dialog) {
const scope = this.$rootScope.$new(true);
scope.permissionsString = dialog.permissionsString;
scope.component = dialog.component;
scope.callback = dialog.callback;
const el = this.$compile("<permissions-modal component='component' permissions-string='permissionsString' callback='callback' class='sk-modal'></permissions-modal>")(scope);
angular.element(document.body).append(el);
}
this.application.componentManager.openModalComponent = openModalComponent.bind(this);
this.application.componentManager.presentPermissionsDialog = presentPermissionsDialog.bind(this);
}
showInvalidSessionAlert() {
/** Don't show repeatedly; at most 30 seconds in between */
const SHOW_INTERVAL = 30;
const lastShownSeconds = (new Date() - this.lastShownDate) / 1000;
if (!this.lastShownDate || lastShownSeconds > SHOW_INTERVAL) {
this.lastShownDate = new Date();
setTimeout(() => {
this.alertService.alert({
text: STRING_SESSION_EXPIRED
});
}, 500);
}
}
addDragDropHandlers() {
/**
* Disable dragging and dropping of files (but allow text) into main SN interface.
* both 'dragover' and 'drop' are required to prevent dropping of files.
* This will not prevent extensions from receiving drop events.
*/
window.addEventListener('dragover', (event) => {
if (event.dataTransfer.files.length > 0) {
event.preventDefault();
}
}, false);
window.addEventListener('drop', (event) => {
if (event.dataTransfer.files.length > 0) {
event.preventDefault();
this.application.alertService.alert({
text: STRING_DEFAULT_FILE_ERROR
});
}
}, false);
}
async handleAutoSignInFromParams() {
const params = this.$location.search();
const server = params.server;
const email = params.email;
const password = params.pw;
if (!server || !email || !password) return;
const user = this.application.getUser();
if (user) {
if (user.email === email && await this.application.getHost() === server) {
/** Already signed in, return */
return;
} else {
/** Sign out */
await this.application.signOut();
await this.application.restart();
}
}
await this.application.setHost(server);
this.application.signIn({
email: email,
password: password,
});
} }
} }

View File

@@ -1,6 +1,6 @@
import { SNNote, SNSmartTag, ContentTypes, ApplicationEvents } from 'snjs'; import { SNNote, SNSmartTag, ContentTypes, ApplicationEvents } from 'snjs';
import template from '%/tags.pug'; import template from '%/tags.pug';
import { AppStateEvents } from '@/state'; import { AppStateEvents } from '@/services/state';
import { PANEL_NAME_TAGS } from '@/controllers/constants'; import { PANEL_NAME_TAGS } from '@/controllers/constants';
import { PrefKeys } from '@/services/preferencesManager'; import { PrefKeys } from '@/services/preferencesManager';
import { STRING_DELETE_TAG } from '@/strings'; import { STRING_DELETE_TAG } from '@/strings';
@@ -9,16 +9,9 @@ import { PureCtrl } from '@Controllers';
class TagsPanelCtrl extends PureCtrl { class TagsPanelCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$scope,
$rootScope,
$timeout, $timeout,
application,
appState,
preferencesManager
) { ) {
super($scope, $timeout, application, appState); super($timeout);
this.$rootScope = $rootScope;
this.preferencesManager = preferencesManager;
this.panelPuppet = { this.panelPuppet = {
onReady: () => this.loadPreferences() onReady: () => this.loadPreferences()
}; };
@@ -92,7 +85,7 @@ class TagsPanelCtrl extends PureCtrl {
this.loadPreferences(); this.loadPreferences();
} else if (eventName === AppStateEvents.TagChanged) { } else if (eventName === AppStateEvents.TagChanged) {
this.setState({ this.setState({
selectedTag: this.appState.getSelectedTag() selectedTag: this.application.getAppState().getSelectedTag()
}); });
} }
} }
@@ -136,11 +129,11 @@ class TagsPanelCtrl extends PureCtrl {
if (!this.panelPuppet.ready) { if (!this.panelPuppet.ready) {
return; return;
} }
const width = this.preferencesManager.getValue(PrefKeys.TagsPanelWidth); const width = this.application.getPrefsService().getValue(PrefKeys.TagsPanelWidth);
if (width) { if (width) {
this.panelPuppet.setWidth(width); this.panelPuppet.setWidth(width);
if (this.panelPuppet.isCollapsed()) { if (this.panelPuppet.isCollapsed()) {
this.appState.panelDidResize({ this.application.getAppState().panelDidResize({
name: PANEL_NAME_TAGS, name: PANEL_NAME_TAGS,
collapsed: this.panelPuppet.isCollapsed() collapsed: this.panelPuppet.isCollapsed()
}); });
@@ -149,12 +142,12 @@ class TagsPanelCtrl extends PureCtrl {
} }
onPanelResize = (newWidth, lastLeft, isAtMaxWidth, isCollapsed) => { onPanelResize = (newWidth, lastLeft, isAtMaxWidth, isCollapsed) => {
this.preferencesManager.setUserPrefValue( this.application.getPrefsService().setUserPrefValue(
PrefKeys.TagsPanelWidth, PrefKeys.TagsPanelWidth,
newWidth, newWidth,
true true
); );
this.appState.panelDidResize({ this.application.getAppState().panelDidResize({
name: PANEL_NAME_TAGS, name: PANEL_NAME_TAGS,
collapsed: isCollapsed collapsed: isCollapsed
}); });
@@ -202,7 +195,7 @@ class TagsPanelCtrl extends PureCtrl {
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.application.getAppState().setSelectedTag(tag);
} }
async clickedAddNewTag() { async clickedAddNewTag() {
@@ -299,7 +292,9 @@ class TagsPanelCtrl extends PureCtrl {
export class TagsPanel { export class TagsPanel {
constructor() { constructor() {
this.restrict = 'E'; this.restrict = 'E';
this.scope = {}; this.scope = {
application: '='
};
this.template = template; this.template = template;
this.replace = true; this.replace = true;
this.controller = TagsPanelCtrl; this.controller = TagsPanelCtrl;

View File

@@ -3,27 +3,38 @@ export function clickOutside($document) {
return { return {
restrict: 'A', restrict: 'A',
replace: false, replace: false,
link: function($scope, $element, attrs) { link: function ($scope, $element, attrs) {
var didApplyClickOutside = false; // Causes memory leak as-is:
// let didApplyClickOutside = false;
$element.bind('click', function(e) { // $scope.$on('$destroy', () => {
didApplyClickOutside = false; // attrs.clickOutside = null;
if (attrs.isOpen) { // $element.unbind('click', $scope.onElementClick);
e.stopPropagation(); // $document.unbind('click', $scope.onDocumentClick);
} // $scope.onElementClick = null;
}); // $scope.onDocumentClick = null;
// });
$document.bind('click', function() { // $scope.onElementClick = (event) => {
// Ignore click if on SKAlert // didApplyClickOutside = false;
if (event.target.closest(".sk-modal")) { // if (attrs.isOpen) {
return; // event.stopPropagation();
} // }
// };
// $scope.onDocumentClick = (event) => {
// /* Ignore click if on SKAlert */
// if (event.target.closest('.sk-modal')) {
// return;
// }
// if (!didApplyClickOutside) {
// $scope.$apply(attrs.clickOutside);
// didApplyClickOutside = true;
// }
// };
if (!didApplyClickOutside) { // $element.bind('click', $scope.onElementClick);
$scope.$apply(attrs.clickOutside); // $document.bind('click', $scope.onDocumentClick);
didApplyClickOutside = true;
}
});
} }
}; };
} }

View File

@@ -1,16 +1,20 @@
/* @ngInject */ /* @ngInject */
export function infiniteScroll($rootScope, $window, $timeout) { export function infiniteScroll() {
return { return {
link: function(scope, elem, attrs) { link: function(scope, elem, attrs) {
const offset = parseInt(attrs.threshold) || 0; const offset = parseInt(attrs.threshold) || 0;
const e = elem[0]; const e = elem[0];
elem.on('scroll', function() { scope.onScroll = () => {
if ( if (
scope.$eval(attrs.canLoad) && scope.$eval(attrs.canLoad) &&
e.scrollTop + e.offsetHeight >= e.scrollHeight - offset e.scrollTop + e.offsetHeight >= e.scrollHeight - offset
) { ) {
scope.$apply(attrs.infiniteScroll); scope.$apply(attrs.infiniteScroll);
} }
};
elem.on('scroll', scope.onScroll);
scope.$on('$destroy', () => {
elem.off('scroll', scope.onScroll);;
}); });
} }
}; };

View File

@@ -28,32 +28,18 @@ const ELEMENT_NAME_AUTH_PASSWORD_CONF = 'password_conf';
class AccountMenuCtrl extends PureCtrl { class AccountMenuCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$scope,
$rootScope,
$timeout, $timeout,
appVersion, appVersion,
application,
appState,
archiveManager,
godService,
lockManager,
) { ) {
super($scope, $timeout, application, appState); super($timeout);
this.$rootScope = $rootScope;
this.appState = appState;
this.application = application;
this.archiveManager = archiveManager;
this.godService = godService;
this.lockManager = lockManager;
this.appVersion = appVersion; this.appVersion = appVersion;
this.syncStatus = this.application.getSyncStatus();
} }
/** @override */ /** @override */
getInitialState() { getInitialState() {
return { return {
appVersion: 'v' + (window.electronAppVersion || this.appVersion), appVersion: 'v' + (window.electronAppVersion || this.appVersion),
passcodeAutoLockOptions: this.lockManager.getAutoLockIntervalOptions(), passcodeAutoLockOptions: this.application.getLockService().getAutoLockIntervalOptions(),
user: this.application.getUser(), user: this.application.getUser(),
formData: { formData: {
mergeLocal: true, mergeLocal: true,
@@ -90,11 +76,12 @@ class AccountMenuCtrl extends PureCtrl {
this.initProps({ this.initProps({
closeFunction: this.closeFunction closeFunction: this.closeFunction
}); });
this.syncStatus = this.application.getSyncStatus();
} }
close() { close() {
this.$timeout(() => { this.$timeout(() => {
this.props.closeFunction()(); this.props.closeFunction();
}); });
} }
@@ -277,19 +264,19 @@ class AccountMenuCtrl extends PureCtrl {
openPasswordWizard() { openPasswordWizard() {
this.close(); this.close();
this.godService.presentPasswordWizard(); this.application.presentPasswordWizard();
} }
async openPrivilegesModal() { async openPrivilegesModal() {
this.close(); this.close();
const run = () => { const run = () => {
this.godService.presentPrivilegesManagementModal(); this.application.presentPrivilegesManagementModal();
}; };
const needsPrivilege = await this.application.privilegesService.actionRequiresPrivilege( const needsPrivilege = await this.application.privilegesService.actionRequiresPrivilege(
ProtectedActions.ManagePrivileges ProtectedActions.ManagePrivileges
); );
if (needsPrivilege) { if (needsPrivilege) {
this.godService.presentPrivilegesModal( this.application.presentPrivilegesModal(
ProtectedActions.ManagePrivileges, ProtectedActions.ManagePrivileges,
() => { () => {
run(); run();
@@ -366,7 +353,7 @@ class AccountMenuCtrl extends PureCtrl {
ProtectedActions.ManageBackups ProtectedActions.ManageBackups
); );
if (needsPrivilege) { if (needsPrivilege) {
this.godService.presentPrivilegesModal( this.application.presentPrivilegesModal(
ProtectedActions.ManageBackups, ProtectedActions.ManageBackups,
run run
); );
@@ -416,7 +403,7 @@ class AccountMenuCtrl extends PureCtrl {
} }
async downloadDataArchive() { async downloadDataArchive() {
this.archiveManager.downloadBackup(this.state.mutable.backupEncrypted); this.application.getArchiveService().downloadBackup(this.state.mutable.backupEncrypted);
} }
notesAndTagsCount() { notesAndTagsCount() {
@@ -434,7 +421,7 @@ class AccountMenuCtrl extends PureCtrl {
} }
async reloadAutoLockInterval() { async reloadAutoLockInterval() {
const interval = await this.lockManager.getAutoLockInterval(); const interval = await this.application.getLockService().getAutoLockInterval();
this.setState({ this.setState({
selectedAutoLockInterval: interval selectedAutoLockInterval: interval
}); });
@@ -442,14 +429,14 @@ class AccountMenuCtrl extends PureCtrl {
async selectAutoLockInterval(interval) { async selectAutoLockInterval(interval) {
const run = async () => { const run = async () => {
await this.lockManager.setAutoLockInterval(interval); await this.application.getLockService().setAutoLockInterval(interval);
this.reloadAutoLockInterval(); this.reloadAutoLockInterval();
}; };
const needsPrivilege = await this.application.privilegesService.actionRequiresPrivilege( const needsPrivilege = await this.application.privilegesService.actionRequiresPrivilege(
ProtectedActions.ManagePasscode ProtectedActions.ManagePasscode
); );
if (needsPrivilege) { if (needsPrivilege) {
this.godService.presentPrivilegesModal( this.application.presentPrivilegesModal(
ProtectedActions.ManagePasscode, ProtectedActions.ManagePasscode,
() => { () => {
run(); run();
@@ -508,7 +495,7 @@ class AccountMenuCtrl extends PureCtrl {
ProtectedActions.ManagePasscode ProtectedActions.ManagePasscode
); );
if (needsPrivilege) { if (needsPrivilege) {
this.godService.presentPrivilegesModal( this.application.presentPrivilegesModal(
ProtectedActions.ManagePasscode, ProtectedActions.ManagePasscode,
run run
); );
@@ -536,7 +523,7 @@ class AccountMenuCtrl extends PureCtrl {
ProtectedActions.ManagePasscode ProtectedActions.ManagePasscode
); );
if (needsPrivilege) { if (needsPrivilege) {
this.godService.presentPrivilegesModal( this.application.presentPrivilegesModal(
ProtectedActions.ManagePasscode, ProtectedActions.ManagePasscode,
run run
); );
@@ -558,7 +545,8 @@ export class AccountMenu {
this.controllerAs = 'self'; this.controllerAs = 'self';
this.bindToController = true; this.bindToController = true;
this.scope = { this.scope = {
closeFunction: '&' closeFunction: '&',
application: '='
}; };
} }
} }

View File

@@ -4,14 +4,9 @@ import { PureCtrl } from '@Controllers';
class ActionsMenuCtrl extends PureCtrl { class ActionsMenuCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$scope, $timeout
$timeout,
application,
appState,
godService
) { ) {
super($scope, $timeout, application, appState); super($timeout);
this.godService = godService;
this.state = { this.state = {
extensions: [] extensions: []
}; };
@@ -77,7 +72,7 @@ class ActionsMenuCtrl extends PureCtrl {
switch (action.verb) { switch (action.verb) {
case 'render': { case 'render': {
const item = result.item; const item = result.item;
this.godService.presentRevisionPreviewModal( this.application.presentRevisionPreviewModal(
item.uuid, item.uuid,
item.content item.content
); );
@@ -111,7 +106,8 @@ export class ActionsMenu {
this.controllerAs = 'self'; this.controllerAs = 'self';
this.bindToController = true; this.bindToController = true;
this.scope = { this.scope = {
item: '=' item: '=',
application: '='
}; };
} }
} }

View File

@@ -5,13 +5,10 @@ import { PureCtrl } from '@Controllers';
class ChallengeModalCtrl extends PureCtrl { class ChallengeModalCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$scope,
$element, $element,
$timeout, $timeout
application,
appState
) { ) {
super($scope, $timeout, application, appState); super($timeout);
this.$element = $element; this.$element = $element;
this.processingTypes = []; this.processingTypes = [];
} }
@@ -48,6 +45,13 @@ class ChallengeModalCtrl extends PureCtrl {
}); });
} }
deinit() {
this.application = null;
this.orchestrator = null;
this.challenge = null;
super.deinit();
}
reloadProcessingStatus() { reloadProcessingStatus() {
this.setState({ this.setState({
processing: this.processingTypes.length > 0 processing: this.processingTypes.length > 0
@@ -106,7 +110,10 @@ class ChallengeModalCtrl extends PureCtrl {
} }
dismiss() { dismiss() {
this.$element.remove(); const elem = this.$element;
const scope = elem.scope();
scope.$destroy();
elem.remove();
} }
} }
@@ -116,11 +123,10 @@ export class ChallengeModal {
this.template = template; this.template = template;
this.controller = ChallengeModalCtrl; this.controller = ChallengeModalCtrl;
this.controllerAs = 'ctrl'; this.controllerAs = 'ctrl';
this.bindToController = true; this.bindToController = {
this.scope = {
onSubmit: '=',
challenge: '=', challenge: '=',
orchestrator: '=' orchestrator: '=',
application: '='
}; };
} }
} }

View File

@@ -2,18 +2,19 @@ import template from '%/directives/component-modal.pug';
export class ComponentModalCtrl { export class ComponentModalCtrl {
/* @ngInject */ /* @ngInject */
constructor($scope, $element) { constructor($element) {
this.$element = $element; this.$element = $element;
this.$scope = $scope;
} }
dismiss(callback) { dismiss(callback) {
this.$element.remove(); if(this.onDismiss) {
this.$scope.$destroy(); this.onDismiss(this.component);
if(this.onDismiss && this.onDismiss()) {
this.onDismiss()(this.component);
} }
callback && callback(); this.callback && this.callback();
const elem = this.$element;
const scope = elem.scope();
scope.$destroy();
elem.remove();
} }
} }

View File

@@ -14,26 +14,41 @@ class ComponentViewCtrl {
$scope, $scope,
$rootScope, $rootScope,
$timeout, $timeout,
application,
desktopManager,
themeManager
) { ) {
this.$rootScope = $rootScope; this.$rootScope = $rootScope;
this.$timeout = $timeout; this.$timeout = $timeout;
this.application = application;
this.themeManager = themeManager;
this.desktopManager = desktopManager;
this.componentValid = true; this.componentValid = true;
this.cleanUpWatch = $scope.$watch('ctrl.component', (component, prevComponent) => {
$scope.$watch('ctrl.component', (component, prevComponent) => {
this.componentValueDidSet(component, prevComponent); this.componentValueDidSet(component, prevComponent);
}); });
$scope.$on('ext-reload-complete', () => { this.cleanUpOn = $scope.$on('ext-reload-complete', () => {
this.reloadStatus(false); this.reloadStatus(false);
}); });
$scope.$on('$destroy', () => {
this.destroy(); /** To allow for registering events */
}); this.onVisibilityChange = this.onVisibilityChange.bind(this);
}
$onDestroy() {
this.cleanUpWatch();
this.cleanUpOn();
this.cleanUpWatch = null;
this.cleanUpOn = null;
this.application.componentManager.deregisterHandler(this.themeHandlerIdentifier);
this.application.componentManager.deregisterHandler(this.identifier);
if (this.component && !this.manualDealloc) {
const dontSync = true;
this.application.componentManager.deactivateComponent(this.component, dontSync);
}
this.application.getDesktopService().deregisterUpdateObserver(this.updateObserver);
document.removeEventListener(
VISIBILITY_CHANGE_LISTENER_KEY,
this.onVisibilityChange
);
this.component = null;
this.onLoad = null;
this.application = null;
this.onVisibilityChange = null;
} }
$onInit() { $onInit() {
@@ -42,12 +57,12 @@ class ComponentViewCtrl {
}; };
registerPackageUpdateObserver() { registerPackageUpdateObserver() {
this.updateObserver = this.desktopManager this.updateObserver = this.application.getDesktopService()
.registerUpdateObserver((component) => { .registerUpdateObserver((component) => {
if(component === this.component && component.active) { if (component === this.component && component.active) {
this.reloadComponent(); this.reloadComponent();
} }
}); });
} }
registerComponentHandlers() { registerComponentHandlers() {
@@ -65,7 +80,7 @@ class ComponentViewCtrl {
identifier: this.identifier, identifier: this.identifier,
areas: [this.component.area], areas: [this.component.area],
activationHandler: (component) => { activationHandler: (component) => {
if(component !== this.component) { if (component !== this.component) {
return; return;
} }
this.$timeout(() => { this.$timeout(() => {
@@ -73,7 +88,7 @@ class ComponentViewCtrl {
}); });
}, },
actionHandler: (component, action, data) => { actionHandler: (component, action, data) => {
if(action === 'set-size') { if (action === 'set-size') {
this.application.componentManager.handleSetSizeEvent(component, data); this.application.componentManager.handleSetSizeEvent(component, data);
} }
} }
@@ -81,10 +96,10 @@ class ComponentViewCtrl {
} }
onVisibilityChange() { onVisibilityChange() {
if(document.visibilityState === 'hidden') { if (document.visibilityState === 'hidden') {
return; return;
} }
if(this.issueLoading) { if (this.issueLoading) {
this.reloadComponent(); this.reloadComponent();
} }
} }
@@ -100,34 +115,34 @@ class ComponentViewCtrl {
const component = this.component; const component = this.component;
const previouslyValid = this.componentValid; const previouslyValid = this.componentValid;
const offlineRestricted = component.offlineOnly && !isDesktopApplication(); const offlineRestricted = component.offlineOnly && !isDesktopApplication();
const hasUrlError = function(){ const hasUrlError = function () {
if(isDesktopApplication()) { if (isDesktopApplication()) {
return !component.local_url && !component.hasValidHostedUrl(); return !component.local_url && !component.hasValidHostedUrl();
} else { } else {
return !component.hasValidHostedUrl(); return !component.hasValidHostedUrl();
} }
}(); }();
this.expired = component.valid_until && component.valid_until <= new Date(); this.expired = component.valid_until && component.valid_until <= new Date();
if(!component.lockReadonly) { if (!component.lockReadonly) {
component.readonly = this.expired; component.readonly = this.expired;
} }
this.componentValid = !offlineRestricted && !hasUrlError; this.componentValid = !offlineRestricted && !hasUrlError;
if(!this.componentValid) { if (!this.componentValid) {
this.loading = false; this.loading = false;
} }
if(offlineRestricted) { if (offlineRestricted) {
this.error = 'offline-restricted'; this.error = 'offline-restricted';
} else if(hasUrlError) { } else if (hasUrlError) {
this.error = 'url-missing'; this.error = 'url-missing';
} else { } else {
this.error = null; this.error = null;
} }
if(this.componentValid !== previouslyValid) { if (this.componentValid !== previouslyValid) {
if(this.componentValid) { if (this.componentValid) {
this.application.componentManager.reloadComponent(component, true); this.application.componentManager.reloadComponent(component, true);
} }
} }
if(this.expired && doManualReload) { if (this.expired && doManualReload) {
this.$rootScope.$broadcast('reload-ext-dat'); this.$rootScope.$broadcast('reload-ext-dat');
} }
@@ -137,17 +152,17 @@ class ComponentViewCtrl {
} }
handleActivation() { handleActivation() {
if(!this.component.active) { if (!this.component.active) {
return; return;
} }
const iframe = this.application.componentManager.iframeForComponent( const iframe = this.application.componentManager.iframeForComponent(
this.component this.component
); );
if(!iframe) { if (!iframe) {
return; return;
} }
this.loading = true; this.loading = true;
if(this.loadTimeout) { if (this.loadTimeout) {
this.$timeout.cancel(this.loadTimeout); this.$timeout.cancel(this.loadTimeout);
} }
this.loadTimeout = this.$timeout(() => { this.loadTimeout = this.$timeout(() => {
@@ -160,16 +175,16 @@ class ComponentViewCtrl {
} }
async handleIframeLoadTimeout() { async handleIframeLoadTimeout() {
if(this.loading) { if (this.loading) {
this.loading = false; this.loading = false;
this.issueLoading = true; this.issueLoading = true;
if(!this.didAttemptReload) { if (!this.didAttemptReload) {
this.didAttemptReload = true; this.didAttemptReload = true;
this.reloadComponent(); this.reloadComponent();
} else { } else {
document.addEventListener( document.addEventListener(
VISIBILITY_CHANGE_LISTENER_KEY, VISIBILITY_CHANGE_LISTENER_KEY,
this.onVisibilityChange.bind(this) this.onVisibilityChange
); );
} }
} }
@@ -177,13 +192,13 @@ class ComponentViewCtrl {
async handleIframeLoad(iframe) { async handleIframeLoad(iframe) {
let desktopError = false; let desktopError = false;
if(isDesktopApplication()) { if (isDesktopApplication()) {
try { try {
/** Accessing iframe.contentWindow.origin only allowed in desktop app. */ /** Accessing iframe.contentWindow.origin only allowed in desktop app. */
if(!iframe.contentWindow.origin || iframe.contentWindow.origin === 'null') { if (!iframe.contentWindow.origin || iframe.contentWindow.origin === 'null') {
desktopError = true; desktopError = true;
} }
} catch (e) {} } catch (e) { }
} }
this.$timeout.cancel(this.loadTimeout); this.$timeout.cancel(this.loadTimeout);
await this.application.componentManager.registerComponentWindow( await this.application.componentManager.registerComponentWindow(
@@ -201,13 +216,13 @@ class ComponentViewCtrl {
componentValueDidSet(component, prevComponent) { componentValueDidSet(component, prevComponent) {
const dontSync = true; const dontSync = true;
if(prevComponent && component !== prevComponent) { if (prevComponent && component !== prevComponent) {
this.application.componentManager.deactivateComponent( this.application.componentManager.deactivateComponent(
prevComponent, prevComponent,
dontSync dontSync
); );
} }
if(component) { if (component) {
this.application.componentManager.activateComponent( this.application.componentManager.activateComponent(
component, component,
dontSync dontSync
@@ -217,7 +232,7 @@ class ComponentViewCtrl {
} }
disableActiveTheme() { disableActiveTheme() {
this.themeManager.deactivateAllThemes(); this.application.getThemeService().deactivateAllThemes();
} }
getUrl() { getUrl() {
@@ -225,21 +240,6 @@ class ComponentViewCtrl {
this.component.runningLocally = (url === this.component.local_url); this.component.runningLocally = (url === this.component.local_url);
return url; return url;
} }
destroy() {
this.application.componentManager.deregisterHandler(this.themeHandlerIdentifier);
this.application.componentManager.deregisterHandler(this.identifier);
if(this.component && !this.manualDealloc) {
const dontSync = true;
this.application.componentManager.deactivateComponent(this.component, dontSync);
}
this.desktopManager.deregisterUpdateObserver(this.updateObserver);
document.removeEventListener(
VISIBILITY_CHANGE_LISTENER_KEY,
this.onVisibilityChange.bind(this)
);
}
} }
export class ComponentView { export class ComponentView {
@@ -249,7 +249,8 @@ export class ComponentView {
this.scope = { this.scope = {
component: '=', component: '=',
onLoad: '=?', onLoad: '=?',
manualDealloc: '=?' manualDealloc: '=?',
application: '='
}; };
this.controller = ComponentViewCtrl; this.controller = ComponentViewCtrl;
this.controllerAs = 'ctrl'; this.controllerAs = 'ctrl';

View File

@@ -2,14 +2,8 @@ import template from '%/directives/conflict-resolution-modal.pug';
class ConflictResolutionCtrl { class ConflictResolutionCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor($element) {
$element,
archiveManager,
application
) {
this.$element = $element; this.$element = $element;
this.application = application;
this.archiveManager = archiveManager;
} }
$onInit() { $onInit() {
@@ -56,7 +50,7 @@ class ConflictResolutionCtrl {
} }
export() { export() {
this.archiveManager.downloadBackupOfItems( this.application.getArchiveService().downloadBackupOfItems(
[this.item1, this.item2], [this.item1, this.item2],
true true
); );
@@ -67,7 +61,10 @@ class ConflictResolutionCtrl {
} }
dismiss() { dismiss() {
this.$element.remove(); const elem = this.$element;
const scope = elem.scope();
scope.$destroy();
elem.remove();
} }
} }
@@ -81,7 +78,8 @@ export class ConflictResolutionModal {
this.scope = { this.scope = {
item1: '=', item1: '=',
item2: '=', item2: '=',
callback: '=' callback: '=',
application: '='
}; };
} }
} }

View File

@@ -5,12 +5,9 @@ import { PureCtrl } from '@Controllers';
class EditorMenuCtrl extends PureCtrl { class EditorMenuCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$scope,
$timeout, $timeout,
application,
appState
) { ) {
super($scope, $timeout, application, appState); super($timeout);
this.state = { this.state = {
isDesktop: isDesktopApplication() isDesktop: isDesktopApplication()
}; };
@@ -98,7 +95,8 @@ export class EditorMenu {
this.scope = { this.scope = {
callback: '&', callback: '&',
selectedEditor: '=', selectedEditor: '=',
currentItem: '=' currentItem: '=',
application: '='
}; };
} }
} }

View File

@@ -3,14 +3,16 @@ import template from '%/directives/input-modal.pug';
class InputModalCtrl { class InputModalCtrl {
/* @ngInject */ /* @ngInject */
constructor($scope, $element) { constructor($element) {
this.$element = $element; this.$element = $element;
this.formData = {}; this.formData = {};
} }
dismiss() { dismiss() {
this.$element.remove(); const elem = this.$element;
this.$scope.$destroy(); const scope = elem.scope();
scope.$destroy();
elem.remove();
} }
submit() { submit() {

View File

@@ -26,13 +26,17 @@ class PanelResizerCtrl {
constructor( constructor(
$compile, $compile,
$element, $element,
$scope,
$timeout, $timeout,
) { ) {
this.$compile = $compile; this.$compile = $compile;
this.$element = $element; this.$element = $element;
this.$scope = $scope;
this.$timeout = $timeout; this.$timeout = $timeout;
/** To allow for registering events */
this.handleResize = this.handleResize.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.onMouseDown = this.onMouseDown.bind(this);
} }
$onInit() { $onInit() {
@@ -45,6 +49,19 @@ class PanelResizerCtrl {
this.addMouseUpListener(); this.addMouseUpListener();
} }
$onDestroy() {
this.onResizeFinish = null;
this.control = null;
window.removeEventListener(WINDOW_EVENT_RESIZE, this.handleResize);
document.removeEventListener(MouseEvents.Move, this.onMouseMove);
document.removeEventListener(MouseEvents.Up, this.onMouseUp);
this.resizerColumn.removeEventListener(MouseEvents.Down, this.onMouseDown);
this.handleResize = null;
this.onMouseMove = null;
this.onMouseUp = null;
this.onMouseDown = null;
}
configureControl() { configureControl() {
this.control.setWidth = (value) => { this.control.setWidth = (value) => {
this.setWidth(value, true); this.setWidth(value, true);
@@ -96,17 +113,17 @@ class PanelResizerCtrl {
} }
configureRightPanel() { configureRightPanel() {
const handleResize = debounce(event => { window.addEventListener(WINDOW_EVENT_RESIZE, this.handleResize);
}
handleResize() {
debounce(() => {
this.reloadDefaultValues(); this.reloadDefaultValues();
this.handleWidthEvent(); this.handleWidthEvent();
this.$timeout(() => { this.$timeout(() => {
this.finishSettingWidth(); this.finishSettingWidth();
}); });
}, 250); }, 250);
window.addEventListener(WINDOW_EVENT_RESIZE, handleResize);
this.$scope.$on('$destroy', () => {
window.removeEventListener(WINDOW_EVENT_RESIZE, handleResize);
});
} }
getParentRect() { getParentRect() {
@@ -135,7 +152,7 @@ class PanelResizerCtrl {
this.finishSettingWidth(); this.finishSettingWidth();
const newCollapseState = !preClickCollapseState; const newCollapseState = !preClickCollapseState;
this.onResizeFinish()( this.onResizeFinish(
this.lastWidth, this.lastWidth,
this.lastLeft, this.lastLeft,
this.isAtMaxWidth(), this.isAtMaxWidth(),
@@ -146,31 +163,35 @@ class PanelResizerCtrl {
} }
addMouseDownListener() { addMouseDownListener() {
this.resizerColumn.addEventListener(MouseEvents.Down, (event) => { this.resizerColumn.addEventListener(MouseEvents.Down, this.onMouseDown);
this.addInvisibleOverlay(); }
this.pressed = true;
this.lastDownX = event.clientX; onMouseDown(event) {
this.startWidth = this.panel.scrollWidth; this.addInvisibleOverlay();
this.startLeft = this.panel.offsetLeft; this.pressed = true;
this.panel.classList.add(CssClasses.NoSelection); this.lastDownX = event.clientX;
if (this.hoverable) { this.startWidth = this.panel.scrollWidth;
this.resizerColumn.classList.add(CssClasses.Dragging); this.startLeft = this.panel.offsetLeft;
} this.panel.classList.add(CssClasses.NoSelection);
}); if (this.hoverable) {
this.resizerColumn.classList.add(CssClasses.Dragging);
}
} }
addMouseMoveListener() { addMouseMoveListener() {
document.addEventListener(MouseEvents.Move, (event) => { document.addEventListener(MouseEvents.Move, this.onMouseMove);
if (!this.pressed) { }
return;
} onMouseMove(event) {
event.preventDefault(); if (!this.pressed) {
if (this.property && this.property === PanelSides.Left) { return;
this.handleLeftEvent(event); }
} else { event.preventDefault();
this.handleWidthEvent(event); if (this.property && this.property === PanelSides.Left) {
} this.handleLeftEvent(event);
}); } else {
this.handleWidthEvent(event);
}
} }
handleWidthEvent(event) { handleWidthEvent(event) {
@@ -186,9 +207,6 @@ class PanelResizerCtrl {
const deltaX = x - this.lastDownX; const deltaX = x - this.lastDownX;
const newWidth = this.startWidth + deltaX; const newWidth = this.startWidth + deltaX;
this.setWidth(newWidth, false); this.setWidth(newWidth, false);
if (this.onResize()) {
this.onResize()(this.lastWidth, this.panel);
}
} }
handleLeftEvent(event) { handleLeftEvent(event) {
@@ -216,24 +234,27 @@ class PanelResizerCtrl {
} }
addMouseUpListener() { addMouseUpListener() {
document.addEventListener(MouseEvents.Up, event => { document.addEventListener(MouseEvents.Up, this.onMouseUp);
this.removeInvisibleOverlay(); }
if (this.pressed) {
this.pressed = false; onMouseUp() {
this.resizerColumn.classList.remove(CssClasses.Dragging); this.removeInvisibleOverlay();
this.panel.classList.remove(CssClasses.NoSelection); if (!this.pressed) {
const isMaxWidth = this.isAtMaxWidth(); return;
if (this.onResizeFinish) { }
this.onResizeFinish()( this.pressed = false;
this.lastWidth, this.resizerColumn.classList.remove(CssClasses.Dragging);
this.lastLeft, this.panel.classList.remove(CssClasses.NoSelection);
isMaxWidth, const isMaxWidth = this.isAtMaxWidth();
this.isCollapsed() if (this.onResizeFinish) {
); this.onResizeFinish(
} this.lastWidth,
this.finishSettingWidth(); this.lastLeft,
} isMaxWidth,
}); this.isCollapsed()
);
}
this.finishSettingWidth();
} }
isAtMaxWidth() { isAtMaxWidth() {
@@ -301,7 +322,7 @@ class PanelResizerCtrl {
if (this.overlay) { if (this.overlay) {
return; return;
} }
this.overlay = this.$compile(`<div id='resizer-overlay'></div>`)(this.$scope); this.overlay = this.$compile(`<div id='resizer-overlay'></div>`)(this);
angular.element(document.body).prepend(this.overlay); angular.element(document.body).prepend(this.overlay);
} }
@@ -336,7 +357,6 @@ export class PanelResizer {
hoverable: '=', hoverable: '=',
index: '=', index: '=',
minWidth: '=', minWidth: '=',
onResize: '&',
onResizeFinish: '&', onResizeFinish: '&',
panelId: '=', panelId: '=',
property: '=' property: '='

View File

@@ -11,15 +11,11 @@ class PasswordWizardCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$element, $element,
$scope,
$timeout, $timeout,
application,
appState
) { ) {
super($scope, $timeout, application, appState); super($timeout);
this.$element = $element; this.$element = $element;
this.$timeout = $timeout; this.$timeout = $timeout;
this.$scope = $scope;
this.registerWindowUnloadStopper(); this.registerWindowUnloadStopper();
} }
@@ -38,14 +34,16 @@ class PasswordWizardCtrl extends PureCtrl {
}); });
} }
$onDestroy() {
super.$onDestroy();
window.onbeforeunload = null;
}
/** Confirms with user before closing tab */ /** Confirms with user before closing tab */
registerWindowUnloadStopper() { registerWindowUnloadStopper() {
window.onbeforeunload = (e) => { window.onbeforeunload = (e) => {
return true; return true;
}; };
this.$scope.$on("$destroy", () => {
window.onbeforeunload = null;
});
} }
resetContinueState() { resetContinueState() {
@@ -190,8 +188,10 @@ class PasswordWizardCtrl extends PureCtrl {
text: "Cannot close window until pending tasks are complete." text: "Cannot close window until pending tasks are complete."
}); });
} else { } else {
this.$element.remove(); const elem = this.$element;
this.$scope.$destroy(); const scope = elem.scope();
scope.$destroy();
elem.remove();
} }
} }
} }
@@ -204,7 +204,8 @@ export class PasswordWizard {
this.controllerAs = 'ctrl'; this.controllerAs = 'ctrl';
this.bindToController = true; this.bindToController = true;
this.scope = { this.scope = {
type: '=' type: '=',
application: '='
}; };
} }
} }

View File

@@ -7,7 +7,10 @@ class PermissionsModalCtrl {
} }
dismiss() { dismiss() {
this.$element.remove(); const elem = this.$element;
const scope = elem.scope();
scope.$destroy();
elem.remove();
} }
accept() { accept() {

View File

@@ -4,12 +4,10 @@ class PrivilegesAuthModalCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$element, $element,
$timeout, $timeout
application
) { ) {
this.$element = $element; this.$element = $element;
this.$timeout = $timeout; this.$timeout = $timeout;
this.application = application;
} }
$onInit() { $onInit() {
@@ -83,7 +81,10 @@ class PrivilegesAuthModalCtrl {
} }
dismiss() { dismiss() {
this.$element.remove(); const elem = this.$element;
const scope = elem.scope();
scope.$destroy();
elem.remove();
} }
} }
@@ -97,7 +98,8 @@ export class PrivilegesAuthModal {
this.scope = { this.scope = {
action: '=', action: '=',
onSuccess: '=', onSuccess: '=',
onCancel: '=' onCancel: '=',
application: '='
}; };
} }
} }

View File

@@ -5,15 +5,11 @@ import { PureCtrl } from '@Controllers';
class PrivilegesManagementModalCtrl extends PureCtrl { class PrivilegesManagementModalCtrl extends PureCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$scope,
$timeout, $timeout,
$element, $element
application,
appState
) { ) {
super($scope, $timeout, application, appState); super($timeout);
this.$element = $element; this.$element = $element;
this.application = application;
} }
onAppLaunch() { onAppLaunch() {
@@ -78,7 +74,10 @@ class PrivilegesManagementModalCtrl extends PureCtrl {
} }
dismiss() { dismiss() {
this.$element.remove(); const elem = this.$element;
const scope = elem.scope();
scope.$destroy();
elem.remove();
} }
} }
@@ -89,6 +88,8 @@ export class PrivilegesManagementModal {
this.controller = PrivilegesManagementModalCtrl; this.controller = PrivilegesManagementModalCtrl;
this.controllerAs = 'ctrl'; this.controllerAs = 'ctrl';
this.bindToController = true; this.bindToController = true;
this.scope = {}; this.scope = {
application: '='
};
} }
} }

View File

@@ -8,24 +8,21 @@ class RevisionPreviewModalCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$element, $element,
$scope, $timeout
$timeout,
application
) { ) {
this.$element = $element; this.$element = $element;
this.$scope = $scope;
this.$timeout = $timeout; this.$timeout = $timeout;
this.application = application;
$scope.$on('$destroy', () => {
if (this.identifier) {
this.application.componentManager.deregisterHandler(this.identifier);
}
});
} }
$onInit() { $onInit() {
this.configure(); this.configure();
} }
$onDestroy() {
if (this.identifier) {
this.application.componentManager.deregisterHandler(this.identifier);
}
}
async configure() { async configure() {
this.note = await this.application.createTemplateItem({ this.note = await this.application.createTemplateItem({
@@ -110,8 +107,10 @@ class RevisionPreviewModalCtrl {
} }
dismiss() { dismiss() {
this.$element.remove(); const elem = this.$element;
this.$scope.$destroy(); const scope = elem.scope();
scope.$destroy();
elem.remove();
} }
} }
@@ -124,7 +123,8 @@ export class RevisionPreviewModal {
this.bindToController = true; this.bindToController = true;
this.scope = { this.scope = {
uuid: '=', uuid: '=',
content: '=' content: '=',
application: '='
}; };
} }
} }

View File

@@ -3,19 +3,15 @@ import template from '%/directives/session-history-menu.pug';
class SessionHistoryMenuCtrl { class SessionHistoryMenuCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$timeout, $timeout
godService,
application,
) { ) {
this.$timeout = $timeout; this.$timeout = $timeout;
this.godService = godService;
this.application = application;
this.diskEnabled = this.application.historyManager.isDiskEnabled();
this.autoOptimize = this.application.historyManager.isAutoOptimizeEnabled();
} }
$onInit() { $onInit() {
this.reloadHistory(); this.reloadHistory();
this.diskEnabled = this.application.historyManager.isDiskEnabled();
this.autoOptimize = this.application.historyManager.isAutoOptimizeEnabled();
} }
reloadHistory() { reloadHistory() {
@@ -27,7 +23,7 @@ class SessionHistoryMenuCtrl {
} }
openRevision(revision) { openRevision(revision) {
this.godService.presentRevisionPreviewModal( this.application.presentRevisionPreviewModal(
revision.item.uuid, revision.item.uuid,
revision.item.content revision.item.content
); );
@@ -110,7 +106,8 @@ export class SessionHistoryMenu {
this.controllerAs = 'ctrl'; this.controllerAs = 'ctrl';
this.bindToController = true; this.bindToController = true;
this.scope = { this.scope = {
item: '=' item: '=',
application: '='
}; };
} }
} }

View File

@@ -3,18 +3,14 @@ import template from '%/directives/sync-resolution-menu.pug';
class SyncResolutionMenuCtrl { class SyncResolutionMenuCtrl {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$timeout, $timeout
archiveManager,
application
) { ) {
this.$timeout = $timeout; this.$timeout = $timeout;
this.archiveManager = archiveManager;
this.application = application;
this.status = {}; this.status = {};
} }
downloadBackup(encrypted) { downloadBackup(encrypted) {
this.archiveManager.downloadBackup(encrypted); this.application.getArchiveService().downloadBackup(encrypted);
this.status.backupFinished = true; this.status.backupFinished = true;
} }
@@ -38,7 +34,7 @@ class SyncResolutionMenuCtrl {
close() { close() {
this.$timeout(() => { this.$timeout(() => {
this.closeFunction()(); this.closeFunction();
}); });
} }
} }
@@ -51,7 +47,8 @@ export class SyncResolutionMenu {
this.controllerAs = 'ctrl'; this.controllerAs = 'ctrl';
this.bindToController = true; this.bindToController = true;
this.scope = { this.scope = {
closeFunction: '&' closeFunction: '&',
application: '='
}; };
} }
} }

View File

@@ -1,16 +1,10 @@
import { EncryptionIntents, ProtectedActions } from 'snjs'; import { EncryptionIntents, ProtectedActions } from 'snjs';
export class ArchiveManager { export class ArchiveManager {
/* @ngInject */ constructor(application) {
constructor(lockManager, application, godService) {
this.lockManager = lockManager;
this.application = application; this.application = application;
this.godService = godService;
} }
/*
Public
*/
/** @public */ /** @public */
async downloadBackup(encrypted) { async downloadBackup(encrypted) {
return this.downloadBackupOfItems(this.application.modelManager.allItems, encrypted); return this.downloadBackupOfItems(this.application.modelManager.allItems, encrypted);
@@ -37,7 +31,7 @@ export class ArchiveManager {
}; };
if (await this.application.privilegesService.actionRequiresPrivilege(ProtectedActions.ManageBackups)) { if (await this.application.privilegesService.actionRequiresPrivilege(ProtectedActions.ManageBackups)) {
this.godService.presentPrivilegesModal(ProtectedActions.ManageBackups, () => { this.application.presentPrivilegesModal(ProtectedActions.ManageBackups, () => {
run(); run();
}); });
} else { } else {

View File

@@ -9,23 +9,25 @@ const COMPONENT_CONTENT_KEY_PACKAGE_INFO = 'package_info';
const COMPONENT_CONTENT_KEY_LOCAL_URL = 'local_url'; const COMPONENT_CONTENT_KEY_LOCAL_URL = 'local_url';
export class DesktopManager extends ApplicationService { export class DesktopManager extends ApplicationService {
/* @ngInject */
constructor( constructor(
$rootScope, $rootScope,
$timeout, $timeout,
application, application
appState,
) { ) {
super(application); super(application);
this.$rootScope = $rootScope; this.$rootScope = $rootScope;
this.$timeout = $timeout; this.$timeout = $timeout;
this.appState = appState;
this.application = application;
this.componentActivationObservers = []; this.componentActivationObservers = [];
this.updateObservers = []; this.updateObservers = [];
this.isDesktop = isDesktopApplication(); this.isDesktop = isDesktopApplication();
} }
deinit() {
this.componentActivationObservers.length = 0;
this.updateObservers.length = 0;
super.deinit();
}
/** @override */ /** @override */
onAppEvent(eventName) { onAppEvent(eventName) {
super.onAppEvent(eventName); super.onAppEvent(eventName);
@@ -177,7 +179,7 @@ export class DesktopManager extends ApplicationService {
/* Used to resolve 'sn://' */ /* Used to resolve 'sn://' */
desktop_setExtServerHost(host) { desktop_setExtServerHost(host) {
this.extServerHost = host; this.extServerHost = host;
this.appState.desktopExtensionsReady(); this.application.getAppState().desktopExtensionsReady();
} }
desktop_setComponentInstallationSyncHandler(handler) { desktop_setComponentInstallationSyncHandler(handler) {
@@ -207,11 +209,11 @@ export class DesktopManager extends ApplicationService {
} }
desktop_didBeginBackup() { desktop_didBeginBackup() {
this.appState.beganBackupDownload(); this.application.getAppState().beganBackupDownload();
} }
desktop_didFinishBackup(success) { desktop_didFinishBackup(success) {
this.appState.endedBackupDownload({ this.application.getAppState().endedBackupDownload({
success: success success: success
}); });
} }

View File

@@ -1,114 +0,0 @@
import angular from 'angular';
export class GodService {
/* @ngInject */
constructor(
$rootScope,
$compile,
application
) {
this.$rootScope = $rootScope;
this.$compile = $compile;
this.application = application;
}
async checkForSecurityUpdate() {
return this.application.protocolUpgradeAvailable();
}
presentPasswordWizard(type) {
const scope = this.$rootScope.$new(true);
scope.type = type;
const el = this.$compile("<password-wizard type='type'></password-wizard>")(scope);
angular.element(document.body).append(el);
}
promptForChallenge(challenge, orchestrator) {
const scope = this.$rootScope.$new(true);
scope.challenge = challenge;
scope.orchestrator = orchestrator;
const el = this.$compile(
"<challenge-modal " +
"class='sk-modal' challenge='challenge' orchestrator='orchestrator'>" +
"</challenge-modal>"
)(scope);
angular.element(document.body).append(el);
}
async performProtocolUpgrade() {
const errors = await this.application.upgradeProtocolVersion();
if (errors.length === 0) {
this.application.alertService.alert({
text: "Success! Your encryption version has been upgraded." +
" You'll be asked to enter your credentials again on other devices you're signed into."
});
} else {
this.application.alertService.alert({
text: "Unable to upgrade encryption version. Please try again."
});
}
}
async presentPrivilegesModal(action, onSuccess, onCancel) {
if (this.authenticationInProgress()) {
onCancel && onCancel();
return;
}
const customSuccess = async () => {
onSuccess && await onSuccess();
this.currentAuthenticationElement = null;
};
const customCancel = async () => {
onCancel && await onCancel();
this.currentAuthenticationElement = null;
};
const scope = this.$rootScope.$new(true);
scope.action = action;
scope.onSuccess = customSuccess;
scope.onCancel = customCancel;
const el = this.$compile(`
<privileges-auth-modal action='action' on-success='onSuccess'
on-cancel='onCancel' class='sk-modal'></privileges-auth-modal>
`)(scope);
angular.element(document.body).append(el);
this.currentAuthenticationElement = el;
}
presentPrivilegesManagementModal() {
var scope = this.$rootScope.$new(true);
var el = this.$compile("<privileges-management-modal class='sk-modal'></privileges-management-modal>")(scope);
angular.element(document.body).append(el);
}
authenticationInProgress() {
return this.currentAuthenticationElement != null;
}
presentPasswordModal(callback) {
const scope = this.$rootScope.$new(true);
scope.type = "password";
scope.title = "Decryption Assistance";
scope.message = `Unable to decrypt this item with your current keys.
Please enter your account password at the time of this revision.`;
scope.callback = callback;
const el = this.$compile(
`<input-modal type='type' message='message'
title='title' callback='callback'></input-modal>`
)(scope);
angular.element(document.body).append(el);
}
presentRevisionPreviewModal(uuid, content) {
const scope = this.$rootScope.$new(true);
scope.uuid = uuid;
scope.content = content;
const el = this.$compile(
`<revision-preview-modal uuid='uuid' content='content'
class='sk-modal'></revision-preview-modal>`
)(scope);
angular.element(document.body).append(el);
}
}

View File

@@ -1,10 +1,10 @@
export { AlertService } from './alertService'; export { AlertService } from './alertService';
export { ArchiveManager } from './archiveManager'; export { ArchiveManager } from './archiveManager';
export { DesktopManager } from './desktopManager'; export { DesktopManager } from './desktopManager';
export { GodService } from './godService';
export { KeyboardManager } from './keyboardManager'; export { KeyboardManager } from './keyboardManager';
export { LockManager } from './lockManager'; export { LockManager } from './lockManager';
export { NativeExtManager } from './nativeExtManager'; export { NativeExtManager } from './nativeExtManager';
export { PreferencesManager } from './preferencesManager'; export { PreferencesManager } from './preferencesManager';
export { StatusManager } from './statusManager'; export { StatusManager } from './statusManager';
export { ThemeManager } from './themeManager'; export { ThemeManager } from './themeManager';
export { AppState } from './state';

View File

@@ -22,8 +22,19 @@ const KeyboardKeyEvents = {
export class KeyboardManager { export class KeyboardManager {
constructor() { constructor() {
this.observers = []; this.observers = [];
window.addEventListener('keydown', this.handleKeyDown.bind(this)); this.handleKeyDown = this.handleKeyDown.bind(this);
window.addEventListener('keyup', this.handleKeyUp.bind(this)); this.handleKeyUp = this.handleKeyUp.bind(this);
window.addEventListener('keydown', this.handleKeyDown);
window.addEventListener('keyup', this.handleKeyUp);
}
/** @access public */
deinit() {
this.observers.length = 0;
window.removeEventListener('keydown', this.handleKeyDown);
window.removeEventListener('keyup', this.handleKeyUp);
this.handleKeyDown = null;
this.handleKeyUp = null;
} }
modifiersForEvent(event) { modifiersForEvent(event) {

View File

@@ -1,5 +1,5 @@
import { isDesktopApplication } from '@/utils'; import { isDesktopApplication } from '@/utils';
import { AppStateEvents } from '../state'; import { AppStateEvents } from '@/services/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;
@@ -12,16 +12,15 @@ const LOCK_INTERVAL_ONE_HOUR= 3600 * MILLISECONDS_PER_SECOND;
const STORAGE_KEY_AUTOLOCK_INTERVAL = "AutoLockIntervalKey"; const STORAGE_KEY_AUTOLOCK_INTERVAL = "AutoLockIntervalKey";
export class LockManager { export class LockManager {
/* @ngInject */ constructor(application) {
constructor($rootScope, application, appState) {
this.$rootScope = $rootScope;
this.application = application; this.application = application;
this.appState = appState; setImmediate(() => {
this.observeVisibility(); this.observeVisibility();
});
} }
observeVisibility() { observeVisibility() {
this.appState.addObserver((eventName, data) => { this.unsubState = this.application.getAppState().addObserver((eventName) => {
if(eventName === AppStateEvents.WindowDidBlur) { if(eventName === AppStateEvents.WindowDidBlur) {
this.documentVisibilityChanged(false); this.documentVisibilityChanged(false);
} else if(eventName === AppStateEvents.WindowDidFocus) { } else if(eventName === AppStateEvents.WindowDidFocus) {
@@ -33,6 +32,13 @@ export class LockManager {
} }
} }
deinit() {
this.unsubState();
if (this.pollFocusInterval) {
clearInterval(this.pollFocusInterval);
}
}
async setAutoLockInterval(interval) { async setAutoLockInterval(interval) {
return this.application.setValue(STORAGE_KEY_AUTOLOCK_INTERVAL, interval); return this.application.setValue(STORAGE_KEY_AUTOLOCK_INTERVAL, interval);
} }
@@ -53,7 +59,7 @@ export class LockManager {
* not triggered on a typical window blur event but rather on tab changes. * not triggered on a typical window blur event but rather on tab changes.
*/ */
beginWebFocusPolling() { beginWebFocusPolling() {
this.pollFocusTimeout = setInterval(() => { this.pollFocusInterval = setInterval(() => {
const hasFocus = document.hasFocus(); const hasFocus = document.hasFocus();
if(hasFocus && this.lastFocusState === 'hidden') { if(hasFocus && this.lastFocusState === 'hidden') {
this.documentVisibilityChanged(true); this.documentVisibilityChanged(true);

View File

@@ -13,7 +13,6 @@ export class NativeExtManager extends ApplicationService {
/* @ngInject */ /* @ngInject */
constructor(application) { constructor(application) {
super(application); super(application);
this.application = application;
this.extManagerId = 'org.standardnotes.extensions-manager'; this.extManagerId = 'org.standardnotes.extensions-manager';
this.batchManagerId = 'org.standardnotes.batch-manager'; this.batchManagerId = 'org.standardnotes.batch-manager';
} }

View File

@@ -1,5 +1,4 @@
import { import {
ApplicationEvents,
SNPredicate, SNPredicate,
ContentTypes, ContentTypes,
CreateMaxPayloadFromAnyObject, CreateMaxPayloadFromAnyObject,
@@ -24,15 +23,7 @@ export const PrefKeys = {
}; };
export class PreferencesManager extends ApplicationService { export class PreferencesManager extends ApplicationService {
/* @ngInject */
constructor(
appState,
application
) {
super(application);
this.appState = appState;
}
/** @override */ /** @override */
onAppLaunch() { onAppLaunch() {
super.onAppLaunch(); super.onAppLaunch();
@@ -65,7 +56,7 @@ export class PreferencesManager extends ApplicationService {
} }
preferencesDidChange() { preferencesDidChange() {
this.appState.setUserPreferences(this.userPreferences); this.application.getAppState().setUserPreferences(this.userPreferences);
} }
syncUserPreferences() { syncUserPreferences() {

View File

@@ -23,21 +23,33 @@ export const EventSources = {
export class AppState { export class AppState {
/* @ngInject */ /* @ngInject */
constructor( constructor(
$timeout,
$rootScope, $rootScope,
application, $timeout,
godService application
) { ) {
this.$timeout = $timeout; this.$timeout = $timeout;
this.$rootScope = $rootScope; this.$rootScope = $rootScope;
this.application = application; this.application = application;
this.godService = godService;
this.observers = []; this.observers = [];
this.locked = true; this.locked = true;
this.registerVisibilityObservers(); this.registerVisibilityObservers();
this.addAppEventObserver(); this.addAppEventObserver();
} }
deinit() {
this.unsubApp();
this.unsubApp = null;
this.observers.length = 0;
if(this.rootScopeCleanup1) {
this.rootScopeCleanup1();
this.rootScopeCleanup2();
this.rootScopeCleanup1 = null;
this.rootScopeCleanup2 = null;
}
document.removeEventListener('visibilitychange', this.onVisibilityChange);
this.onVisibilityChange = null;
}
addAppEventObserver() { addAppEventObserver() {
this.unsubApp = this.application.addEventObserver(async (eventName) => { this.unsubApp = this.application.addEventObserver(async (eventName) => {
if (eventName === ApplicationEvents.Started) { if (eventName === ApplicationEvents.Started) {
@@ -54,23 +66,26 @@ export class AppState {
registerVisibilityObservers() { registerVisibilityObservers() {
if (isDesktopApplication()) { if (isDesktopApplication()) {
this.$rootScope.$on('window-lost-focus', () => { this.rootScopeCleanup1 = this.$rootScope.$on('window-lost-focus', () => {
this.notifyEvent(AppStateEvents.WindowDidBlur); this.notifyEvent(AppStateEvents.WindowDidBlur);
}); });
this.$rootScope.$on('window-gained-focus', () => { this.rootScopeCleanup2 = this.$rootScope.$on('window-gained-focus', () => {
this.notifyEvent(AppStateEvents.WindowDidFocus); this.notifyEvent(AppStateEvents.WindowDidFocus);
}); });
} else { } else {
/* Tab visibility listener, web only */ /* Tab visibility listener, web only */
document.addEventListener('visibilitychange', (e) => { this.onVisibilityChange = this.onVisibilityChange.bind(this);
const visible = document.visibilityState === "visible"; document.addEventListener('visibilitychange', this.onVisibilityChange);
const event = visible
? AppStateEvents.WindowDidFocus
: AppStateEvents.WindowDidBlur;
this.notifyEvent(event);
});
} }
} }
onVisibilityChange() {
const visible = document.visibilityState === "visible";
const event = visible
? AppStateEvents.WindowDidFocus
: AppStateEvents.WindowDidBlur;
this.notifyEvent(event);
}
/** @returns A function that unregisters this observer */ /** @returns A function that unregisters this observer */
addObserver(callback) { addObserver(callback) {
@@ -120,10 +135,10 @@ export class AppState {
); );
}; };
if (note && note.content.protected && if (note && note.content.protected &&
await this.application.privilegesService.actionRequiresPrivilege( await this.application.application.privilegesService.actionRequiresPrivilege(
ProtectedActions.ViewProtectedNotes ProtectedActions.ViewProtectedNotes
)) { )) {
this.godService.presentPrivilegesModal( this.application.presentPrivilegesModal(
ProtectedActions.ViewProtectedNotes, ProtectedActions.ViewProtectedNotes,
run run
); );

View File

@@ -5,33 +5,35 @@ import {
EncryptionIntents, EncryptionIntents,
ApplicationService, ApplicationService,
} from 'snjs'; } from 'snjs';
import { AppStateEvents } from '@/state'; import { AppStateEvents } from '@/services/state';
const CACHED_THEMES_KEY = 'cachedThemes'; const CACHED_THEMES_KEY = 'cachedThemes';
export class ThemeManager extends ApplicationService { export class ThemeManager extends ApplicationService {
/* @ngInject */ constructor(application) {
constructor(
application,
appState,
desktopManager,
) {
super(application); super(application);
this.appState = appState;
this.desktopManager = desktopManager;
this.activeThemes = []; this.activeThemes = [];
this.unsubState = appState.addObserver((eventName, data) => { setImmediate(() => {
if (eventName === AppStateEvents.DesktopExtsReady) { this.unsubState = this.application.getAppState().addObserver((eventName, data) => {
this.activateCachedThemes(); if (eventName === AppStateEvents.DesktopExtsReady) {
} this.activateCachedThemes();
}
});
}); });
} }
deinit() {
this.unsubState();
this.unsubState = null;
this.activeThemes.length = 0;
super.deinit();
}
/** @override */ /** @override */
onAppStart() { onAppStart() {
super.onAppStart(); super.onAppStart();
this.registerObservers(); this.registerObservers();
if (!this.desktopManager.isDesktop) { if (!this.application.getDesktopService().isDesktop) {
this.activateCachedThemes(); this.activateCachedThemes();
} }
} }
@@ -52,7 +54,7 @@ export class ThemeManager extends ApplicationService {
} }
registerObservers() { registerObservers() {
this.desktopManager.registerUpdateObserver((component) => { this.application.getDesktopService().registerUpdateObserver((component) => {
if (component.active && component.isTheme()) { if (component.active && component.isTheme()) {
this.deactivateTheme(component); this.deactivateTheme(component);
setTimeout(() => { setTimeout(() => {

View File

@@ -36,7 +36,7 @@ export const STRING_E2E_ENABLED = "End-to-end encryption is enabled. Your data i
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 application 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.";

View File

@@ -0,0 +1,15 @@
.main-ui-view(
ng-class='self.platformString'
)
#app.app(
ng-class='self.state.appClass',
ng-if='!self.state.needsUnlock && self.state.ready'
)
tags-panel(application='self.application')
notes-panel(application='self.application')
editor-panel(application='self.application')
footer(
ng-if='!self.state.needsUnlock && self.state.ready'
application='self.application'
)

View File

@@ -11,5 +11,6 @@
| {{ctrl.component.name}} | {{ctrl.component.name}}
a.sk-a.info.close-button(ng-click="ctrl.dismiss()") Close a.sk-a.info.close-button(ng-click="ctrl.dismiss()") Close
component-view.component-view( component-view.component-view(
component="ctrl.component" component="ctrl.component",
application='self.application'
) )

View File

@@ -46,6 +46,6 @@
| an unlocked application, but do not affect data encryption state. | an unlocked application, but do not affect data encryption state.
p.sk-p p.sk-p
| Privileges sync across your other devices; however, note that if you | Privileges sync across your other devices; however, note that if you
| require a "Local Passcode" privilege, and another device does not have | require an "Application Passcode" privilege, and another device does not have
| a local passcode set up, the local passcode requirement will be ignored | an application passcode set up, the application passcode requirement will be ignored
| on that device. | on that device.

View File

@@ -23,5 +23,6 @@
) {{ctrl.content.text}} ) {{ctrl.content.text}}
component-view.component-view( component-view.component-view(
component="ctrl.editor" component="ctrl.editor"
ng-if="ctrl.editor" ng-if="ctrl.editor",
application='self.application'
) )

View File

@@ -165,7 +165,8 @@
callback='self.editorMenuOnSelect', callback='self.editorMenuOnSelect',
current-item='self.state.note', current-item='self.state.note',
ng-if='self.state.showEditorMenu', ng-if='self.state.showEditorMenu',
selected-editor='self.state.selectedEditor' selected-editor='self.state.selectedEditor',
application='self.application'
) )
.sk-app-bar-item( .sk-app-bar-item(
click-outside=`self.setMenuState('showExtensions', false)`, click-outside=`self.setMenuState('showExtensions', false)`,
@@ -176,7 +177,8 @@
.sk-label Actions .sk-label Actions
actions-menu( actions-menu(
item='self.state.note', item='self.state.note',
ng-if='self.state.showExtensions' ng-if='self.state.showExtensions',
application='self.application'
) )
.sk-app-bar-item( .sk-app-bar-item(
click-outside=`self.setMenuState('showSessionHistory', false)`, click-outside=`self.setMenuState('showSessionHistory', false)`,
@@ -186,7 +188,8 @@
.sk-label Session History .sk-label Session History
session-history-menu( session-history-menu(
item='self.state.note', item='self.state.note',
ng-if='self.state.showSessionHistory' ng-if='self.state.showSessionHistory',
application='self.application'
) )
#editor-content.editor-content( #editor-content.editor-content(
ng-if='self.state.noteReady && !self.state.note.errorDecrypting' ng-if='self.state.noteReady && !self.state.note.errorDecrypting'
@@ -196,14 +199,15 @@
hoverable='true', hoverable='true',
min-width='300', min-width='300',
ng-if='self.state.marginResizersEnabled', ng-if='self.state.marginResizersEnabled',
on-resize-finish='self.onPanelResizeFinish', on-resize-finish='self.onPanelResizeFinish()',
panel-id="'editor-content'", panel-id="'editor-content'",
property="'left'" property="'left'"
) )
component-view.component-view( component-view.component-view(
component='self.state.selectedEditor', component='self.state.selectedEditor',
ng-if='self.state.selectedEditor', ng-if='self.state.selectedEditor',
on-load='self.onEditorLoad' on-load='self.onEditorLoad',
application='self.application'
) )
textarea#note-text-editor.editable( textarea#note-text-editor.editable(
dir='auto', dir='auto',
@@ -222,7 +226,7 @@
control='self.rightPanelPuppet', control='self.rightPanelPuppet',
hoverable='true', min-width='300', hoverable='true', min-width='300',
ng-if='self.state.marginResizersEnabled', ng-if='self.state.marginResizersEnabled',
on-resize-finish='self.onPanelResizeFinish', on-resize-finish='self.onPanelResizeFinish()',
panel-id="'editor-content'", panel-id="'editor-content'",
property="'right'" property="'right'"
) )
@@ -249,5 +253,6 @@
manual-dealloc='true', manual-dealloc='true',
ng-if='component.active', ng-if='component.active',
ng-repeat='component in self.state.componentStack', ng-repeat='component in self.state.componentStack',
ng-show='!component.hidden' ng-show='!component.hidden',
application='self.application'
) )

View File

@@ -2,7 +2,7 @@
#footer-bar.sk-app-bar.no-edges.no-bottom-edge #footer-bar.sk-app-bar.no-edges.no-bottom-edge
.left .left
.sk-app-bar-item( .sk-app-bar-item(
click-outside='ctrl.clickOutsideAccountMenu()', click-outside='ctrl.clickOutsideAccountMenu()',
is-open='ctrl.showAccountMenu', is-open='ctrl.showAccountMenu',
ng-click='ctrl.accountMenuPressed()' ng-click='ctrl.accountMenuPressed()'
) )
@@ -13,9 +13,10 @@
.sk-app-bar-item-column .sk-app-bar-item-column
.sk-label.title(ng-class='{red: ctrl.error}') Account .sk-label.title(ng-class='{red: ctrl.error}') Account
account-menu( account-menu(
close-function='ctrl.closeAccountMenu', close-function='ctrl.closeAccountMenu()',
ng-click='$event.stopPropagation()', ng-click='$event.stopPropagation()',
ng-if='ctrl.showAccountMenu', ng-if='ctrl.showAccountMenu',
application='ctrl.application'
) )
.sk-app-bar-item .sk-app-bar-item
a.no-decoration.sk-label.title( a.no-decoration.sk-label.title(
@@ -31,21 +32,22 @@
component-modal( component-modal(
component='room', component='room',
ng-if='room.showRoom', ng-if='room.showRoom',
on-dismiss='ctrl.onRoomDismiss' on-dismiss='ctrl.onRoomDismiss()',
application='self.application'
) )
.center .center
.sk-app-bar-item(ng-show='ctrl.arbitraryStatusMessage') .sk-app-bar-item(ng-if='ctrl.arbitraryStatusMessage')
.sk-app-bar-item-column .sk-app-bar-item-column
span.neutral.sk-label {{ctrl.arbitraryStatusMessage}} span.neutral.sk-label {{ctrl.arbitraryStatusMessage}}
.right .right
.sk-app-bar-item( .sk-app-bar-item(
ng-click='ctrl.openSecurityUpdate()', ng-click='ctrl.openSecurityUpdate()',
ng-show='ctrl.state.dataUpgradeAvailable' ng-if='ctrl.state.dataUpgradeAvailable'
) )
span.success.sk-label Encryption upgrade available. span.success.sk-label Encryption upgrade available.
.sk-app-bar-item( .sk-app-bar-item(
ng-click='ctrl.clickedNewUpdateAnnouncement()', ng-click='ctrl.clickedNewUpdateAnnouncement()',
ng-show='ctrl.newUpdateAvailable == true' ng-if='ctrl.newUpdateAvailable == true'
) )
span.info.sk-label New update available. span.info.sk-label New update available.
.sk-app-bar-item.no-pointer( .sk-app-bar-item.no-pointer(
@@ -59,9 +61,10 @@
) )
.sk-label.warning(ng-if='ctrl.state.outOfSync') Potentially Out of Sync .sk-label.warning(ng-if='ctrl.state.outOfSync') Potentially Out of Sync
sync-resolution-menu( sync-resolution-menu(
close-function='ctrl.toggleSyncResolutionMenu', close-function='ctrl.toggleSyncResolutionMenu()',
ng-click='$event.stopPropagation();', ng-click='$event.stopPropagation();',
ng-if='ctrl.showSyncResolution' ng-if='ctrl.showSyncResolution',
application='self.application'
) )
.sk-app-bar-item(ng-if='ctrl.lastSyncDate && ctrl.isRefreshing') .sk-app-bar-item(ng-if='ctrl.lastSyncDate && ctrl.isRefreshing')
.sk-spinner.small .sk-spinner.small

View File

@@ -26,7 +26,7 @@
) Forgot? ) Forgot?
div(ng-if='ctrl.formData.showRecovery') div(ng-if='ctrl.formData.showRecovery')
.sk-p .sk-p
| If you forgot your local passcode, your only option is to clear | If you forgot your application passcode, your only option is to clear
| your local data from this device and sign back in to your account. | your local data from this device and sign back in to your account.
.sk-panel-row .sk-panel-row
a.sk-a.danger.center-text( a.sk-a.danger.center-text(

View File

@@ -145,6 +145,6 @@
control="self.panelPuppet" control="self.panelPuppet"
default-width="300" default-width="300"
hoverable="true" hoverable="true"
on-resize-finish="self.onPanelResize" on-resize-finish="self.onPanelResize()"
panel-id="'notes-column'" panel-id="'notes-column'"
) )

View File

@@ -1,14 +1,4 @@
.main-ui-view( application-view(
ng-class='self.platformString' ng-repeat='application in self.applications',
) application='application'
#app.app( )
ng-class='self.state.appClass',
ng-if='!self.state.needsUnlock && self.state.ready'
)
tags-panel
notes-panel
editor-panel
footer(
ng-if='!self.state.needsUnlock && self.state.ready'
)

View File

@@ -1,6 +1,9 @@
#tags-column.sn-component.section.tags(aria-label='Tags') #tags-column.sn-component.section.tags(aria-label='Tags')
.component-view-container(ng-if='self.component.active') .component-view-container(ng-if='self.component.active')
component-view.component-view(component='self.component') component-view.component-view(
component='self.component',
application='self.application'
)
#tags-content.content(ng-if='!(self.component && self.component.active)') #tags-content.content(ng-if='!(self.component && self.component.active)')
.tags-title-section.section-title-bar .tags-title-section.section-title-bar
.section-title-bar-header .section-title-bar-header
@@ -60,6 +63,6 @@
control='self.panelPuppet', control='self.panelPuppet',
default-width='150', default-width='150',
hoverable='true', hoverable='true',
on-resize-finish='self.onPanelResize', on-resize-finish='self.onPanelResize()',
panel-id="'tags-column'" panel-id="'tags-column'"
) )

6944
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

View File

@@ -1,2 +1,85 @@
(window.webpackJsonpSNCrypto=window.webpackJsonpSNCrypto||[]).push([[0],{125:function(t,c){},126:function(t,c){},168:function(t,c,n){"use strict";n.r(c);var o=n(21);n.d(c,"ready",(function(){return o.ready})),n.d(c,"crypto_pwhash",(function(){return o.crypto_pwhash})),n.d(c,"crypto_pwhash_ALG_DEFAULT",(function(){return o.crypto_pwhash_ALG_DEFAULT})),n.d(c,"crypto_aead_xchacha20poly1305_ietf_encrypt",(function(){return o.crypto_aead_xchacha20poly1305_ietf_encrypt})),n.d(c,"crypto_aead_xchacha20poly1305_ietf_decrypt",(function(){return o.crypto_aead_xchacha20poly1305_ietf_decrypt}))},51:function(t,c){},91:function(t,c){},93:function(t,c){}}]); (window["webpackJsonpSNCrypto"] = window["webpackJsonpSNCrypto"] || []).push([["libsodium"],{
/***/ "./lib/libsodium.js":
/*!**************************!*\
!*** ./lib/libsodium.js ***!
\**************************/
/*! exports provided: ready, crypto_pwhash, crypto_pwhash_ALG_DEFAULT, crypto_aead_xchacha20poly1305_ietf_encrypt, crypto_aead_xchacha20poly1305_ietf_decrypt */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var libsodium_wrappers__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! libsodium-wrappers */ "./node_modules/libsodium-wrappers/dist/modules/libsodium-wrappers.js");
/* harmony import */ var libsodium_wrappers__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(libsodium_wrappers__WEBPACK_IMPORTED_MODULE_0__);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ready", function() { return libsodium_wrappers__WEBPACK_IMPORTED_MODULE_0__["ready"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "crypto_pwhash", function() { return libsodium_wrappers__WEBPACK_IMPORTED_MODULE_0__["crypto_pwhash"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "crypto_pwhash_ALG_DEFAULT", function() { return libsodium_wrappers__WEBPACK_IMPORTED_MODULE_0__["crypto_pwhash_ALG_DEFAULT"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "crypto_aead_xchacha20poly1305_ietf_encrypt", function() { return libsodium_wrappers__WEBPACK_IMPORTED_MODULE_0__["crypto_aead_xchacha20poly1305_ietf_encrypt"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "crypto_aead_xchacha20poly1305_ietf_decrypt", function() { return libsodium_wrappers__WEBPACK_IMPORTED_MODULE_0__["crypto_aead_xchacha20poly1305_ietf_decrypt"]; });
/* eslint-disable camelcase */
/***/ }),
/***/ 0:
/*!********************!*\
!*** fs (ignored) ***!
\********************/
/*! no static exports found */
/***/ (function(module, exports) {
/* (ignored) */
/***/ }),
/***/ 1:
/*!**********************!*\
!*** util (ignored) ***!
\**********************/
/*! no static exports found */
/***/ (function(module, exports) {
/* (ignored) */
/***/ }),
/***/ 2:
/*!**********************!*\
!*** util (ignored) ***!
\**********************/
/*! no static exports found */
/***/ (function(module, exports) {
/* (ignored) */
/***/ }),
/***/ 3:
/*!************************!*\
!*** buffer (ignored) ***!
\************************/
/*! no static exports found */
/***/ (function(module, exports) {
/* (ignored) */
/***/ }),
/***/ 4:
/*!************************!*\
!*** crypto (ignored) ***!
\************************/
/*! no static exports found */
/***/ (function(module, exports) {
/* (ignored) */
/***/ })
}]);
//# sourceMappingURL=libsodium.bundle.js.map //# sourceMappingURL=libsodium.bundle.js.map

File diff suppressed because one or more lines are too long