feat: snjs app groups (#468)
* feat: snjs app groups * fix: update snjs version to point to wip commit * wip: account switcher * feat: rename lock manager to auto lock service * fix: more relevant sign out copy * chore(deps): update snjs * fix: use setTimeout instead of setImmediate * feat: make account switcher expiremental feature * chore(deps): upgrade snjs
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { AccountSwitcherScope } from './../types';
|
||||
import { ComponentGroup } from './component_group';
|
||||
import { EditorGroup } from '@/ui_models/editor_group';
|
||||
import { InputModalScope } from '@/directives/views/inputModal';
|
||||
@@ -16,7 +17,7 @@ import { AlertService } from '@/services/alertService';
|
||||
import { WebDeviceInterface } from '@/web_device_interface';
|
||||
import {
|
||||
DesktopManager,
|
||||
LockManager,
|
||||
AutolockService,
|
||||
ArchiveManager,
|
||||
NativeExtManager,
|
||||
StatusManager,
|
||||
@@ -27,11 +28,12 @@ import {
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { SNWebCrypto } from 'sncrypto/dist/sncrypto-web';
|
||||
import { Bridge } from '@/services/bridge';
|
||||
import { DeinitSource } from 'snjs/dist/@types/types';
|
||||
|
||||
type WebServices = {
|
||||
appState: AppState
|
||||
desktopService: DesktopManager
|
||||
lockService: LockManager
|
||||
autolockService: AutolockService
|
||||
archiveService: ArchiveManager
|
||||
nativeExtService: NativeExtManager
|
||||
statusService: StatusManager
|
||||
@@ -44,7 +46,6 @@ export class WebApplication extends SNApplication {
|
||||
|
||||
private $compile?: ng.ICompileService
|
||||
private scope?: ng.IScope
|
||||
private onDeinit?: (app: WebApplication) => void
|
||||
private webServices!: WebServices
|
||||
private currentAuthenticationElement?: JQLite
|
||||
public editorGroup: EditorGroup
|
||||
@@ -52,38 +53,33 @@ export class WebApplication extends SNApplication {
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
deviceInterface: WebDeviceInterface,
|
||||
identifier: string,
|
||||
$compile: ng.ICompileService,
|
||||
$timeout: ng.ITimeoutService,
|
||||
scope: ng.IScope,
|
||||
onDeinit: (app: WebApplication) => void,
|
||||
defaultSyncServerHost: string,
|
||||
bridge: Bridge,
|
||||
) {
|
||||
const deviceInterface = new WebDeviceInterface(
|
||||
$timeout,
|
||||
bridge
|
||||
);
|
||||
super(
|
||||
bridge.environment,
|
||||
platformFromString(getPlatformString()),
|
||||
deviceInterface,
|
||||
new SNWebCrypto(),
|
||||
new AlertService(),
|
||||
undefined,
|
||||
identifier,
|
||||
undefined,
|
||||
undefined,
|
||||
defaultSyncServerHost
|
||||
);
|
||||
this.$compile = $compile;
|
||||
this.scope = scope;
|
||||
this.onDeinit = onDeinit;
|
||||
deviceInterface.setApplication(this);
|
||||
this.editorGroup = new EditorGroup(this);
|
||||
this.componentGroup = new ComponentGroup(this);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
deinit() {
|
||||
deinit(source: DeinitSource) {
|
||||
for (const key of Object.keys(this.webServices)) {
|
||||
const service = (this.webServices as any)[key];
|
||||
if (service.deinit) {
|
||||
@@ -92,8 +88,6 @@ export class WebApplication extends SNApplication {
|
||||
service.application = undefined;
|
||||
}
|
||||
this.webServices = {} as WebServices;
|
||||
this.onDeinit!(this);
|
||||
this.onDeinit = undefined;
|
||||
this.$compile = undefined;
|
||||
this.editorGroup.deinit();
|
||||
this.componentGroup.deinit();
|
||||
@@ -102,9 +96,9 @@ export class WebApplication extends SNApplication {
|
||||
this.scope = undefined;
|
||||
/** Allow our Angular directives to be destroyed and any pending digest cycles
|
||||
* to complete before destroying the global application instance and all its services */
|
||||
setImmediate(() => {
|
||||
super.deinit();
|
||||
})
|
||||
setTimeout(() => {
|
||||
super.deinit(source);
|
||||
}, 0)
|
||||
}
|
||||
|
||||
setWebServices(services: WebServices) {
|
||||
@@ -119,8 +113,8 @@ export class WebApplication extends SNApplication {
|
||||
return this.webServices.desktopService;
|
||||
}
|
||||
|
||||
public getLockService() {
|
||||
return this.webServices.lockService;
|
||||
public getAutolockService() {
|
||||
return this.webServices.autolockService;
|
||||
}
|
||||
|
||||
public getArchiveService() {
|
||||
@@ -257,4 +251,15 @@ export class WebApplication extends SNApplication {
|
||||
)(scope);
|
||||
angular.element(document.body).append(el);
|
||||
}
|
||||
|
||||
public openAccountSwitcher() {
|
||||
const scope = this.scope!.$new(true) as Partial<AccountSwitcherScope>;
|
||||
scope.application = this;
|
||||
const el = this.$compile!(
|
||||
"<account-switcher application='application' "
|
||||
+ "class='sk-modal'></account-switcher>"
|
||||
)(scope as any);
|
||||
angular.element(document.body).append(el);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { WebDeviceInterface } from '@/web_device_interface';
|
||||
import { WebApplication } from './application';
|
||||
import { removeFromArray } from 'snjs';
|
||||
import { ApplicationDescriptor, SNApplicationGroup, DeviceInterface } from 'snjs';
|
||||
import {
|
||||
ArchiveManager,
|
||||
DesktopManager,
|
||||
KeyboardManager,
|
||||
LockManager,
|
||||
AutolockService,
|
||||
NativeExtManager,
|
||||
PreferencesManager,
|
||||
StatusManager,
|
||||
@@ -13,16 +14,11 @@ import {
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { Bridge } from '@/services/bridge';
|
||||
|
||||
type AppManagerChangeCallback = () => void
|
||||
|
||||
export class ApplicationGroup {
|
||||
export class ApplicationGroup extends SNApplicationGroup {
|
||||
|
||||
$compile: ng.ICompileService
|
||||
$rootScope: ng.IRootScopeService
|
||||
$timeout: ng.ITimeoutService
|
||||
applications: WebApplication[] = []
|
||||
changeObservers: AppManagerChangeCallback[] = []
|
||||
activeApplication?: WebApplication
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
@@ -32,46 +28,35 @@ export class ApplicationGroup {
|
||||
private defaultSyncServerHost: string,
|
||||
private bridge: Bridge,
|
||||
) {
|
||||
super(new WebDeviceInterface(
|
||||
$timeout,
|
||||
bridge
|
||||
));
|
||||
this.$compile = $compile;
|
||||
this.$timeout = $timeout;
|
||||
this.$rootScope = $rootScope;
|
||||
this.onApplicationDeinit = this.onApplicationDeinit.bind(this);
|
||||
this.createDefaultApplication();
|
||||
}
|
||||
|
||||
async initialize(callback?: any) {
|
||||
await super.initialize({
|
||||
applicationCreator: this.createApplication
|
||||
});
|
||||
|
||||
/** FIXME(baptiste): rely on a less fragile method to detect Electron */
|
||||
if ((window as any).isElectron) {
|
||||
Object.defineProperty(window, 'desktopManager', {
|
||||
get: () => this.activeApplication?.getDesktopService()
|
||||
get: () => (this.primaryApplication as WebApplication).getDesktopService()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private createDefaultApplication() {
|
||||
this.activeApplication = this.createNewApplication();
|
||||
this.applications.push(this.activeApplication!);
|
||||
this.notifyObserversOfAppChange();
|
||||
}
|
||||
|
||||
/** @callback */
|
||||
onApplicationDeinit(application: WebApplication) {
|
||||
removeFromArray(this.applications, application);
|
||||
if (this.activeApplication === application) {
|
||||
this.activeApplication = undefined;
|
||||
}
|
||||
if (this.applications.length === 0) {
|
||||
this.createDefaultApplication();
|
||||
} else {
|
||||
this.notifyObserversOfAppChange();
|
||||
}
|
||||
}
|
||||
|
||||
private createNewApplication() {
|
||||
private createApplication = (descriptor: ApplicationDescriptor, deviceInterface: DeviceInterface) => {
|
||||
const scope = this.$rootScope.$new(true);
|
||||
const application = new WebApplication(
|
||||
deviceInterface as WebDeviceInterface,
|
||||
descriptor.identifier,
|
||||
this.$compile,
|
||||
this.$timeout,
|
||||
scope,
|
||||
this.onApplicationDeinit,
|
||||
this.defaultSyncServerHost,
|
||||
this.bridge,
|
||||
);
|
||||
@@ -90,7 +75,7 @@ export class ApplicationGroup {
|
||||
this.bridge,
|
||||
);
|
||||
const keyboardService = new KeyboardManager();
|
||||
const lockService = new LockManager(
|
||||
const autolockService = new AutolockService(
|
||||
application
|
||||
);
|
||||
const nativeExtService = new NativeExtManager(
|
||||
@@ -108,7 +93,7 @@ export class ApplicationGroup {
|
||||
archiveService,
|
||||
desktopService,
|
||||
keyboardService,
|
||||
lockService,
|
||||
autolockService,
|
||||
nativeExtService,
|
||||
prefsService,
|
||||
statusService,
|
||||
@@ -116,34 +101,4 @@ export class ApplicationGroup {
|
||||
});
|
||||
return application;
|
||||
}
|
||||
|
||||
get application() {
|
||||
return this.activeApplication;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
public addApplicationChangeObserver(callback: AppManagerChangeCallback) {
|
||||
this.changeObservers.push(callback);
|
||||
if (this.application) {
|
||||
callback();
|
||||
}
|
||||
|
||||
return () => {
|
||||
removeFromArray(this.changeObservers, callback);
|
||||
}
|
||||
}
|
||||
|
||||
private notifyObserversOfAppChange() {
|
||||
for (const observer of this.changeObservers) {
|
||||
observer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SNComponent, ComponentArea, removeFromArray, addIfUnique } from 'snjs';
|
||||
import { WebApplication } from './application';
|
||||
import { UuidString } from '@node_modules/snjs/dist/@types/types';
|
||||
import { UuidString } from 'snjs/dist/@types/types';
|
||||
|
||||
/** Areas that only allow a single component to be active */
|
||||
const SingleComponentAreas = [
|
||||
@@ -48,8 +48,8 @@ export class ComponentGroup {
|
||||
}
|
||||
removeFromArray(this.activeComponents, component.uuid);
|
||||
/** If this function is called as part of global application deinit (locking),
|
||||
* componentManager can be destroyed. In this case, it's harmless to not take any
|
||||
* action since the componentManager will be destroyed, and the component will
|
||||
* componentManager can be destroyed. In this case, it's harmless to not take any
|
||||
* action since the componentManager will be destroyed, and the component will
|
||||
* essentially be deregistered. */
|
||||
if(this.componentManager) {
|
||||
await this.componentManager.deactivateComponent(component.uuid);
|
||||
|
||||
Reference in New Issue
Block a user