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

View File

@@ -4,13 +4,14 @@ import {
Environments,
platformFromString
} from 'snjs';
import angular from 'angular';
import { getPlatformString } from '@/utils';
import { AlertService } from '@/services/alertService';
import { WebDeviceInterface } from '@/web_device_interface';
export class Application extends SNApplication {
export class WebApplication extends SNApplication {
/* @ngInject */
constructor($timeout) {
constructor($compile, $timeout, scope, onDeinit) {
const deviceInterface = new WebDeviceInterface({ timeout: $timeout });
super({
environment: Environments.Web,
@@ -25,6 +26,189 @@ export class Application extends SNApplication {
}
]
});
this.$compile = $compile;
this.scope = scope;
this.onDeinit = onDeinit;
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 */
constructor(
$scope,
$timeout,
application,
appState
) {
if (!$scope || !$timeout || !application || !appState) {
throw 'Invalid PureCtrl construction.';
constructor($timeout) {
if(!$timeout) {
throw Error('$timeout must not be null');
}
super(application);
this.$scope = $scope;
this.$timeout = $timeout;
this.appState = appState;
this.props = {};
this.state = {};
/* Allow caller constructor to finish setting instance variables */
setImmediate(() => {
this.state = this.getInitialState();
});
$scope.$on('$destroy', () => {
this.unsubState();
this.deinit();
});
}
$onInit() {
this.addAppEventObserver();
this.addAppStateObserver();
}
$onInit() {
this.addAppStateObserver();
deinit() {
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 */
@@ -43,8 +47,11 @@ export class PureCtrl extends ApplicationService {
}
async setState(state) {
if(!this.$timeout) {
return;
}
return new Promise((resolve) => {
this.$timeout(() => {
this.stateTimeout = this.$timeout(() => {
this.state = Object.freeze(Object.assign({}, this.state, state));
resolve();
});
@@ -59,7 +66,7 @@ export class PureCtrl extends ApplicationService {
}
addAppStateObserver() {
this.unsubState = this.appState.addObserver((eventName, data) => {
this.unsubState = this.application.getAppState().addObserver((eventName, data) => {
this.onAppStateEvent(eventName, data);
});
}
@@ -68,9 +75,46 @@ export class PureCtrl extends ApplicationService {
/** 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 */
async onAppStart() {
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 template from '%/editor.pug';
import { PureCtrl } from '@Controllers';
import { AppStateEvents, EventSources } from '@/state';
import { AppStateEvents, EventSources } from '@/services/state';
import {
STRING_DELETED_NOTE,
STRING_INVALID_NOTE,
@@ -48,35 +48,34 @@ const Fonts = {
class EditorCtrl extends PureCtrl {
/* @ngInject */
constructor(
$scope,
$timeout,
$rootScope,
application,
appState,
desktopManager,
keyboardManager,
preferencesManager,
) {
super($scope, $timeout, application, appState);
this.$rootScope = $rootScope;
this.desktopManager = desktopManager;
this.keyboardManager = keyboardManager;
this.preferencesManager = preferencesManager;
constructor($timeout) {
super($timeout);
this.leftPanelPuppet = {
onReady: () => this.reloadPreferences()
};
this.rightPanelPuppet = {
onReady: () => this.reloadPreferences()
};
this.addSyncStatusObserver();
this.registerKeyboardShortcuts();
/** Used by .pug template */
this.prefKeyMonospace = PrefKeys.EditorMonospaceEnabled;
this.prefKeySpellcheck = PrefKeys.EditorSpellcheck;
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 */
getInitialState() {
return {
@@ -100,7 +99,7 @@ class EditorCtrl extends PureCtrl {
onAppStateEvent(eventName, data) {
if (eventName === AppStateEvents.NoteChanged) {
this.handleNoteSelectionChange(
this.appState.getSelectedNote(),
this.application.getAppState().getSelectedNote(),
data.previousNote
);
} else if (eventName === AppStateEvents.PreferencesChanged) {
@@ -209,7 +208,7 @@ class EditorCtrl extends PureCtrl {
async handleNoteSelectionChange(note, previousNote) {
this.setState({
note: this.appState.getSelectedNote(),
note: this.application.getAppState().getSelectedNote(),
showExtensions: false,
showOptionsMenu: false,
altKeyDown: false,
@@ -504,7 +503,7 @@ class EditorCtrl extends PureCtrl {
}
onContentFocus() {
this.appState.editorDidFocus(this.lastEditorFocusEventSource);
this.application.getAppState().editorDidFocus(this.lastEditorFocusEventSource);
this.lastEditorFocusEventSource = null;
}
@@ -552,7 +551,7 @@ class EditorCtrl extends PureCtrl {
dontUpdatePreviews: true
});
}
this.appState.setSelectedNote(null);
this.application.getAppState().setSelectedNote(null);
this.setMenuState('showOptionsMenu', false);
}
});
@@ -561,7 +560,7 @@ class EditorCtrl extends PureCtrl {
ProtectedActions.DeleteNote
);
if (requiresPrivilege) {
this.godService.presentPrivilegesModal(
this.application.presentPrivilegesModal(
ProtectedActions.DeleteNote,
() => {
run();
@@ -592,7 +591,7 @@ class EditorCtrl extends PureCtrl {
bypassDebouncer: true,
dontUpdatePreviews: true
});
this.appState.setSelectedNote(null);
this.application.getAppState().setSelectedNote(null);
}
deleteNotePermanantely() {
@@ -649,7 +648,7 @@ class EditorCtrl extends PureCtrl {
ProtectedActions.ViewProtectedNotes
).then((configured) => {
if (!configured) {
this.godService.presentPrivilegesManagementModal();
this.application.presentPrivilegesManagementModal();
}
});
}
@@ -743,13 +742,13 @@ class EditorCtrl extends PureCtrl {
onPanelResizeFinish = (width, left, isMaxWidth) => {
if (isMaxWidth) {
this.preferencesManager.setUserPrefValue(
this.application.getPrefsService().setUserPrefValue(
PrefKeys.EditorWidth,
null
);
} else {
if (width !== undefined && width !== null) {
this.preferencesManager.setUserPrefValue(
this.application.getPrefsService().setUserPrefValue(
PrefKeys.EditorWidth,
width
);
@@ -757,25 +756,25 @@ class EditorCtrl extends PureCtrl {
}
}
if (left !== undefined && left !== null) {
this.preferencesManager.setUserPrefValue(
this.application.getPrefsService().setUserPrefValue(
PrefKeys.EditorLeft,
left
);
this.rightPanelPuppet.setLeft(left);
}
this.preferencesManager.syncUserPreferences();
this.application.getPrefsService().syncUserPreferences();
}
reloadPreferences() {
const monospaceEnabled = this.preferencesManager.getValue(
const monospaceEnabled = this.application.getPrefsService().getValue(
PrefKeys.EditorMonospaceEnabled,
true
);
const spellcheck = this.preferencesManager.getValue(
const spellcheck = this.application.getPrefsService().getValue(
PrefKeys.EditorSpellcheck,
true
);
const marginResizersEnabled = this.preferencesManager.getValue(
const marginResizersEnabled = this.application.getPrefsService().getValue(
PrefKeys.EditorResizersEnabled,
true
);
@@ -797,7 +796,7 @@ class EditorCtrl extends PureCtrl {
this.leftPanelPuppet.ready &&
this.rightPanelPuppet.ready
) {
const width = this.preferencesManager.getValue(
const width = this.application.getPrefsService().getValue(
PrefKeys.EditorWidth,
null
);
@@ -805,7 +804,7 @@ class EditorCtrl extends PureCtrl {
this.leftPanelPuppet.setWidth(width);
this.rightPanelPuppet.setWidth(width);
}
const left = this.preferencesManager.getValue(
const left = this.application.getPrefsService().getValue(
PrefKeys.EditorLeft,
null
);
@@ -836,7 +835,7 @@ class EditorCtrl extends PureCtrl {
async toggleKey(key) {
this[key] = !this[key];
this.preferencesManager.setUserPrefValue(
this.application.getPrefsService().setUserPrefValue(
key,
this[key],
true
@@ -863,7 +862,7 @@ class EditorCtrl extends PureCtrl {
/** @components */
onEditorLoad = (editor) => {
this.desktopManager.redoSearch();
this.application.getDesktopService().redoSearch();
}
registerComponentHandler() {
@@ -1047,7 +1046,7 @@ class EditorCtrl extends PureCtrl {
}
registerKeyboardShortcuts() {
this.altKeyObserver = this.keyboardManager.addKeyObserver({
this.altKeyObserver = this.application.getKeyboardService().addKeyObserver({
modifiers: [
KeyboardModifiers.Alt
],
@@ -1063,7 +1062,7 @@ class EditorCtrl extends PureCtrl {
}
});
this.trashKeyObserver = this.keyboardManager.addKeyObserver({
this.trashKeyObserver = this.application.getKeyboardService().addKeyObserver({
key: KeyboardKeys.Backspace,
notElementIds: [
ElementIds.NoteTextEditor,
@@ -1075,7 +1074,7 @@ class EditorCtrl extends PureCtrl {
},
});
this.deleteKeyObserver = this.keyboardManager.addKeyObserver({
this.deleteKeyObserver = this.application.getKeyboardService().addKeyObserver({
key: KeyboardKeys.Backspace,
modifiers: [
KeyboardModifiers.Meta,
@@ -1090,10 +1089,9 @@ class EditorCtrl extends PureCtrl {
}
onSystemEditorLoad() {
if (this.loadedTabListener) {
if (this.tabObserver) {
return;
}
this.loadedTabListener = true;
/**
* Insert 4 spaces when a tab key is pressed,
* only used when inside of the text editor.
@@ -1103,7 +1101,7 @@ class EditorCtrl extends PureCtrl {
const editor = document.getElementById(
ElementIds.NoteTextEditor
);
this.tabObserver = this.keyboardManager.addKeyObserver({
this.tabObserver = this.application.getKeyboardService().addKeyObserver({
element: editor,
key: KeyboardKeys.Tab,
onKeyDown: (event) => {
@@ -1144,19 +1142,29 @@ class EditorCtrl extends PureCtrl {
* Handles when the editor is destroyed,
* (and not when our controller is destroyed.)
*/
angular.element(editor).on('$destroy', () => {
if (this.tabObserver) {
this.keyboardManager.removeKeyObserver(this.tabObserver);
this.loadedTabListener = false;
}
angular.element(editor).one('$destroy', () => {
this.removeTabObserver();
});
};
}
removeTabObserver() {
if (!this.application) {
return;
}
const keyboardService = this.application.getKeyboardService();
if (this.tabObserver && keyboardService) {
keyboardService.removeKeyObserver(this.tabObserver);
this.tabObserver = null;
}
}
}
export class EditorPanel {
constructor() {
this.restrict = 'E';
this.scope = {};
this.scope = {
application: '='
};
this.template = template;
this.replace = true;
this.controller = EditorCtrl;

View File

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

View File

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

View File

@@ -1,258 +1,16 @@
import { getPlatformString } from '@/utils';
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 */
constructor(
$compile,
$location,
$scope,
$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()
constructor(applicationManager) {
this.applicationManager = applicationManager;
this.applicationManager.addApplicationChangeObserver(() => {
this.reload();
});
}
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.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,
});
reload() {
this.applications = this.applicationManager.getApplications();
}
}

View File

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

View File

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

View File

@@ -1,16 +1,20 @@
/* @ngInject */
export function infiniteScroll($rootScope, $window, $timeout) {
export function infiniteScroll() {
return {
link: function(scope, elem, attrs) {
const offset = parseInt(attrs.threshold) || 0;
const e = elem[0];
elem.on('scroll', function() {
scope.onScroll = () => {
if (
scope.$eval(attrs.canLoad) &&
e.scrollTop + e.offsetHeight >= e.scrollHeight - offset
) {
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 {
/* @ngInject */
constructor(
$scope,
$rootScope,
$timeout,
appVersion,
application,
appState,
archiveManager,
godService,
lockManager,
) {
super($scope, $timeout, application, appState);
this.$rootScope = $rootScope;
this.appState = appState;
this.application = application;
this.archiveManager = archiveManager;
this.godService = godService;
this.lockManager = lockManager;
super($timeout);
this.appVersion = appVersion;
this.syncStatus = this.application.getSyncStatus();
}
/** @override */
getInitialState() {
return {
appVersion: 'v' + (window.electronAppVersion || this.appVersion),
passcodeAutoLockOptions: this.lockManager.getAutoLockIntervalOptions(),
passcodeAutoLockOptions: this.application.getLockService().getAutoLockIntervalOptions(),
user: this.application.getUser(),
formData: {
mergeLocal: true,
@@ -90,11 +76,12 @@ class AccountMenuCtrl extends PureCtrl {
this.initProps({
closeFunction: this.closeFunction
});
this.syncStatus = this.application.getSyncStatus();
}
close() {
this.$timeout(() => {
this.props.closeFunction()();
this.props.closeFunction();
});
}
@@ -277,19 +264,19 @@ class AccountMenuCtrl extends PureCtrl {
openPasswordWizard() {
this.close();
this.godService.presentPasswordWizard();
this.application.presentPasswordWizard();
}
async openPrivilegesModal() {
this.close();
const run = () => {
this.godService.presentPrivilegesManagementModal();
this.application.presentPrivilegesManagementModal();
};
const needsPrivilege = await this.application.privilegesService.actionRequiresPrivilege(
ProtectedActions.ManagePrivileges
);
if (needsPrivilege) {
this.godService.presentPrivilegesModal(
this.application.presentPrivilegesModal(
ProtectedActions.ManagePrivileges,
() => {
run();
@@ -366,7 +353,7 @@ class AccountMenuCtrl extends PureCtrl {
ProtectedActions.ManageBackups
);
if (needsPrivilege) {
this.godService.presentPrivilegesModal(
this.application.presentPrivilegesModal(
ProtectedActions.ManageBackups,
run
);
@@ -416,7 +403,7 @@ class AccountMenuCtrl extends PureCtrl {
}
async downloadDataArchive() {
this.archiveManager.downloadBackup(this.state.mutable.backupEncrypted);
this.application.getArchiveService().downloadBackup(this.state.mutable.backupEncrypted);
}
notesAndTagsCount() {
@@ -434,7 +421,7 @@ class AccountMenuCtrl extends PureCtrl {
}
async reloadAutoLockInterval() {
const interval = await this.lockManager.getAutoLockInterval();
const interval = await this.application.getLockService().getAutoLockInterval();
this.setState({
selectedAutoLockInterval: interval
});
@@ -442,14 +429,14 @@ class AccountMenuCtrl extends PureCtrl {
async selectAutoLockInterval(interval) {
const run = async () => {
await this.lockManager.setAutoLockInterval(interval);
await this.application.getLockService().setAutoLockInterval(interval);
this.reloadAutoLockInterval();
};
const needsPrivilege = await this.application.privilegesService.actionRequiresPrivilege(
ProtectedActions.ManagePasscode
);
if (needsPrivilege) {
this.godService.presentPrivilegesModal(
this.application.presentPrivilegesModal(
ProtectedActions.ManagePasscode,
() => {
run();
@@ -508,7 +495,7 @@ class AccountMenuCtrl extends PureCtrl {
ProtectedActions.ManagePasscode
);
if (needsPrivilege) {
this.godService.presentPrivilegesModal(
this.application.presentPrivilegesModal(
ProtectedActions.ManagePasscode,
run
);
@@ -536,7 +523,7 @@ class AccountMenuCtrl extends PureCtrl {
ProtectedActions.ManagePasscode
);
if (needsPrivilege) {
this.godService.presentPrivilegesModal(
this.application.presentPrivilegesModal(
ProtectedActions.ManagePasscode,
run
);
@@ -558,7 +545,8 @@ export class AccountMenu {
this.controllerAs = 'self';
this.bindToController = true;
this.scope = {
closeFunction: '&'
closeFunction: '&',
application: '='
};
}
}

View File

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

View File

@@ -5,13 +5,10 @@ import { PureCtrl } from '@Controllers';
class ChallengeModalCtrl extends PureCtrl {
/* @ngInject */
constructor(
$scope,
$element,
$timeout,
application,
appState
$timeout
) {
super($scope, $timeout, application, appState);
super($timeout);
this.$element = $element;
this.processingTypes = [];
}
@@ -48,6 +45,13 @@ class ChallengeModalCtrl extends PureCtrl {
});
}
deinit() {
this.application = null;
this.orchestrator = null;
this.challenge = null;
super.deinit();
}
reloadProcessingStatus() {
this.setState({
processing: this.processingTypes.length > 0
@@ -106,7 +110,10 @@ class ChallengeModalCtrl extends PureCtrl {
}
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.controller = ChallengeModalCtrl;
this.controllerAs = 'ctrl';
this.bindToController = true;
this.scope = {
onSubmit: '=',
this.bindToController = {
challenge: '=',
orchestrator: '='
orchestrator: '=',
application: '='
};
}
}

View File

@@ -2,18 +2,19 @@ import template from '%/directives/component-modal.pug';
export class ComponentModalCtrl {
/* @ngInject */
constructor($scope, $element) {
constructor($element) {
this.$element = $element;
this.$scope = $scope;
}
dismiss(callback) {
this.$element.remove();
this.$scope.$destroy();
if(this.onDismiss && this.onDismiss()) {
this.onDismiss()(this.component);
if(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,
$rootScope,
$timeout,
application,
desktopManager,
themeManager
) {
this.$rootScope = $rootScope;
this.$timeout = $timeout;
this.application = application;
this.themeManager = themeManager;
this.desktopManager = desktopManager;
this.componentValid = true;
$scope.$watch('ctrl.component', (component, prevComponent) => {
this.cleanUpWatch = $scope.$watch('ctrl.component', (component, prevComponent) => {
this.componentValueDidSet(component, prevComponent);
});
$scope.$on('ext-reload-complete', () => {
this.cleanUpOn = $scope.$on('ext-reload-complete', () => {
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() {
@@ -42,12 +57,12 @@ class ComponentViewCtrl {
};
registerPackageUpdateObserver() {
this.updateObserver = this.desktopManager
.registerUpdateObserver((component) => {
if(component === this.component && component.active) {
this.reloadComponent();
}
});
this.updateObserver = this.application.getDesktopService()
.registerUpdateObserver((component) => {
if (component === this.component && component.active) {
this.reloadComponent();
}
});
}
registerComponentHandlers() {
@@ -65,7 +80,7 @@ class ComponentViewCtrl {
identifier: this.identifier,
areas: [this.component.area],
activationHandler: (component) => {
if(component !== this.component) {
if (component !== this.component) {
return;
}
this.$timeout(() => {
@@ -73,7 +88,7 @@ class ComponentViewCtrl {
});
},
actionHandler: (component, action, data) => {
if(action === 'set-size') {
if (action === 'set-size') {
this.application.componentManager.handleSetSizeEvent(component, data);
}
}
@@ -81,10 +96,10 @@ class ComponentViewCtrl {
}
onVisibilityChange() {
if(document.visibilityState === 'hidden') {
if (document.visibilityState === 'hidden') {
return;
}
if(this.issueLoading) {
if (this.issueLoading) {
this.reloadComponent();
}
}
@@ -100,34 +115,34 @@ class ComponentViewCtrl {
const component = this.component;
const previouslyValid = this.componentValid;
const offlineRestricted = component.offlineOnly && !isDesktopApplication();
const hasUrlError = function(){
if(isDesktopApplication()) {
const hasUrlError = function () {
if (isDesktopApplication()) {
return !component.local_url && !component.hasValidHostedUrl();
} else {
return !component.hasValidHostedUrl();
}
}();
this.expired = component.valid_until && component.valid_until <= new Date();
if(!component.lockReadonly) {
if (!component.lockReadonly) {
component.readonly = this.expired;
}
this.componentValid = !offlineRestricted && !hasUrlError;
if(!this.componentValid) {
if (!this.componentValid) {
this.loading = false;
}
if(offlineRestricted) {
if (offlineRestricted) {
this.error = 'offline-restricted';
} else if(hasUrlError) {
} else if (hasUrlError) {
this.error = 'url-missing';
} else {
this.error = null;
}
if(this.componentValid !== previouslyValid) {
if(this.componentValid) {
if (this.componentValid !== previouslyValid) {
if (this.componentValid) {
this.application.componentManager.reloadComponent(component, true);
}
}
if(this.expired && doManualReload) {
if (this.expired && doManualReload) {
this.$rootScope.$broadcast('reload-ext-dat');
}
@@ -137,17 +152,17 @@ class ComponentViewCtrl {
}
handleActivation() {
if(!this.component.active) {
if (!this.component.active) {
return;
}
const iframe = this.application.componentManager.iframeForComponent(
this.component
);
if(!iframe) {
if (!iframe) {
return;
}
this.loading = true;
if(this.loadTimeout) {
if (this.loadTimeout) {
this.$timeout.cancel(this.loadTimeout);
}
this.loadTimeout = this.$timeout(() => {
@@ -160,16 +175,16 @@ class ComponentViewCtrl {
}
async handleIframeLoadTimeout() {
if(this.loading) {
if (this.loading) {
this.loading = false;
this.issueLoading = true;
if(!this.didAttemptReload) {
if (!this.didAttemptReload) {
this.didAttemptReload = true;
this.reloadComponent();
} else {
document.addEventListener(
VISIBILITY_CHANGE_LISTENER_KEY,
this.onVisibilityChange.bind(this)
this.onVisibilityChange
);
}
}
@@ -177,13 +192,13 @@ class ComponentViewCtrl {
async handleIframeLoad(iframe) {
let desktopError = false;
if(isDesktopApplication()) {
if (isDesktopApplication()) {
try {
/** 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;
}
} catch (e) {}
} catch (e) { }
}
this.$timeout.cancel(this.loadTimeout);
await this.application.componentManager.registerComponentWindow(
@@ -201,13 +216,13 @@ class ComponentViewCtrl {
componentValueDidSet(component, prevComponent) {
const dontSync = true;
if(prevComponent && component !== prevComponent) {
if (prevComponent && component !== prevComponent) {
this.application.componentManager.deactivateComponent(
prevComponent,
dontSync
);
}
if(component) {
if (component) {
this.application.componentManager.activateComponent(
component,
dontSync
@@ -217,7 +232,7 @@ class ComponentViewCtrl {
}
disableActiveTheme() {
this.themeManager.deactivateAllThemes();
this.application.getThemeService().deactivateAllThemes();
}
getUrl() {
@@ -225,21 +240,6 @@ class ComponentViewCtrl {
this.component.runningLocally = (url === this.component.local_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 {
@@ -249,7 +249,8 @@ export class ComponentView {
this.scope = {
component: '=',
onLoad: '=?',
manualDealloc: '=?'
manualDealloc: '=?',
application: '='
};
this.controller = ComponentViewCtrl;
this.controllerAs = 'ctrl';

View File

@@ -2,14 +2,8 @@ import template from '%/directives/conflict-resolution-modal.pug';
class ConflictResolutionCtrl {
/* @ngInject */
constructor(
$element,
archiveManager,
application
) {
constructor($element) {
this.$element = $element;
this.application = application;
this.archiveManager = archiveManager;
}
$onInit() {
@@ -56,7 +50,7 @@ class ConflictResolutionCtrl {
}
export() {
this.archiveManager.downloadBackupOfItems(
this.application.getArchiveService().downloadBackupOfItems(
[this.item1, this.item2],
true
);
@@ -67,7 +61,10 @@ class ConflictResolutionCtrl {
}
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 = {
item1: '=',
item2: '=',
callback: '='
callback: '=',
application: '='
};
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,12 +4,10 @@ class PrivilegesAuthModalCtrl {
/* @ngInject */
constructor(
$element,
$timeout,
application
$timeout
) {
this.$element = $element;
this.$timeout = $timeout;
this.application = application;
}
$onInit() {
@@ -83,7 +81,10 @@ class PrivilegesAuthModalCtrl {
}
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 = {
action: '=',
onSuccess: '=',
onCancel: '='
onCancel: '=',
application: '='
};
}
}

View File

@@ -5,15 +5,11 @@ import { PureCtrl } from '@Controllers';
class PrivilegesManagementModalCtrl extends PureCtrl {
/* @ngInject */
constructor(
$scope,
$timeout,
$element,
application,
appState
$element
) {
super($scope, $timeout, application, appState);
super($timeout);
this.$element = $element;
this.application = application;
}
onAppLaunch() {
@@ -78,7 +74,10 @@ class PrivilegesManagementModalCtrl extends PureCtrl {
}
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.controllerAs = 'ctrl';
this.bindToController = true;
this.scope = {};
this.scope = {
application: '='
};
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,23 +9,25 @@ const COMPONENT_CONTENT_KEY_PACKAGE_INFO = 'package_info';
const COMPONENT_CONTENT_KEY_LOCAL_URL = 'local_url';
export class DesktopManager extends ApplicationService {
/* @ngInject */
constructor(
$rootScope,
$timeout,
application,
appState,
application
) {
super(application);
this.$rootScope = $rootScope;
this.$timeout = $timeout;
this.appState = appState;
this.application = application;
this.componentActivationObservers = [];
this.updateObservers = [];
this.isDesktop = isDesktopApplication();
}
deinit() {
this.componentActivationObservers.length = 0;
this.updateObservers.length = 0;
super.deinit();
}
/** @override */
onAppEvent(eventName) {
super.onAppEvent(eventName);
@@ -177,7 +179,7 @@ export class DesktopManager extends ApplicationService {
/* Used to resolve 'sn://' */
desktop_setExtServerHost(host) {
this.extServerHost = host;
this.appState.desktopExtensionsReady();
this.application.getAppState().desktopExtensionsReady();
}
desktop_setComponentInstallationSyncHandler(handler) {
@@ -207,11 +209,11 @@ export class DesktopManager extends ApplicationService {
}
desktop_didBeginBackup() {
this.appState.beganBackupDownload();
this.application.getAppState().beganBackupDownload();
}
desktop_didFinishBackup(success) {
this.appState.endedBackupDownload({
this.application.getAppState().endedBackupDownload({
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 { ArchiveManager } from './archiveManager';
export { DesktopManager } from './desktopManager';
export { GodService } from './godService';
export { KeyboardManager } from './keyboardManager';
export { LockManager } from './lockManager';
export { NativeExtManager } from './nativeExtManager';
export { PreferencesManager } from './preferencesManager';
export { StatusManager } from './statusManager';
export { ThemeManager } from './themeManager';
export { AppState } from './state';

View File

@@ -22,8 +22,19 @@ const KeyboardKeyEvents = {
export class KeyboardManager {
constructor() {
this.observers = [];
window.addEventListener('keydown', this.handleKeyDown.bind(this));
window.addEventListener('keyup', this.handleKeyUp.bind(this));
this.handleKeyDown = this.handleKeyDown.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) {

View File

@@ -1,5 +1,5 @@
import { isDesktopApplication } from '@/utils';
import { AppStateEvents } from '../state';
import { AppStateEvents } from '@/services/state';
const MILLISECONDS_PER_SECOND = 1000;
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";
export class LockManager {
/* @ngInject */
constructor($rootScope, application, appState) {
this.$rootScope = $rootScope;
constructor(application) {
this.application = application;
this.appState = appState;
this.observeVisibility();
setImmediate(() => {
this.observeVisibility();
});
}
observeVisibility() {
this.appState.addObserver((eventName, data) => {
this.unsubState = this.application.getAppState().addObserver((eventName) => {
if(eventName === AppStateEvents.WindowDidBlur) {
this.documentVisibilityChanged(false);
} else if(eventName === AppStateEvents.WindowDidFocus) {
@@ -33,6 +32,13 @@ export class LockManager {
}
}
deinit() {
this.unsubState();
if (this.pollFocusInterval) {
clearInterval(this.pollFocusInterval);
}
}
async setAutoLockInterval(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.
*/
beginWebFocusPolling() {
this.pollFocusTimeout = setInterval(() => {
this.pollFocusInterval = setInterval(() => {
const hasFocus = document.hasFocus();
if(hasFocus && this.lastFocusState === 'hidden') {
this.documentVisibilityChanged(true);

View File

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

View File

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

View File

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

View File

@@ -5,33 +5,35 @@ import {
EncryptionIntents,
ApplicationService,
} from 'snjs';
import { AppStateEvents } from '@/state';
import { AppStateEvents } from '@/services/state';
const CACHED_THEMES_KEY = 'cachedThemes';
export class ThemeManager extends ApplicationService {
/* @ngInject */
constructor(
application,
appState,
desktopManager,
) {
constructor(application) {
super(application);
this.appState = appState;
this.desktopManager = desktopManager;
this.activeThemes = [];
this.unsubState = appState.addObserver((eventName, data) => {
if (eventName === AppStateEvents.DesktopExtsReady) {
this.activateCachedThemes();
}
setImmediate(() => {
this.unsubState = this.application.getAppState().addObserver((eventName, data) => {
if (eventName === AppStateEvents.DesktopExtsReady) {
this.activateCachedThemes();
}
});
});
}
deinit() {
this.unsubState();
this.unsubState = null;
this.activeThemes.length = 0;
super.deinit();
}
/** @override */
onAppStart() {
super.onAppStart();
this.registerObservers();
if (!this.desktopManager.isDesktop) {
if (!this.application.getDesktopService().isDesktop) {
this.activateCachedThemes();
}
}
@@ -52,7 +54,7 @@ export class ThemeManager extends ApplicationService {
}
registerObservers() {
this.desktopManager.registerUpdateObserver((component) => {
this.application.getDesktopService().registerUpdateObserver((component) => {
if (component.active && component.isTheme()) {
this.deactivateTheme(component);
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_ENC_NOT_ENABLED = "Encryption is not enabled. Sign in, register, or add a passcode lock to enable encryption.";
export const STRING_IMPORT_SUCCESS = "Your data has been successfully imported.";
export const STRING_REMOVE_PASSCODE_CONFIRMATION = "Are you sure you want to remove your local passcode?";
export const STRING_REMOVE_PASSCODE_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_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.";

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}}
a.sk-a.info.close-button(ng-click="ctrl.dismiss()") Close
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.
p.sk-p
| Privileges sync across your other devices; however, note that if you
| require a "Local Passcode" privilege, and another device does not have
| a local passcode set up, the local passcode requirement will be ignored
| require an "Application Passcode" privilege, and another device does not have
| an application passcode set up, the application passcode requirement will be ignored
| on that device.

View File

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

View File

@@ -165,7 +165,8 @@
callback='self.editorMenuOnSelect',
current-item='self.state.note',
ng-if='self.state.showEditorMenu',
selected-editor='self.state.selectedEditor'
selected-editor='self.state.selectedEditor',
application='self.application'
)
.sk-app-bar-item(
click-outside=`self.setMenuState('showExtensions', false)`,
@@ -176,7 +177,8 @@
.sk-label Actions
actions-menu(
item='self.state.note',
ng-if='self.state.showExtensions'
ng-if='self.state.showExtensions',
application='self.application'
)
.sk-app-bar-item(
click-outside=`self.setMenuState('showSessionHistory', false)`,
@@ -186,7 +188,8 @@
.sk-label Session History
session-history-menu(
item='self.state.note',
ng-if='self.state.showSessionHistory'
ng-if='self.state.showSessionHistory',
application='self.application'
)
#editor-content.editor-content(
ng-if='self.state.noteReady && !self.state.note.errorDecrypting'
@@ -196,14 +199,15 @@
hoverable='true',
min-width='300',
ng-if='self.state.marginResizersEnabled',
on-resize-finish='self.onPanelResizeFinish',
on-resize-finish='self.onPanelResizeFinish()',
panel-id="'editor-content'",
property="'left'"
)
component-view.component-view(
component='self.state.selectedEditor',
ng-if='self.state.selectedEditor',
on-load='self.onEditorLoad'
on-load='self.onEditorLoad',
application='self.application'
)
textarea#note-text-editor.editable(
dir='auto',
@@ -222,7 +226,7 @@
control='self.rightPanelPuppet',
hoverable='true', min-width='300',
ng-if='self.state.marginResizersEnabled',
on-resize-finish='self.onPanelResizeFinish',
on-resize-finish='self.onPanelResizeFinish()',
panel-id="'editor-content'",
property="'right'"
)
@@ -249,5 +253,6 @@
manual-dealloc='true',
ng-if='component.active',
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
.left
.sk-app-bar-item(
click-outside='ctrl.clickOutsideAccountMenu()',
click-outside='ctrl.clickOutsideAccountMenu()',
is-open='ctrl.showAccountMenu',
ng-click='ctrl.accountMenuPressed()'
)
@@ -13,9 +13,10 @@
.sk-app-bar-item-column
.sk-label.title(ng-class='{red: ctrl.error}') Account
account-menu(
close-function='ctrl.closeAccountMenu',
close-function='ctrl.closeAccountMenu()',
ng-click='$event.stopPropagation()',
ng-if='ctrl.showAccountMenu',
ng-if='ctrl.showAccountMenu',
application='ctrl.application'
)
.sk-app-bar-item
a.no-decoration.sk-label.title(
@@ -31,21 +32,22 @@
component-modal(
component='room',
ng-if='room.showRoom',
on-dismiss='ctrl.onRoomDismiss'
on-dismiss='ctrl.onRoomDismiss()',
application='self.application'
)
.center
.sk-app-bar-item(ng-show='ctrl.arbitraryStatusMessage')
.sk-app-bar-item(ng-if='ctrl.arbitraryStatusMessage')
.sk-app-bar-item-column
span.neutral.sk-label {{ctrl.arbitraryStatusMessage}}
.right
.sk-app-bar-item(
ng-click='ctrl.openSecurityUpdate()',
ng-show='ctrl.state.dataUpgradeAvailable'
ng-if='ctrl.state.dataUpgradeAvailable'
)
span.success.sk-label Encryption upgrade available.
.sk-app-bar-item(
ng-click='ctrl.clickedNewUpdateAnnouncement()',
ng-show='ctrl.newUpdateAvailable == true'
ng-if='ctrl.newUpdateAvailable == true'
)
span.info.sk-label New update available.
.sk-app-bar-item.no-pointer(
@@ -59,9 +61,10 @@
)
.sk-label.warning(ng-if='ctrl.state.outOfSync') Potentially Out of Sync
sync-resolution-menu(
close-function='ctrl.toggleSyncResolutionMenu',
close-function='ctrl.toggleSyncResolutionMenu()',
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-spinner.small

View File

@@ -26,7 +26,7 @@
) Forgot?
div(ng-if='ctrl.formData.showRecovery')
.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.
.sk-panel-row
a.sk-a.danger.center-text(

View File

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

View File

@@ -1,14 +1,4 @@
.main-ui-view(
ng-class='self.platformString'
)
#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'
)
application-view(
ng-repeat='application in self.applications',
application='application'
)

View File

@@ -1,6 +1,9 @@
#tags-column.sn-component.section.tags(aria-label='Tags')
.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-title-section.section-title-bar
.section-title-bar-header
@@ -60,6 +63,6 @@
control='self.panelPuppet',
default-width='150',
hoverable='true',
on-resize-finish='self.onPanelResize',
on-resize-finish='self.onPanelResize()',
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

File diff suppressed because one or more lines are too long