Release/3.6.0 (#527)
* feat: (wip) authorize note access
* fix: remove multiEditorEnabled
* refactor: update SNJS + eslint
* refactor: remove privileges in favor of SNJS protections
* fix: do not close editor when editing an archived note
* chore: remove progress indicator for webpack dev server
* fix: add rel="noreferrer" to bugsnag links
* chore(deps): upgrade snjs
* chore(deps): upgrade snjs
* feat: batch manager protection + react challenge modal + eslint fix
* fix: lint errors
* fix: launch state error
* fix: challenge modal: cancel instead of dismiss when pressing escape
* feat: improve focus styles
* fix: cancel session revoking when pressing escape on confirm dialog
* fix: lint warning
* chore(deps): upgrade minor versions
* feat: make SNWebCrypto a constant
* feat: add random identifier to bugsnag reports
* fix: check onKeyUp instead of onKeyDown
* feat: implement SNJS backup file password retrieval
* chore(deps): upgrade snjs
* feat: display warning banner when using the app with no account
* fix: properly color svg button
* fix: wording
* fix: hide account warning after login + improve key storage wording
* chore(deps): upgrade stylekit
* feat: use stylekit fonts for the editor
* chore(deps): bump nokogiri from 1.10.8 to 1.11.1 (#511)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.10.8 to 1.11.1.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.10.8...v1.11.1)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Baptiste Grob <60621355+baptiste-grob@users.noreply.github.com>
* chore(deps): bump ini from 1.3.5 to 1.3.8 (#504)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Baptiste Grob <60621355+baptiste-grob@users.noreply.github.com>
* fix: rename master branch to main
* fix: add missing placeholders for submodules (#516)
Co-authored-by: Baptiste Grob <60621355+baptiste-grob@users.noreply.github.com>
* chore(deps): upgrade snjs, babel, typescript, reach, mobx, preact
* feat: clear protection session
* fix: use correct close icon size
* fix: hide protections paragraph when no account or passcode exist
* chore(deps): remove unused dependencies
* fix: button casing
* feat: implement SNApplication.hasProtectionSources
* chore(version): 3.6.0
* feat: enable sessions management for every build
* feat: make "Protected" flag more subtle
* fix: only match protected note title
* fix: remove inconsistencies between protected note label and date
* feat: show warning when protecting a note with no protection source
* feat: make unprotecting a note a protected action
* chore(deps): upgrade snjs
* chore(version): 3.6.0-beta01
* fix: run docker with root to fix crashing on Linux (undoes 62da387d3a) (#525)
* feat: make encrypted backups protected (#524)
Co-authored-by: Baptiste Grob <60621355+baptiste-grob@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: proletarius101 <54175165+proletarius101@users.noreply.github.com>
Co-authored-by: Darius JJ Chuck <79410894+standarius@users.noreply.github.com>
Co-authored-by: Antonella Sgarlatta <antonella@standardnotes.org>
This commit is contained in:
@@ -1,22 +1,22 @@
|
||||
import { isDesktopApplication, isDev } from '@/utils';
|
||||
import pull from 'lodash/pull';
|
||||
import {
|
||||
ProtectedAction,
|
||||
ApplicationEvent,
|
||||
SNTag,
|
||||
SNNote,
|
||||
SNUserPrefs,
|
||||
ContentType,
|
||||
SNSmartTag,
|
||||
PayloadSource,
|
||||
DeinitSource,
|
||||
UuidString,
|
||||
SyncOpStatus,
|
||||
PrefKey,
|
||||
SNApplication,
|
||||
} from '@standardnotes/snjs';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { Editor } from '@/ui_models/editor';
|
||||
import { action, makeObservable, observable } from 'mobx';
|
||||
import { action, makeObservable, observable, runInAction } from 'mobx';
|
||||
import { Bridge } from '@/services/bridge';
|
||||
import { storage, StorageKey } from '@/services/localStorage';
|
||||
|
||||
export enum AppStateEvent {
|
||||
TagChanged,
|
||||
@@ -29,6 +29,11 @@ export enum AppStateEvent {
|
||||
WindowDidBlur,
|
||||
}
|
||||
|
||||
export type PanelResizedData = {
|
||||
panel: string;
|
||||
collapsed: boolean;
|
||||
};
|
||||
|
||||
export enum EventSource {
|
||||
UserInteraction,
|
||||
Script,
|
||||
@@ -36,8 +41,6 @@ export enum EventSource {
|
||||
|
||||
type ObserverCallback = (event: AppStateEvent, data?: any) => Promise<void>;
|
||||
|
||||
const SHOW_BETA_WARNING_KEY = 'show_beta_warning';
|
||||
|
||||
class ActionsMenuState {
|
||||
hiddenExtensions: Record<UuidString, boolean> = {};
|
||||
|
||||
@@ -45,7 +48,7 @@ class ActionsMenuState {
|
||||
makeObservable(this, {
|
||||
hiddenExtensions: observable,
|
||||
toggleExtensionVisibility: action,
|
||||
deinit: action,
|
||||
reset: action,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,7 +56,7 @@ class ActionsMenuState {
|
||||
this.hiddenExtensions[uuid] = !this.hiddenExtensions[uuid];
|
||||
}
|
||||
|
||||
deinit() {
|
||||
reset() {
|
||||
this.hiddenExtensions = {};
|
||||
}
|
||||
}
|
||||
@@ -72,7 +75,7 @@ export class SyncState {
|
||||
});
|
||||
}
|
||||
|
||||
update(status: SyncOpStatus) {
|
||||
update(status: SyncOpStatus): void {
|
||||
this.errorMessage = status.error?.message;
|
||||
this.inProgress = status.syncInProgress;
|
||||
const stats = status.getStats();
|
||||
@@ -92,6 +95,59 @@ export class SyncState {
|
||||
}
|
||||
}
|
||||
|
||||
class AccountMenuState {
|
||||
show = false;
|
||||
constructor() {
|
||||
makeObservable(this, {
|
||||
show: observable,
|
||||
setShow: action,
|
||||
toggleShow: action,
|
||||
});
|
||||
}
|
||||
setShow(show: boolean) {
|
||||
this.show = show;
|
||||
}
|
||||
toggleShow() {
|
||||
this.show = !this.show;
|
||||
}
|
||||
}
|
||||
|
||||
class NoAccountWarningState {
|
||||
show: boolean;
|
||||
constructor(application: SNApplication, appObservers: (() => void)[]) {
|
||||
this.show = application.hasAccount()
|
||||
? false
|
||||
: storage.get(StorageKey.ShowNoAccountWarning) ?? true;
|
||||
|
||||
appObservers.push(
|
||||
application.addEventObserver(async () => {
|
||||
runInAction(() => {
|
||||
this.show = false;
|
||||
});
|
||||
}, ApplicationEvent.SignedIn),
|
||||
application.addEventObserver(async () => {
|
||||
if (application.hasAccount()) {
|
||||
runInAction(() => {
|
||||
this.show = false;
|
||||
});
|
||||
}
|
||||
}, ApplicationEvent.Started)
|
||||
);
|
||||
|
||||
makeObservable(this, {
|
||||
show: observable,
|
||||
hide: action,
|
||||
});
|
||||
}
|
||||
hide() {
|
||||
this.show = false;
|
||||
storage.set(StorageKey.ShowNoAccountWarning, false);
|
||||
}
|
||||
reset() {
|
||||
storage.remove(StorageKey.ShowNoAccountWarning);
|
||||
}
|
||||
}
|
||||
|
||||
export class AppState {
|
||||
readonly enableUnfinishedFeatures =
|
||||
isDev || location.host.includes('app-dev.standardnotes.org');
|
||||
@@ -106,12 +162,15 @@ export class AppState {
|
||||
rootScopeCleanup2: any;
|
||||
onVisibilityChange: any;
|
||||
selectedTag?: SNTag;
|
||||
multiEditorEnabled = false;
|
||||
showBetaWarning = false;
|
||||
showBetaWarning: boolean;
|
||||
readonly accountMenu = new AccountMenuState();
|
||||
readonly actionsMenu = new ActionsMenuState();
|
||||
readonly noAccountWarning: NoAccountWarningState;
|
||||
readonly sync = new SyncState();
|
||||
isSessionsModalVisible = false;
|
||||
|
||||
private appEventObserverRemovers: (() => void)[] = [];
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
$rootScope: ng.IRootScopeService,
|
||||
@@ -122,15 +181,10 @@ export class AppState {
|
||||
this.$timeout = $timeout;
|
||||
this.$rootScope = $rootScope;
|
||||
this.application = application;
|
||||
makeObservable(this, {
|
||||
showBetaWarning: observable,
|
||||
isSessionsModalVisible: observable,
|
||||
|
||||
enableBetaWarning: action,
|
||||
disableBetaWarning: action,
|
||||
openSessionsModal: action,
|
||||
closeSessionsModal: action,
|
||||
});
|
||||
this.noAccountWarning = new NoAccountWarningState(
|
||||
application,
|
||||
this.appEventObserverRemovers
|
||||
);
|
||||
this.addAppEventObserver();
|
||||
this.streamNotesAndTags();
|
||||
this.onVisibilityChange = () => {
|
||||
@@ -141,17 +195,35 @@ export class AppState {
|
||||
this.notifyEvent(event);
|
||||
};
|
||||
this.registerVisibilityObservers();
|
||||
this.determineBetaWarningValue();
|
||||
|
||||
if (this.bridge.appVersion.includes('-beta')) {
|
||||
this.showBetaWarning = storage.get(StorageKey.ShowBetaWarning) ?? true;
|
||||
} else {
|
||||
this.showBetaWarning = false;
|
||||
}
|
||||
|
||||
makeObservable(this, {
|
||||
showBetaWarning: observable,
|
||||
isSessionsModalVisible: observable,
|
||||
|
||||
enableBetaWarning: action,
|
||||
disableBetaWarning: action,
|
||||
openSessionsModal: action,
|
||||
closeSessionsModal: action,
|
||||
});
|
||||
}
|
||||
|
||||
deinit(source: DeinitSource) {
|
||||
deinit(source: DeinitSource): void {
|
||||
if (source === DeinitSource.SignOut) {
|
||||
localStorage.removeItem(SHOW_BETA_WARNING_KEY);
|
||||
storage.remove(StorageKey.ShowBetaWarning);
|
||||
this.noAccountWarning.reset();
|
||||
}
|
||||
this.actionsMenu.deinit();
|
||||
this.actionsMenu.reset();
|
||||
this.unsubApp();
|
||||
this.unsubApp = undefined;
|
||||
this.observers.length = 0;
|
||||
this.appEventObserverRemovers.forEach((remover) => remover());
|
||||
this.appEventObserverRemovers.length = 0;
|
||||
if (this.rootScopeCleanup1) {
|
||||
this.rootScopeCleanup1();
|
||||
this.rootScopeCleanup2();
|
||||
@@ -172,30 +244,12 @@ export class AppState {
|
||||
|
||||
disableBetaWarning() {
|
||||
this.showBetaWarning = false;
|
||||
localStorage.setItem(SHOW_BETA_WARNING_KEY, 'false');
|
||||
storage.set(StorageKey.ShowBetaWarning, false);
|
||||
}
|
||||
|
||||
enableBetaWarning() {
|
||||
this.showBetaWarning = true;
|
||||
localStorage.setItem(SHOW_BETA_WARNING_KEY, 'true');
|
||||
}
|
||||
|
||||
clearBetaWarning() {
|
||||
localStorage.setItem(SHOW_BETA_WARNING_KEY, 'true');
|
||||
}
|
||||
|
||||
private determineBetaWarningValue() {
|
||||
if (this.bridge.appVersion.includes('-beta')) {
|
||||
switch (localStorage.getItem(SHOW_BETA_WARNING_KEY)) {
|
||||
case 'true':
|
||||
default:
|
||||
this.enableBetaWarning();
|
||||
break;
|
||||
case 'false':
|
||||
this.disableBetaWarning();
|
||||
break;
|
||||
}
|
||||
}
|
||||
storage.set(StorageKey.ShowBetaWarning, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,7 +264,7 @@ export class AppState {
|
||||
: this.selectedTag.uuid
|
||||
: undefined;
|
||||
|
||||
if (!activeEditor || this.multiEditorEnabled) {
|
||||
if (!activeEditor) {
|
||||
this.application.editorGroup.createEditor(
|
||||
undefined,
|
||||
title,
|
||||
@@ -221,35 +275,25 @@ export class AppState {
|
||||
}
|
||||
}
|
||||
|
||||
async openEditor(noteUuid: string) {
|
||||
async openEditor(noteUuid: string): Promise<void> {
|
||||
if (this.getActiveEditor()?.note?.uuid === noteUuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const note = this.application.findItem(noteUuid) as SNNote;
|
||||
if (this.getActiveEditor()?.note?.uuid === noteUuid) return;
|
||||
const run = async () => {
|
||||
if (!note) {
|
||||
console.warn('Tried accessing a non-existant note of UUID ' + noteUuid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (await this.application.authorizeNoteAccess(note)) {
|
||||
const activeEditor = this.getActiveEditor();
|
||||
if (!activeEditor || this.multiEditorEnabled) {
|
||||
if (!activeEditor) {
|
||||
this.application.editorGroup.createEditor(noteUuid);
|
||||
} else {
|
||||
activeEditor.setNote(note);
|
||||
}
|
||||
await this.notifyEvent(AppStateEvent.ActiveEditorChanged);
|
||||
};
|
||||
if (
|
||||
note &&
|
||||
note.safeContent.protected &&
|
||||
(await this.application.privilegesService!.actionRequiresPrivilege(
|
||||
ProtectedAction.ViewProtectedNotes
|
||||
))
|
||||
) {
|
||||
return new Promise((resolve) => {
|
||||
this.application.presentPrivilegesModal(
|
||||
ProtectedAction.ViewProtectedNotes,
|
||||
() => {
|
||||
run().then(resolve);
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
return run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +343,11 @@ export class AppState {
|
||||
this.closeEditor(editor);
|
||||
} else if (note.trashed && !this.selectedTag?.isTrashTag) {
|
||||
this.closeEditor(editor);
|
||||
} else if (note.archived && !this.selectedTag?.isArchiveTag) {
|
||||
} else if (
|
||||
note.archived &&
|
||||
!this.selectedTag?.isArchiveTag &&
|
||||
!this.application.getPreference(PrefKey.NotesShowArchived, false)
|
||||
) {
|
||||
this.closeEditor(editor);
|
||||
}
|
||||
}
|
||||
@@ -400,10 +448,11 @@ export class AppState {
|
||||
}
|
||||
|
||||
panelDidResize(name: string, collapsed: boolean) {
|
||||
this.notifyEvent(AppStateEvent.PanelResized, {
|
||||
const data: PanelResizedData = {
|
||||
panel: name,
|
||||
collapsed: collapsed,
|
||||
});
|
||||
};
|
||||
this.notifyEvent(AppStateEvent.PanelResized, data);
|
||||
}
|
||||
|
||||
editorDidFocus(eventSource: EventSource) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { PermissionDialog } from '@standardnotes/snjs';
|
||||
import { ComponentModalScope } from './../directives/views/componentModal';
|
||||
import { AccountSwitcherScope, PermissionsModalScope } from './../types';
|
||||
import { ComponentGroup } from './component_group';
|
||||
@@ -8,11 +7,12 @@ import { PasswordWizardType, PasswordWizardScope } from '@/types';
|
||||
import {
|
||||
SNApplication,
|
||||
platformFromString,
|
||||
Challenge,
|
||||
ProtectedAction, SNComponent
|
||||
SNComponent,
|
||||
PermissionDialog,
|
||||
DeinitSource,
|
||||
} from '@standardnotes/snjs';
|
||||
import angular from 'angular';
|
||||
import { getPlatformString } from '@/utils';
|
||||
import { getPlatform, getPlatformString } from '@/utils';
|
||||
import { AlertService } from '@/services/alertService';
|
||||
import { WebDeviceInterface } from '@/web_device_interface';
|
||||
import {
|
||||
@@ -25,26 +25,25 @@ import {
|
||||
KeyboardManager
|
||||
} from '@/services';
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { SNWebCrypto } from '@standardnotes/sncrypto-web';
|
||||
import { Bridge } from '@/services/bridge';
|
||||
import { DeinitSource } from '@standardnotes/snjs';
|
||||
import { WebCrypto } from '@/crypto';
|
||||
|
||||
type WebServices = {
|
||||
appState: AppState
|
||||
desktopService: DesktopManager
|
||||
autolockService: AutolockService
|
||||
archiveService: ArchiveManager
|
||||
nativeExtService: NativeExtManager
|
||||
statusManager: StatusManager
|
||||
themeService: ThemeManager
|
||||
keyboardService: KeyboardManager
|
||||
appState: AppState;
|
||||
desktopService: DesktopManager;
|
||||
autolockService: AutolockService;
|
||||
archiveService: ArchiveManager;
|
||||
nativeExtService: NativeExtManager;
|
||||
statusManager: StatusManager;
|
||||
themeService: ThemeManager;
|
||||
keyboardService: KeyboardManager;
|
||||
}
|
||||
|
||||
export class WebApplication extends SNApplication {
|
||||
|
||||
private scope?: ng.IScope
|
||||
private scope?: angular.IScope
|
||||
private webServices!: WebServices
|
||||
private currentAuthenticationElement?: JQLite
|
||||
private currentAuthenticationElement?: angular.IRootElementService
|
||||
public editorGroup: EditorGroup
|
||||
public componentGroup: ComponentGroup
|
||||
|
||||
@@ -52,16 +51,16 @@ export class WebApplication extends SNApplication {
|
||||
constructor(
|
||||
deviceInterface: WebDeviceInterface,
|
||||
identifier: string,
|
||||
private $compile: ng.ICompileService,
|
||||
scope: ng.IScope,
|
||||
private $compile: angular.ICompileService,
|
||||
scope: angular.IScope,
|
||||
defaultSyncServerHost: string,
|
||||
private bridge: Bridge,
|
||||
) {
|
||||
super(
|
||||
bridge.environment,
|
||||
platformFromString(getPlatformString()),
|
||||
getPlatform(),
|
||||
deviceInterface,
|
||||
new SNWebCrypto(),
|
||||
WebCrypto,
|
||||
new AlertService(),
|
||||
identifier,
|
||||
undefined,
|
||||
@@ -78,7 +77,7 @@ export class WebApplication extends SNApplication {
|
||||
}
|
||||
|
||||
/** @override */
|
||||
deinit(source: DeinitSource) {
|
||||
deinit(source: DeinitSource): void {
|
||||
for (const service of Object.values(this.webServices)) {
|
||||
if ('deinit' in service) {
|
||||
service.deinit?.(source);
|
||||
@@ -98,24 +97,24 @@ export class WebApplication extends SNApplication {
|
||||
* to complete before destroying the global application instance and all its services */
|
||||
setTimeout(() => {
|
||||
super.deinit(source);
|
||||
}, 0)
|
||||
}, 0);
|
||||
}
|
||||
|
||||
onStart() {
|
||||
onStart(): void {
|
||||
super.onStart();
|
||||
this.componentManager!.openModalComponent = this.openModalComponent;
|
||||
this.componentManager!.presentPermissionsDialog = this.presentPermissionsDialog;
|
||||
}
|
||||
|
||||
setWebServices(services: WebServices) {
|
||||
setWebServices(services: WebServices): void {
|
||||
this.webServices = services;
|
||||
}
|
||||
|
||||
public getAppState() {
|
||||
public getAppState(): AppState {
|
||||
return this.webServices.appState;
|
||||
}
|
||||
|
||||
public getDesktopService() {
|
||||
public getDesktopService(): DesktopManager {
|
||||
return this.webServices.desktopService;
|
||||
}
|
||||
|
||||
@@ -158,58 +157,6 @@ export class WebApplication extends SNApplication {
|
||||
this.applicationElement.append(el);
|
||||
}
|
||||
|
||||
promptForChallenge(challenge: Challenge) {
|
||||
const scope: any = this.scope!.$new(true);
|
||||
scope.challenge = challenge;
|
||||
scope.application = this;
|
||||
const el = this.$compile!(
|
||||
"<challenge-modal " +
|
||||
"class='sk-modal' application='application' challenge='challenge'>" +
|
||||
"</challenge-modal>"
|
||||
)(scope);
|
||||
this.applicationElement.append(el);
|
||||
}
|
||||
|
||||
async presentPrivilegesModal(
|
||||
action: ProtectedAction,
|
||||
onSuccess?: any,
|
||||
onCancel?: any
|
||||
) {
|
||||
if (this.authenticationInProgress()) {
|
||||
onCancel && onCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
const customSuccess = async () => {
|
||||
onSuccess && await onSuccess();
|
||||
this.currentAuthenticationElement = undefined;
|
||||
};
|
||||
const customCancel = async () => {
|
||||
onCancel && await onCancel();
|
||||
this.currentAuthenticationElement = undefined;
|
||||
};
|
||||
|
||||
const scope: any = 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);
|
||||
this.applicationElement.append(el);
|
||||
|
||||
this.currentAuthenticationElement = el;
|
||||
}
|
||||
|
||||
presentPrivilegesManagementModal() {
|
||||
const scope: any = this.scope!.$new(true);
|
||||
scope.application = this;
|
||||
const el = this.$compile!("<privileges-management-modal application='application' class='sk-modal'></privileges-management-modal>")(scope);
|
||||
this.applicationElement.append(el);
|
||||
}
|
||||
|
||||
authenticationInProgress() {
|
||||
return this.currentAuthenticationElement != null;
|
||||
}
|
||||
@@ -254,7 +201,12 @@ export class WebApplication extends SNApplication {
|
||||
this.applicationElement.append(el);
|
||||
}
|
||||
|
||||
openModalComponent(component: SNComponent) {
|
||||
async openModalComponent(component: SNComponent): Promise<void> {
|
||||
if (component.package_info?.identifier === "org.standardnotes.batch-manager") {
|
||||
if (!await this.authorizeBatchManagerAccess()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const scope = this.scope!.$new(true) as Partial<ComponentModalScope>;
|
||||
scope.componentUuid = component.uuid;
|
||||
scope.application = this;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { SNComponent, ComponentArea, removeFromArray, addIfUnique } from '@standardnotes/snjs';
|
||||
import { SNComponent, ComponentArea, removeFromArray, addIfUnique , UuidString } from '@standardnotes/snjs';
|
||||
import { WebApplication } from './application';
|
||||
import { UuidString } from '@standardnotes/snjs';
|
||||
|
||||
|
||||
/** Areas that only allow a single component to be active */
|
||||
const SingleComponentAreas = [
|
||||
ComponentArea.Editor,
|
||||
ComponentArea.NoteTags,
|
||||
ComponentArea.TagsList
|
||||
]
|
||||
];
|
||||
|
||||
export class ComponentGroup {
|
||||
|
||||
@@ -20,7 +20,7 @@ export class ComponentGroup {
|
||||
}
|
||||
|
||||
get componentManager() {
|
||||
return this.application?.componentManager!;
|
||||
return this.application.componentManager!;
|
||||
}
|
||||
|
||||
public deinit() {
|
||||
@@ -91,7 +91,7 @@ export class ComponentGroup {
|
||||
callback();
|
||||
return () => {
|
||||
removeFromArray(this.changeObservers, callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private notifyObservers() {
|
||||
|
||||
@@ -68,7 +68,7 @@ export class EditorGroup {
|
||||
}
|
||||
return () => {
|
||||
removeFromArray(this.changeObservers, callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private notifyObservers() {
|
||||
|
||||
Reference in New Issue
Block a user