feat: component viewer (#781)
* wip: component viewer * feat: get component status from component viewer * fix: remove unused property * chore(deps): snjs 2.29.0 * fix: import location
This commit is contained in:
@@ -77,6 +77,8 @@ export class AppState {
|
||||
editingTag: SNTag | undefined;
|
||||
_templateTag: SNTag | undefined;
|
||||
|
||||
private multiEditorSupport = false;
|
||||
|
||||
readonly quickSettingsMenu = new QuickSettingsState();
|
||||
readonly accountMenu: AccountMenuState;
|
||||
readonly actionsMenu = new ActionsMenuState();
|
||||
@@ -224,27 +226,21 @@ export class AppState {
|
||||
storage.set(StorageKey.ShowBetaWarning, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new editor if one doesn't exist. If one does, we'll replace the
|
||||
* editor's note with an empty one.
|
||||
*/
|
||||
async createEditor(title?: string) {
|
||||
const activeEditor = this.getActiveEditor();
|
||||
if (!this.multiEditorSupport) {
|
||||
this.closeActiveEditor();
|
||||
}
|
||||
const activeTagUuid = this.selectedTag
|
||||
? this.selectedTag.isSmartTag
|
||||
? undefined
|
||||
: this.selectedTag.uuid
|
||||
: undefined;
|
||||
|
||||
if (!activeEditor) {
|
||||
this.application.editorGroup.createEditor(
|
||||
undefined,
|
||||
title,
|
||||
activeTagUuid
|
||||
);
|
||||
} else {
|
||||
await activeEditor.reset(title, activeTagUuid);
|
||||
}
|
||||
await this.application.editorGroup.createEditor(
|
||||
undefined,
|
||||
title,
|
||||
activeTagUuid
|
||||
);
|
||||
}
|
||||
|
||||
getActiveEditor() {
|
||||
|
||||
@@ -167,12 +167,12 @@ export class NotesState {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.activeEditor) {
|
||||
this.application.editorGroup.createEditor(noteUuid);
|
||||
} else {
|
||||
this.activeEditor.setNote(note);
|
||||
if (this.activeEditor) {
|
||||
this.application.editorGroup.closeActiveEditor();
|
||||
}
|
||||
|
||||
await this.application.editorGroup.createEditor(noteUuid);
|
||||
|
||||
this.appState.noteTags.reloadTags();
|
||||
await this.onActiveEditorChanged();
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
import { ContentType, SNSmartTag, SNTag, UuidString } from '@standardnotes/snjs';
|
||||
import {
|
||||
ContentType,
|
||||
SNSmartTag,
|
||||
SNTag,
|
||||
UuidString,
|
||||
} from '@standardnotes/snjs';
|
||||
import {
|
||||
action,
|
||||
computed,
|
||||
makeAutoObservable,
|
||||
makeObservable,
|
||||
observable,
|
||||
runInAction
|
||||
runInAction,
|
||||
} from 'mobx';
|
||||
import { WebApplication } from '../application';
|
||||
import { FeaturesState } from './features_state';
|
||||
|
||||
|
||||
export class TagsState {
|
||||
tags: SNTag[] = [];
|
||||
smartTags: SNSmartTag[] = [];
|
||||
|
||||
@@ -18,12 +18,9 @@ import {
|
||||
PermissionDialog,
|
||||
Platform,
|
||||
SNApplication,
|
||||
SNComponent,
|
||||
} from '@standardnotes/snjs';
|
||||
import angular from 'angular';
|
||||
import { ComponentModalScope } from './../directives/views/componentModal';
|
||||
import { AccountSwitcherScope, PermissionsModalScope } from './../types';
|
||||
import { ComponentGroup } from './component_group';
|
||||
|
||||
type WebServices = {
|
||||
appState: AppState;
|
||||
@@ -40,7 +37,6 @@ export class WebApplication extends SNApplication {
|
||||
private webServices!: WebServices;
|
||||
private currentAuthenticationElement?: angular.IRootElementService;
|
||||
public editorGroup: EditorGroup;
|
||||
public componentGroup: ComponentGroup;
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
@@ -71,8 +67,6 @@ export class WebApplication extends SNApplication {
|
||||
this.scope = scope;
|
||||
deviceInterface.setApplication(this);
|
||||
this.editorGroup = new EditorGroup(this);
|
||||
this.componentGroup = new ComponentGroup(this);
|
||||
this.openModalComponent = this.openModalComponent.bind(this);
|
||||
this.presentPermissionsDialog = this.presentPermissionsDialog.bind(this);
|
||||
}
|
||||
|
||||
@@ -85,14 +79,12 @@ export class WebApplication extends SNApplication {
|
||||
(service as any).application = undefined;
|
||||
}
|
||||
this.webServices = {} as WebServices;
|
||||
(this.$compile as any) = undefined;
|
||||
(this.$compile as unknown) = undefined;
|
||||
this.editorGroup.deinit();
|
||||
this.componentGroup.deinit();
|
||||
(this.scope! as any).application = undefined;
|
||||
(this.scope as any).application = undefined;
|
||||
this.scope!.$destroy();
|
||||
this.scope = undefined;
|
||||
(this.openModalComponent as any) = undefined;
|
||||
(this.presentPermissionsDialog as any) = undefined;
|
||||
(this.presentPermissionsDialog as unknown) = 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 */
|
||||
setTimeout(() => {
|
||||
@@ -105,8 +97,7 @@ export class WebApplication extends SNApplication {
|
||||
|
||||
onStart(): void {
|
||||
super.onStart();
|
||||
this.componentManager!.openModalComponent = this.openModalComponent;
|
||||
this.componentManager!.presentPermissionsDialog =
|
||||
this.componentManager.presentPermissionsDialog =
|
||||
this.presentPermissionsDialog;
|
||||
}
|
||||
|
||||
@@ -210,24 +201,6 @@ export class WebApplication extends SNApplication {
|
||||
this.applicationElement.append(el);
|
||||
}
|
||||
|
||||
async openModalComponent(component: SNComponent): Promise<void> {
|
||||
switch (component.package_info?.identifier) {
|
||||
case 'org.standardnotes.cloudlink':
|
||||
if (!(await this.authorizeCloudLinkAccess())) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
const scope = this.scope!.$new(true) as Partial<ComponentModalScope>;
|
||||
scope.componentUuid = component.uuid;
|
||||
scope.application = this;
|
||||
const el = this.$compile!(
|
||||
"<component-modal application='application' component-uuid='componentUuid' " +
|
||||
"class='sk-modal'></component-modal>"
|
||||
)(scope as any);
|
||||
this.applicationElement.append(el);
|
||||
}
|
||||
|
||||
presentPermissionsDialog(dialog: PermissionDialog) {
|
||||
const scope = this.scope!.$new(true) as PermissionsModalScope;
|
||||
scope.permissionsString = dialog.permissionsString;
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
import { SNComponent, ComponentArea, removeFromArray, addIfUnique , UuidString } from '@standardnotes/snjs';
|
||||
import { WebApplication } from './application';
|
||||
|
||||
/** Areas that only allow a single component to be active */
|
||||
const SingleComponentAreas = [
|
||||
ComponentArea.Editor,
|
||||
ComponentArea.NoteTags,
|
||||
ComponentArea.TagsList
|
||||
];
|
||||
|
||||
export class ComponentGroup {
|
||||
|
||||
private application: WebApplication
|
||||
changeObservers: any[] = []
|
||||
activeComponents: UuidString[] = []
|
||||
|
||||
constructor(application: WebApplication) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
get componentManager() {
|
||||
return this.application.componentManager!;
|
||||
}
|
||||
|
||||
public deinit() {
|
||||
(this.application as any) = undefined;
|
||||
}
|
||||
|
||||
async activateComponent(component: SNComponent) {
|
||||
if (this.activeComponents.includes(component.uuid)) {
|
||||
return;
|
||||
}
|
||||
if (SingleComponentAreas.includes(component.area)) {
|
||||
const currentActive = this.activeComponentForArea(component.area);
|
||||
if (currentActive) {
|
||||
await this.deactivateComponent(currentActive, false);
|
||||
}
|
||||
}
|
||||
addIfUnique(this.activeComponents, component.uuid);
|
||||
await this.componentManager.activateComponent(component.uuid);
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
||||
async deactivateComponent(component: SNComponent, notify = true) {
|
||||
if (!this.activeComponents.includes(component.uuid)) {
|
||||
return;
|
||||
}
|
||||
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
|
||||
* essentially be deregistered. */
|
||||
if(this.componentManager) {
|
||||
await this.componentManager.deactivateComponent(component.uuid);
|
||||
if(notify) {
|
||||
this.notifyObservers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async deactivateComponentForArea(area: ComponentArea) {
|
||||
const component = this.activeComponentForArea(area);
|
||||
if (component) {
|
||||
return this.deactivateComponent(component);
|
||||
}
|
||||
}
|
||||
|
||||
activeComponentForArea(area: ComponentArea) {
|
||||
return this.activeComponentsForArea(area)[0];
|
||||
}
|
||||
|
||||
activeComponentsForArea(area: ComponentArea) {
|
||||
return this.allActiveComponents().filter((c) => c.area === area);
|
||||
}
|
||||
|
||||
allComponentsForArea(area: ComponentArea) {
|
||||
return this.componentManager.componentsForArea(area);
|
||||
}
|
||||
|
||||
private allActiveComponents() {
|
||||
return this.application.getAll(this.activeComponents) as SNComponent[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies observer when the active editor has changed.
|
||||
*/
|
||||
public addChangeObserver(callback: () => void) {
|
||||
this.changeObservers.push(callback);
|
||||
callback();
|
||||
return () => {
|
||||
removeFromArray(this.changeObservers, callback);
|
||||
};
|
||||
}
|
||||
|
||||
private notifyObservers() {
|
||||
for (const observer of this.changeObservers) {
|
||||
observer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,37 +3,36 @@ import {
|
||||
ContentType,
|
||||
PayloadSource,
|
||||
UuidString,
|
||||
TagMutator,
|
||||
SNTag,
|
||||
} from '@standardnotes/snjs';
|
||||
import { WebApplication } from './application';
|
||||
import { NoteTagsState } from './app_state/note_tags_state';
|
||||
|
||||
export class Editor {
|
||||
public note!: SNNote;
|
||||
private application: WebApplication;
|
||||
private _onNoteChange?: () => void;
|
||||
private _onNoteValueChange?: (note: SNNote, source?: PayloadSource) => void;
|
||||
private onNoteValueChange?: (note: SNNote, source: PayloadSource) => void;
|
||||
private removeStreamObserver?: () => void;
|
||||
public isTemplateNote = false;
|
||||
|
||||
constructor(
|
||||
application: WebApplication,
|
||||
noteUuid: string | undefined,
|
||||
noteTitle: string | undefined,
|
||||
noteTag: UuidString | undefined
|
||||
private defaultTitle: string | undefined,
|
||||
private defaultTag: UuidString | undefined
|
||||
) {
|
||||
this.application = application;
|
||||
if (noteUuid) {
|
||||
this.note = application.findItem(noteUuid) as SNNote;
|
||||
this.streamItems();
|
||||
} else {
|
||||
this.reset(noteTitle, noteTag)
|
||||
.then(() => this.streamItems())
|
||||
.catch(console.error);
|
||||
}
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
if (!this.note) {
|
||||
await this.createTemplateNote(this.defaultTitle, this.defaultTag);
|
||||
}
|
||||
this.streamItems();
|
||||
}
|
||||
|
||||
private streamItems() {
|
||||
this.removeStreamObserver = this.application.streamItems(
|
||||
ContentType.Note,
|
||||
@@ -45,14 +44,12 @@ export class Editor {
|
||||
|
||||
deinit() {
|
||||
this.removeStreamObserver?.();
|
||||
(this.removeStreamObserver as any) = undefined;
|
||||
this._onNoteChange = undefined;
|
||||
(this.application as any) = undefined;
|
||||
this._onNoteChange = undefined;
|
||||
this._onNoteValueChange = undefined;
|
||||
(this.removeStreamObserver as unknown) = undefined;
|
||||
(this.application as unknown) = undefined;
|
||||
this.onNoteValueChange = undefined;
|
||||
}
|
||||
|
||||
private handleNoteStream(notes: SNNote[], source?: PayloadSource) {
|
||||
private handleNoteStream(notes: SNNote[], source: PayloadSource) {
|
||||
/** Update our note object reference whenever it changes */
|
||||
const matchingNote = notes.find((item) => {
|
||||
return item.uuid === this.note.uuid;
|
||||
@@ -60,7 +57,7 @@ export class Editor {
|
||||
if (matchingNote) {
|
||||
this.isTemplateNote = false;
|
||||
this.note = matchingNote;
|
||||
this._onNoteValueChange && this._onNoteValueChange!(matchingNote, source);
|
||||
this.onNoteValueChange?.(matchingNote, source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,53 +70,31 @@ export class Editor {
|
||||
* Reverts the editor to a blank state, removing any existing note from view,
|
||||
* and creating a placeholder note.
|
||||
*/
|
||||
async reset(noteTitle = '', noteTag?: UuidString) {
|
||||
async createTemplateNote(defaultTitle?: string, noteTag?: UuidString) {
|
||||
const note = (await this.application.createTemplateItem(ContentType.Note, {
|
||||
text: '',
|
||||
title: noteTitle,
|
||||
title: defaultTitle,
|
||||
references: [],
|
||||
})) as SNNote;
|
||||
if (noteTag) {
|
||||
const tag = this.application.findItem(noteTag) as SNTag;
|
||||
await this.application.addTagHierarchyToNote(note, tag);
|
||||
}
|
||||
if (!this.isTemplateNote || this.note.title !== note.title) {
|
||||
this.setNote(note as SNNote, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register to be notified when the editor's note changes.
|
||||
*/
|
||||
public onNoteChange(callback: () => void) {
|
||||
this._onNoteChange = callback;
|
||||
if (this.note) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
public clearNoteChangeListener() {
|
||||
this._onNoteChange = undefined;
|
||||
this.isTemplateNote = true;
|
||||
this.note = note;
|
||||
this.onNoteValueChange?.(this.note, this.note.payload.source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register to be notified when the editor's note's values change
|
||||
* (and thus a new object reference is created)
|
||||
*/
|
||||
public onNoteValueChange(
|
||||
callback: (note: SNNote, source?: PayloadSource) => void
|
||||
public setOnNoteValueChange(
|
||||
callback: (note: SNNote, source: PayloadSource) => void
|
||||
) {
|
||||
this._onNoteValueChange = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the editor contents by setting its note.
|
||||
*/
|
||||
public setNote(note: SNNote, isTemplate = false) {
|
||||
this.note = note;
|
||||
this.isTemplateNote = isTemplate;
|
||||
if (this._onNoteChange) {
|
||||
this._onNoteChange();
|
||||
this.onNoteValueChange = callback;
|
||||
if (this.note) {
|
||||
this.onNoteValueChange(this.note, this.note.payload.source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,31 +2,31 @@ import { removeFromArray, UuidString } from '@standardnotes/snjs';
|
||||
import { Editor } from './editor';
|
||||
import { WebApplication } from './application';
|
||||
|
||||
type EditorGroupChangeCallback = () => void
|
||||
type EditorGroupChangeCallback = () => void;
|
||||
|
||||
export class EditorGroup {
|
||||
|
||||
public editors: Editor[] = []
|
||||
private application: WebApplication
|
||||
changeObservers: EditorGroupChangeCallback[] = []
|
||||
public editors: Editor[] = [];
|
||||
private application: WebApplication;
|
||||
changeObservers: EditorGroupChangeCallback[] = [];
|
||||
|
||||
constructor(application: WebApplication) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
public deinit() {
|
||||
(this.application as any) = undefined;
|
||||
(this.application as unknown) = undefined;
|
||||
for (const editor of this.editors) {
|
||||
this.deleteEditor(editor);
|
||||
}
|
||||
}
|
||||
|
||||
createEditor(
|
||||
async createEditor(
|
||||
noteUuid?: string,
|
||||
noteTitle?: string,
|
||||
noteTag?: UuidString
|
||||
) {
|
||||
const editor = new Editor(this.application, noteUuid, noteTitle, noteTag);
|
||||
await editor.initialize();
|
||||
this.editors.push(editor);
|
||||
this.notifyObservers();
|
||||
}
|
||||
@@ -43,13 +43,13 @@ export class EditorGroup {
|
||||
|
||||
closeActiveEditor() {
|
||||
const activeEditor = this.activeEditor;
|
||||
if(activeEditor) {
|
||||
if (activeEditor) {
|
||||
this.deleteEditor(activeEditor);
|
||||
}
|
||||
}
|
||||
|
||||
closeAllEditors() {
|
||||
for(const editor of this.editors) {
|
||||
for (const editor of this.editors) {
|
||||
this.deleteEditor(editor);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user