Editor component management simplification

This commit is contained in:
Mo Bitar
2020-04-15 13:11:10 -05:00
parent 0d44a2ff64
commit 1280c2ec52
7 changed files with 206 additions and 96 deletions

View File

@@ -8,7 +8,7 @@ import { ComponentMutator } from '@/../../../../snjs/dist/@types/models';
interface EditorMenuScope { interface EditorMenuScope {
callback: (component: SNComponent) => void callback: (component: SNComponent) => void
selectedEditor: SNComponent selectedEditorUuid: string
currentItem: SNItem currentItem: SNItem
application: WebApplication application: WebApplication
} }
@@ -16,7 +16,7 @@ interface EditorMenuScope {
class EditorMenuCtrl extends PureViewCtrl implements EditorMenuScope { class EditorMenuCtrl extends PureViewCtrl implements EditorMenuScope {
callback!: () => (component: SNComponent) => void callback!: () => (component: SNComponent) => void
selectedEditor!: SNComponent selectedEditorUuid!: string
currentItem!: SNItem currentItem!: SNItem
application!: WebApplication application!: WebApplication
@@ -30,6 +30,17 @@ class EditorMenuCtrl extends PureViewCtrl implements EditorMenuScope {
}; };
} }
public isEditorSelected(editor: SNComponent) {
if(!this.selectedEditorUuid) {
return false;
}
return this.selectedEditorUuid === editor.uuid;
}
public isEditorDefault(editor: SNComponent) {
return this.state.defaultEditor?.uuid === editor.uuid;
}
$onInit() { $onInit() {
super.$onInit(); super.$onInit();
const editors = this.application.componentManager!.componentsForArea(ComponentArea.Editor) const editors = this.application.componentManager!.componentsForArea(ComponentArea.Editor)
@@ -108,7 +119,7 @@ export class EditorMenu extends WebDirective {
this.bindToController = true; this.bindToController = true;
this.scope = { this.scope = {
callback: '&', callback: '&',
selectedEditor: '=', selectedEditorUuid: '=',
currentItem: '=', currentItem: '=',
application: '=' application: '='
}; };

View File

@@ -204,12 +204,19 @@ class ApplicationViewCtrl extends PureViewCtrl {
this.uploadSyncStatus, this.uploadSyncStatus,
`Syncing ${stats.uploadCompletionCount}/${stats.uploadTotalCount} items...` `Syncing ${stats.uploadCompletionCount}/${stats.uploadTotalCount} items...`
); );
} else if (this.uploadSyncStatus) { } else {
if (this.syncStatus) {
this.syncStatus = this.application!.getStatusService().removeStatus(
this.syncStatus
);
}
if (this.uploadSyncStatus) {
this.uploadSyncStatus = this.application!.getStatusService().removeStatus( this.uploadSyncStatus = this.application!.getStatusService().removeStatus(
this.uploadSyncStatus this.uploadSyncStatus
); );
} }
} }
}
openModalComponent(component: SNComponent) { openModalComponent(component: SNComponent) {
const scope = this.$rootScope!.$new(true) as ModalComponentScope; const scope = this.$rootScope!.$new(true) as ModalComponentScope;

View File

@@ -63,42 +63,42 @@
.sk-menu-panel-header .sk-menu-panel-header
.sk-menu-panel-header-title Note Options .sk-menu-panel-header-title Note Options
menu-row( menu-row(
action='self.selectedMenuItem(true); self.togglePin()', action='self.selectedMenuItem(true); self.togglePin()'
desc="'Pin or unpin a note from the top of your list'", desc="'Pin or unpin a note from the top of your list'",
label="self.note.pinned ? 'Unpin' : 'Pin'" label="self.note.pinned ? 'Unpin' : 'Pin'"
) )
menu-row( menu-row(
action='self.selectedMenuItem(true); self.toggleArchiveNote()', action='self.selectedMenuItem(true); self.toggleArchiveNote()'
desc="'Archive or unarchive a note from your Archived system tag'", desc="'Archive or unarchive a note from your Archived system tag'",
label="self.note.archived ? 'Unarchive' : 'Archive'" label="self.note.archived ? 'Unarchive' : 'Archive'"
) )
menu-row( menu-row(
action='self.selectedMenuItem(true); self.toggleLockNote()', action='self.selectedMenuItem(true); self.toggleLockNote()'
desc="'Locking notes prevents unintentional editing'", desc="'Locking notes prevents unintentional editing'",
label="self.noteLocked ? 'Unlock' : 'Lock'" label="self.noteLocked ? 'Unlock' : 'Lock'"
) )
menu-row( menu-row(
action='self.selectedMenuItem(true); self.toggleProtectNote()', action='self.selectedMenuItem(true); self.toggleProtectNote()'
desc=`'Protecting a note will require credentials to view desc=`'Protecting a note will require credentials to view
it (Manage Privileges via Account menu)'`, it (Manage Privileges via Account menu)'`,
label="self.note.protected ? 'Unprotect' : 'Protect'" label="self.note.protected ? 'Unprotect' : 'Protect'"
) )
menu-row( menu-row(
action='self.selectedMenuItem(true); self.toggleNotePreview()', action='self.selectedMenuItem(true); self.toggleNotePreview()'
circle="self.note.hidePreview ? 'danger' : 'success'", circle="self.note.hidePreview ? 'danger' : 'success'",
circle-align="'right'", circle-align="'right'",
desc="'Hide or unhide the note preview from the list of notes'", desc="'Hide or unhide the note preview from the list of notes'",
label="'Preview'" label="'Preview'"
) )
menu-row( menu-row(
action='self.selectedMenuItem(); self.deleteNote()', action='self.selectedMenuItem(); self.deleteNote()'
desc="'Send this note to the trash'", desc="'Send this note to the trash'",
label="'Move to Trash'", label="'Move to Trash'",
ng-show='!self.state.altKeyDown && !self.note.trashed && !self.note.errorDecrypting', ng-show='!self.state.altKeyDown && !self.note.trashed && !self.note.errorDecrypting',
stylekit-class="'warning'" stylekit-class="'warning'"
) )
menu-row( menu-row(
action='self.selectedMenuItem(); self.deleteNotePermanantely()', action='self.selectedMenuItem(); self.deleteNotePermanantely()'
desc="'Delete this note permanently from all your devices'", desc="'Delete this note permanently from all your devices'",
label="'Delete Permanently'", label="'Delete Permanently'",
ng-show='!self.note.trashed && self.note.errorDecrypting', ng-show='!self.note.trashed && self.note.errorDecrypting',
@@ -106,20 +106,20 @@
) )
div(ng-if='self.note.trashed || self.state.altKeyDown') div(ng-if='self.note.trashed || self.state.altKeyDown')
menu-row( menu-row(
action='self.selectedMenuItem(true); self.restoreTrashedNote()', action='self.selectedMenuItem(true); self.restoreTrashedNote()'
desc="'Undelete this note and restore it back into your notes'", desc="'Undelete this note and restore it back into your notes'",
label="'Restore'", label="'Restore'",
ng-show='self.note.trashed', ng-show='self.note.trashed',
stylekit-class="'info'" stylekit-class="'info'"
) )
menu-row( menu-row(
action='self.selectedMenuItem(true); self.deleteNotePermanantely()', action='self.selectedMenuItem(true); self.deleteNotePermanantely()'
desc="'Delete this note permanently from all your devices'", desc="'Delete this note permanently from all your devices'",
label="'Delete Permanently'", label="'Delete Permanently'",
stylekit-class="'danger'" stylekit-class="'danger'"
) )
menu-row( menu-row(
action='self.selectedMenuItem(true); self.emptyTrash()', action='self.selectedMenuItem(true); self.emptyTrash()'
desc="'Permanently delete all notes in the trash'", desc="'Permanently delete all notes in the trash'",
label="'Empty Trash'", label="'Empty Trash'",
ng-show='self.note.trashed || !self.state.altKeyDown', ng-show='self.note.trashed || !self.state.altKeyDown',
@@ -130,7 +130,7 @@
.sk-menu-panel-header .sk-menu-panel-header
.sk-menu-panel-header-title Global Display .sk-menu-panel-header-title Global Display
menu-row( menu-row(
action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeyMonospace)", action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeyMonospace)"
circle="self.state.monospaceEnabled ? 'success' : 'neutral'", circle="self.state.monospaceEnabled ? 'success' : 'neutral'",
desc="'Toggles the font style for the default editor'", desc="'Toggles the font style for the default editor'",
disabled='self.activeEditorComponent', disabled='self.activeEditorComponent',
@@ -138,7 +138,7 @@
subtitle="self.activeEditorComponent ? 'Not available with editor extensions' : null" subtitle="self.activeEditorComponent ? 'Not available with editor extensions' : null"
) )
menu-row( menu-row(
action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeySpellcheck)", action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeySpellcheck)"
circle="self.state.spellcheck ? 'success' : 'neutral'", circle="self.state.spellcheck ? 'success' : 'neutral'",
desc="'Toggles spellcheck for the default editor'", desc="'Toggles spellcheck for the default editor'",
disabled='self.activeEditorComponent', disabled='self.activeEditorComponent',
@@ -149,7 +149,7 @@
: (self.state.isDesktop ? 'May degrade editor performance' : null) : (self.state.isDesktop ? 'May degrade editor performance' : null)
`) `)
menu-row( menu-row(
action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeyMarginResizers)", action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeyMarginResizers)"
circle="self.state.marginResizersEnabled ? 'success' : 'neutral'", circle="self.state.marginResizersEnabled ? 'success' : 'neutral'",
desc="'Allows for editor left and right margins to be resized'", desc="'Allows for editor left and right margins to be resized'",
faded='!self.state.marginResizersEnabled', faded='!self.state.marginResizersEnabled',
@@ -166,7 +166,7 @@
callback='self.editorMenuOnSelect', callback='self.editorMenuOnSelect',
current-item='self.note', current-item='self.note',
ng-if='self.state.showEditorMenu', ng-if='self.state.showEditorMenu',
selected-editor='self.activeEditorComponent', selected-editor-uuid='self.activeEditorComponent && self.activeEditorComponent.uuid',
application='self.application' application='self.application'
) )
.sk-app-bar-item( .sk-app-bar-item(
@@ -192,9 +192,7 @@
ng-if='self.state.showSessionHistory', ng-if='self.state.showSessionHistory',
application='self.application' application='self.application'
) )
#editor-content.editor-content( #editor-content.editor-content(ng-if='!self.note.errorDecrypting')
ng-if='self.state.noteReady && !self.note.errorDecrypting'
)
panel-resizer.left( panel-resizer.left(
control='self.leftPanelPuppet', control='self.leftPanelPuppet',
hoverable='true', hoverable='true',
@@ -206,7 +204,7 @@
) )
component-view.component-view( component-view.component-view(
component-uuid='self.activeEditorComponent.uuid', component-uuid='self.activeEditorComponent.uuid',
ng-if='self.activeEditorComponent', ng-if='self.activeEditorComponent && !self.state.editorComponentUnloading',
on-load='self.onEditorLoad', on-load='self.onEditorLoad',
application='self.application' application='self.application'
) )
@@ -216,7 +214,7 @@
ng-change='self.contentChanged()', ng-change='self.contentChanged()',
ng-click='self.clickedTextArea()', ng-click='self.clickedTextArea()',
ng-focus='self.onContentFocus()', ng-focus='self.onContentFocus()',
ng-if='!self.activeEditorComponent', ng-if='!self.activeEditorComponent && !self.state.textareaUnloading',
ng-model='self.editorValues.text', ng-model='self.editorValues.text',
ng-model-options='{ debounce: self.state.editorDebounce}', ng-model-options='{ debounce: self.state.editorDebounce}',
ng-readonly='self.noteLocked', ng-readonly='self.noteLocked',

View File

@@ -70,10 +70,15 @@ type EditorState = {
isDesktop?: boolean isDesktop?: boolean
syncTakingTooLong: boolean syncTakingTooLong: boolean
showExtensions: boolean showExtensions: boolean
noteReady: boolean
showOptionsMenu: boolean showOptionsMenu: boolean
altKeyDown: boolean altKeyDown: boolean
spellcheck: boolean spellcheck: boolean
/** Setting to false then true will allow the current editor component-view to be destroyed
* then re-initialized. Used when changing between component editors. */
editorComponentUnloading: boolean
/** Setting to false then true will allow the main content textarea to be destroyed
* then re-initialized. Used when reloading spellcheck status. */
textareaUnloading: boolean
/** Fields that can be directly mutated by the template */ /** Fields that can be directly mutated by the template */
mutable: {} mutable: {}
} }
@@ -195,7 +200,6 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
editorDebounce: EDITOR_DEBOUNCE, editorDebounce: EDITOR_DEBOUNCE,
isDesktop: isDesktopApplication(), isDesktop: isDesktopApplication(),
spellcheck: true, spellcheck: true,
noteReady: true,
mutable: { mutable: {
tagsString: '' tagsString: ''
} }
@@ -287,18 +291,38 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
} }
async reloadComponentEditorState() { async reloadComponentEditorState() {
const associatedEditor = this.application.componentManager!.editorForNote(this.note) const associatedEditor = this.application.componentManager!.editorForNote(this.note);
if (associatedEditor && associatedEditor !== this.activeEditorComponent) { if (!associatedEditor) {
/** Setting note to not ready will remove the editor from view in a flash,
* so we only want to do this if switching between external editors */
await this.componentGroup.activateComponent(associatedEditor);
} else if (!associatedEditor) {
/** No editor */ /** No editor */
let changed = false;
if (this.activeEditorComponent) {
await this.componentGroup.deactivateComponentForArea(ComponentArea.Editor); await this.componentGroup.deactivateComponentForArea(ComponentArea.Editor);
changed = true;
} }
return associatedEditor; return { editor: undefined, changed };
} }
if (associatedEditor.uuid === this.activeEditorComponent?.uuid) {
/** Same editor, no change */
return { editor: associatedEditor, changed: false };
}
if (!this.activeEditorComponent) {
/** No existing editor set, set this one */
await this.componentGroup.activateComponent(associatedEditor);
} else {
/** Only remaining condition: editor is being changed. Unload current component
* view so that we create a new one, then change the active editor */
await this.setEditorState({
editorComponentUnloading: true
});
await this.componentGroup.activateComponent(associatedEditor);
await this.setEditorState({
editorComponentUnloading: false
});
}
return { editor: associatedEditor, changed: true };
}
/** /**
* Because note.locked accesses note.content.appData, * Because note.locked accesses note.content.appData,
@@ -352,10 +376,11 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
return; return;
} }
/** Find the most recent editor for note */ /** Find the most recent editor for note */
const editor = this.reloadComponentEditorState(); this.reloadComponentEditorState().then(({ editor, changed }) => {
if (!editor) { if (!editor && changed) {
this.reloadFont(); this.reloadFont();
} }
});
} }
); );
} }
@@ -399,7 +424,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
if (this.activeEditorComponent?.isExplicitlyEnabledForItem(this.note)) { if (this.activeEditorComponent?.isExplicitlyEnabledForItem(this.note)) {
await this.disassociateComponentWithCurrentNote(this.activeEditorComponent); await this.disassociateComponentWithCurrentNote(this.activeEditorComponent);
} }
await this.componentGroup.deactivateComponentForArea(ComponentArea.Editor); await this.reloadComponentEditorState();
this.reloadFont(); this.reloadFont();
} }
else if (component.area === ComponentArea.Editor) { else if (component.area === ComponentArea.Editor) {
@@ -415,7 +440,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
}) })
} }
await this.associateComponentWithCurrentNote(component); await this.associateComponentWithCurrentNote(component);
await this.componentGroup.activateComponent(component); await this.reloadComponentEditorState();
} }
else if (component.area === ComponentArea.EditorStack) { else if (component.area === ComponentArea.EditorStack) {
await this.toggleStackComponentForCurrentItem(component); await this.toggleStackComponentForCurrentItem(component);
@@ -962,12 +987,8 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
if (key === WebPrefKey.EditorSpellcheck) { if (key === WebPrefKey.EditorSpellcheck) {
/** Allows textarea to reload */ /** Allows textarea to reload */
await this.setEditorState({ await this.setEditorState({ textareaUnloading: false });
noteReady: false this.setEditorState({ textareaUnloading: true });
});
this.setEditorState({
noteReady: true
});
this.reloadFont(); this.reloadFont();
} else if (key === WebPrefKey.EditorResizersEnabled && (this as any)[key] === true) { } else if (key === WebPrefKey.EditorResizersEnabled && (this as any)[key] === true) {
this.$timeout(() => { this.$timeout(() => {

View File

@@ -5,17 +5,17 @@
.sk-menu-panel-header-title Note Editor .sk-menu-panel-header-title Note Editor
menu-row( menu-row(
action='self.selectComponent(null)', action='self.selectComponent(null)',
circle="self.selectedEditor == null && 'success'", circle="!self.selectedEditorUuid && 'success'",
label="'Plain Editor'" label="'Plain Editor'"
) )
menu-row( menu-row(
ng-repeat='editor in self.state.editors track by editor.uuid' ng-repeat='editor in self.state.editors track by editor.uuid'
action='self.selectComponent(editor)', action='self.selectComponent(editor)',
button-action='self.toggleDefaultForEditor(editor)', button-action='self.toggleDefaultForEditor(editor)',
button-class="self.state.defaultEditor == editor ? 'warning' : 'info'", button-class="self.isEditorSelected(editor) ? 'warning' : 'info'",
button-text="self.state.defaultEditor == editor ? 'Undefault' : 'Set Default'", button-text="self.isEditorDefault(editor) ? 'Undefault' : 'Set Default'",
circle="self.selectedEditor === editor && 'success'", circle="self.isEditorSelected(editor) && 'success'",
has-button='self.selectedEditor == editor || self.state.defaultEditor == editor', has-button='self.isEditorSelected(editor) || isEditorDefault(editor)',
label='editor.name', label='editor.name',
) )
.sk-menu-panel-column( .sk-menu-panel-column(

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long