fix: enable, disable, sync editor stack per note

This commit is contained in:
Baptiste Grob
2020-07-07 12:01:11 +02:00
parent e45d28b07a
commit e280309be6
2 changed files with 181 additions and 178 deletions

View File

@@ -1,9 +1,9 @@
#editor-column.section.editor.sn-component(aria-label='Note') #editor-column.section.editor.sn-component(aria-label='Note')
.sn-component .sn-component
.sk-app-bar.no-edges( .sk-app-bar.no-edges(
ng-if='self.noteLocked', ng-if='self.noteLocked',
ng-init="self.lockText = 'Note Locked'", ng-init="self.lockText = 'Note Locked'",
ng-mouseleave="self.lockText = 'Note Locked'", ng-mouseleave="self.lockText = 'Note Locked'",
ng-mouseover="self.lockText = 'Unlock'" ng-mouseover="self.lockText = 'Unlock'"
) )
.left .left
@@ -12,17 +12,17 @@
i.icon.ion-locked i.icon.ion-locked
| {{self.lockText}} | {{self.lockText}}
#editor-title-bar.section-title-bar( #editor-title-bar.section-title-bar(
ng-class="{'locked' : self.noteLocked}", ng-class="{'locked' : self.noteLocked}",
ng-show='self.note && !self.note.errorDecrypting' ng-show='self.note && !self.note.errorDecrypting'
) )
.title .title
input#note-title-editor.input( input#note-title-editor.input(
ng-blur='self.onTitleBlur()', ng-blur='self.onTitleBlur()',
ng-change='self.onTitleChange()', ng-change='self.onTitleChange()',
ng-disabled='self.noteLocked', ng-disabled='self.noteLocked',
ng-focus='self.onTitleFocus()', ng-focus='self.onTitleFocus()',
ng-keyup='$event.keyCode == 13 && self.onTitleEnter($event)', ng-keyup='$event.keyCode == 13 && self.onTitleEnter($event)',
ng-model='self.editorValues.title', ng-model='self.editorValues.title',
select-on-focus='true', select-on-focus='true',
spellcheck='false' spellcheck='false'
) )
@@ -34,28 +34,28 @@
.editor-tags .editor-tags
#note-tags-component-container(ng-if='self.activeTagsComponent') #note-tags-component-container(ng-if='self.activeTagsComponent')
component-view.component-view( component-view.component-view(
component-uuid='self.activeTagsComponent.uuid', component-uuid='self.activeTagsComponent.uuid',
ng-class="{'locked' : self.noteLocked}", ng-class="{'locked' : self.noteLocked}",
ng-style="self.noteLocked && {'pointer-events' : 'none'}", ng-style="self.noteLocked && {'pointer-events' : 'none'}",
application='self.application' application='self.application'
) )
input.tags-input( input.tags-input(
ng-blur='self.onTagsInputBlur()', ng-blur='self.onTagsInputBlur()',
ng-disabled='self.noteLocked', ng-disabled='self.noteLocked',
ng-if='!self.activeTagsComponent', ng-if='!self.activeTagsComponent',
ng-keyup='$event.keyCode == 13 && $event.target.blur();', ng-keyup='$event.keyCode == 13 && $event.target.blur();',
ng-model='self.editorValues.tagsInputValue', ng-model='self.editorValues.tagsInputValue',
placeholder='#tags', placeholder='#tags',
spellcheck='false', spellcheck='false',
type='text' type='text'
) )
.sn-component(ng-if='self.note') .sn-component(ng-if='self.note')
#editor-menu-bar.sk-app-bar.no-edges #editor-menu-bar.sk-app-bar.no-edges
.left .left
.sk-app-bar-item( .sk-app-bar-item(
click-outside=`self.setMenuState('showOptionsMenu', false)`, click-outside=`self.setMenuState('showOptionsMenu', false)`,
is-open='self.state.showOptionsMenu', is-open='self.state.showOptionsMenu',
ng-class="{'selected' : self.state.showOptionsMenu}", ng-class="{'selected' : self.state.showOptionsMenu}",
ng-click="self.toggleMenu('showOptionsMenu')" ng-click="self.toggleMenu('showOptionsMenu')"
) )
.sk-label Options .sk-label Options
@@ -64,182 +64,182 @@
.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',
stylekit-class="'danger'" stylekit-class="'danger'"
) )
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',
stylekit-class="'danger'", stylekit-class="'danger'",
subtitle="self.getTrashCount() + ' notes in trash'" subtitle="self.getTrashCount() + ' notes in trash'"
) )
.sk-menu-panel-section .sk-menu-panel-section
.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.monospaceFont ? 'success' : 'neutral'", circle="self.state.monospaceFont ? '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',
label="'Monospace Font'", label="'Monospace Font'",
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',
label="'Spellcheck'", label="'Spellcheck'",
subtitle=` subtitle=`
self.activeEditorComponent self.activeEditorComponent
? 'Not available with editor extensions' ? 'Not available with editor extensions'
: (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',
label="'Margin Resizers'" label="'Margin Resizers'"
) )
.sk-app-bar-item( .sk-app-bar-item(
click-outside=`self.setMenuState('showEditorMenu', false)` click-outside=`self.setMenuState('showEditorMenu', false)`
is-open='self.state.showEditorMenu', is-open='self.state.showEditorMenu',
ng-class="{'selected' : self.state.showEditorMenu}", ng-class="{'selected' : self.state.showEditorMenu}",
ng-click="self.toggleMenu('showEditorMenu')" ng-click="self.toggleMenu('showEditorMenu')"
) )
.sk-label Editor .sk-label Editor
editor-menu( editor-menu(
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-uuid='self.activeEditorComponent && self.activeEditorComponent.uuid', selected-editor-uuid='self.activeEditorComponent && self.activeEditorComponent.uuid',
application='self.application' application='self.application'
) )
.sk-app-bar-item( .sk-app-bar-item(
click-outside=`self.setMenuState('showExtensions', false)`, click-outside=`self.setMenuState('showExtensions', false)`,
is-open='self.state.showExtensions', is-open='self.state.showExtensions',
ng-class="{'selected' : self.state.showExtensions}", ng-class="{'selected' : self.state.showExtensions}",
ng-click="self.toggleMenu('showExtensions')" ng-click="self.toggleMenu('showExtensions')"
) )
.sk-label Actions .sk-label Actions
actions-menu( actions-menu(
item='self.note', item='self.note',
ng-if='self.state.showExtensions', ng-if='self.state.showExtensions',
application='self.application' application='self.application'
) )
.sk-app-bar-item( .sk-app-bar-item(
click-outside=`self.setMenuState('showSessionHistory', false)`, click-outside=`self.setMenuState('showSessionHistory', false)`,
is-open='self.state.showSessionHistory', is-open='self.state.showSessionHistory',
ng-click="self.toggleMenu('showSessionHistory')" ng-click="self.toggleMenu('showSessionHistory')"
) )
.sk-label Session History .sk-label Session History
session-history-menu( session-history-menu(
item='self.note', item='self.note',
ng-if='self.state.showSessionHistory', ng-if='self.state.showSessionHistory',
application='self.application' application='self.application'
) )
#editor-content.editor-content(ng-if='!self.note.errorDecrypting') #editor-content.editor-content(ng-if='!self.note.errorDecrypting')
panel-resizer.left( panel-resizer.left(
control='self.leftPanelPuppet', control='self.leftPanelPuppet',
hoverable='true', hoverable='true',
min-width='300', min-width='300',
ng-if='self.state.marginResizersEnabled', ng-if='self.state.marginResizersEnabled',
on-resize-finish='self.onPanelResizeFinish', on-resize-finish='self.onPanelResizeFinish',
panel-id="'editor-content'", panel-id="'editor-content'",
property="'left'" property="'left'"
) )
component-view.component-view( component-view.component-view(
component-uuid='self.activeEditorComponent.uuid', component-uuid='self.activeEditorComponent.uuid',
ng-if='self.activeEditorComponent && !self.state.editorComponentUnloading', ng-if='self.activeEditorComponent && !self.state.editorComponentUnloading',
on-load='self.onEditorLoad', on-load='self.onEditorLoad',
application='self.application' application='self.application'
) )
textarea#note-text-editor.editable( textarea#note-text-editor.editable(
dir='auto', dir='auto',
ng-attr-spellcheck='{{self.state.spellcheck}}', ng-attr-spellcheck='{{self.state.spellcheck}}',
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 && !self.state.textareaUnloading', 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',
ng-trim='false' ng-trim='false'
) )
| {{self.onSystemEditorLoad()}} | {{self.onSystemEditorLoad()}}
panel-resizer( panel-resizer(
control='self.rightPanelPuppet', control='self.rightPanelPuppet',
hoverable='true', min-width='300', hoverable='true', min-width='300',
ng-if='self.state.marginResizersEnabled', ng-if='self.state.marginResizersEnabled',
on-resize-finish='self.onPanelResizeFinish', on-resize-finish='self.onPanelResizeFinish',
panel-id="'editor-content'", panel-id="'editor-content'",
property="'right'" property="'right'"
) )
.section(ng-show='self.note.errorDecrypting') .section(ng-show='self.note.errorDecrypting')
p.medium-padding(style='padding-top: 0 !important;') p.medium-padding(style='padding-top: 0 !important;')
| There was an error decrypting this item. Ensure you are running the | There was an error decrypting this item. Ensure you are running the
| latest version of this app, then sign out and sign back in to try again. | latest version of this app, then sign out and sign back in to try again.
#editor-pane-component-stack(ng-show='self.note') #editor-pane-component-stack(ng-show='self.note')
#component-stack-menu-bar.sk-app-bar.no-edges(ng-if='self.state.allStackComponents.length') #component-stack-menu-bar.sk-app-bar.no-edges(ng-if='self.state.allStackComponents.length')
.left .left
.sk-app-bar-item( .sk-app-bar-item(
ng-repeat='component in self.state.allStackComponents track by component.uuid' ng-repeat='component in self.state.allStackComponents track by component.uuid'
ng-click='self.toggleStackComponentForCurrentItem(component)', ng-click='self.toggleStackComponentForCurrentItem(component)',
) )
.sk-app-bar-item-column .sk-app-bar-item-column
.sk-circle.small( .sk-circle.small(
@@ -249,9 +249,9 @@
.sk-label {{component.name}} .sk-label {{component.name}}
.sn-component .sn-component
component-view.component-view.component-stack-item( component-view.component-view.component-stack-item(
ng-repeat='component in self.state.activeStackComponents track by component.uuid', ng-repeat='component in self.state.allStackComponents track by component.uuid',
component-uuid='component.uuid', component-uuid='component.uuid',
manual-dealloc='true', manual-dealloc='true',
ng-show='!self.stackComponentHidden(component)', ng-show='!self.stackComponentHidden(component)',
application='self.application' application='self.application'
) )

View File

@@ -15,7 +15,7 @@ import {
ComponentArea, ComponentArea,
ComponentAction, ComponentAction,
WebPrefKey, WebPrefKey,
ComponentMutator ComponentMutator,
} from 'snjs'; } from 'snjs';
import find from 'lodash/find'; import find from 'lodash/find';
import { isDesktopApplication } from '@/utils'; import { isDesktopApplication } from '@/utils';
@@ -90,12 +90,11 @@ type EditorValues = {
tagsInputValue?: string tagsInputValue?: string
} }
interface EditorViewScope { function sortAlphabetically(array: SNComponent[]): SNComponent[] {
application: WebApplication return array.sort((a, b) => a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1);
editor: Editor
} }
class EditorViewCtrl extends PureViewCtrl implements EditorViewScope { class EditorViewCtrl extends PureViewCtrl<{}, EditorState> {
/** Passed through template */ /** Passed through template */
readonly application!: WebApplication readonly application!: WebApplication
readonly editor!: Editor readonly editor!: Editor
@@ -210,19 +209,16 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
const currentEditor = this.activeEditorComponent; const currentEditor = this.activeEditorComponent;
const newEditor = this.componentGroup.activeComponentForArea(ComponentArea.Editor); const newEditor = this.componentGroup.activeComponentForArea(ComponentArea.Editor);
if (currentEditor && newEditor && currentEditor.uuid !== newEditor.uuid) { if (currentEditor && newEditor && currentEditor.uuid !== newEditor.uuid) {
/** Unload current component view so that we create a new one, /** Unload current component view so that we create a new one,
* then change the active editor */ * then change the active editor */
await this.setEditorState({ await this.setState({
editorComponentUnloading: true editorComponentUnloading: true
}); });
} }
await this.setEditorState({ this.setState({
activeEditorComponent: newEditor, activeEditorComponent: newEditor,
activeTagsComponent: this.componentGroup.activeComponentForArea(ComponentArea.NoteTags), activeTagsComponent: this.componentGroup.activeComponentForArea(ComponentArea.NoteTags),
activeStackComponents: this.componentGroup.activeComponentsForArea(ComponentArea.EditorStack) /** Stop unloading, if we were already unloading */
})
/** Stop unloading, if we were already unloading */
await this.setEditorState({
editorComponentUnloading: false editorComponentUnloading: false
}); });
} }
@@ -237,14 +233,19 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
editorDebounce: EDITOR_DEBOUNCE, editorDebounce: EDITOR_DEBOUNCE,
isDesktop: isDesktopApplication(), isDesktop: isDesktopApplication(),
spellcheck: true, spellcheck: true,
syncTakingTooLong: false,
showExtensions: false,
showOptionsMenu: false,
showEditorMenu: false,
showSessionHistory: false,
altKeyDown: false,
noteStatus: undefined,
editorComponentUnloading: false,
textareaUnloading: false,
mutable: { mutable: {
tagsString: '' tagsString: ''
} }
} as Partial<EditorState>; } as EditorState;
}
async setEditorState(state: Partial<EditorState>) {
return this.setState(state);
} }
async onAppLaunch() { async onAppLaunch() {
@@ -263,10 +264,10 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
/** @override */ /** @override */
onAppEvent(eventName: ApplicationEvent) { onAppEvent(eventName: ApplicationEvent) {
if (eventName === ApplicationEvent.HighLatencySync) { if (eventName === ApplicationEvent.HighLatencySync) {
this.setEditorState({ syncTakingTooLong: true }); this.setState({ syncTakingTooLong: true });
} else if (eventName === ApplicationEvent.CompletedFullSync) { } else if (eventName === ApplicationEvent.CompletedFullSync) {
this.setEditorState({ syncTakingTooLong: false }); this.setState({ syncTakingTooLong: false });
const isInErrorState = this.getState().saveError; const isInErrorState = this.state.saveError;
/** if we're still dirty, don't change status, a sync is likely upcoming. */ /** if we're still dirty, don't change status, a sync is likely upcoming. */
if (!this.note.dirty && isInErrorState) { if (!this.note.dirty && isInErrorState) {
this.showAllChangesSavedStatus(); this.showAllChangesSavedStatus();
@@ -289,11 +290,11 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
} }
get activeEditorComponent() { get activeEditorComponent() {
return this.getState().activeEditorComponent; return this.state.activeEditorComponent;
} }
get activeTagsComponent() { get activeTagsComponent() {
return this.getState().activeTagsComponent; return this.state.activeTagsComponent;
} }
get componentGroup() { get componentGroup() {
@@ -302,7 +303,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
async handleEditorNoteChange() { async handleEditorNoteChange() {
this.cancelPendingSetStatus(); this.cancelPendingSetStatus();
await this.setEditorState({ await this.setState({
showExtensions: false, showExtensions: false,
showOptionsMenu: false, showOptionsMenu: false,
showEditorMenu: false, showEditorMenu: false,
@@ -322,31 +323,32 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
} }
} }
async reloadComponentEditorState() { async reloadComponentEditorState(): Promise<{ editor?: SNComponent, changed: boolean }> {
const associatedEditor = this.application.componentManager!.editorForNote(this.note); const editor = this.application.componentManager!.editorForNote(this.note);
if (!associatedEditor) { if (!editor) {
/** No editor */ let changed: boolean;
let changed = false;
if (this.activeEditorComponent) { if (this.activeEditorComponent) {
await this.componentGroup.deactivateComponentForArea(ComponentArea.Editor); await this.componentGroup.deactivateComponentForArea(ComponentArea.Editor);
changed = true; changed = true;
} else {
changed = false;
} }
return { editor: undefined, changed }; return { editor, changed };
} }
if (associatedEditor.uuid === this.activeEditorComponent?.uuid) { if (editor.uuid === this.activeEditorComponent?.uuid) {
/** Same editor, no change */ /** Same editor, no change */
return { editor: associatedEditor, changed: false }; return { editor, changed: false };
} }
await this.componentGroup.activateComponent(associatedEditor); await this.componentGroup.activateComponent(editor);
return { editor: associatedEditor, changed: true }; return { editor, changed: true };
} }
/** /**
* Because note.locked accesses note.content.appData, * Because note.locked accesses note.content.appData,
* we do not want to expose the template to direct access to note.locked, * we do not want to expose the template to direct access to note.locked,
* otherwise an exception will occur when trying to access note.locked if the note * otherwise an exception will occur when trying to access note.locked if the note
* is deleted. There is potential for race conditions to occur with setState, where a * is deleted. There is potential for race conditions to occur with setState, where a
* previous setState call may have queued a digest cycle, and the digest cycle triggers * previous setState call may have queued a digest cycle, and the digest cycle triggers
* on a deleted note. * on a deleted note.
@@ -381,38 +383,39 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
this.removeComponentsObserver = this.application.streamItems( this.removeComponentsObserver = this.application.streamItems(
ContentType.Component, ContentType.Component,
async (items) => { async (items) => {
const components = items as SNComponent[]; if (!this.note) return;
if (!this.note) { this.setState({
return; allStackComponents: sortAlphabetically(
} this.application.componentManager!.componentsForArea(ComponentArea.EditorStack)
/** Reload componentStack in case new ones were added or removed */ .filter(component => component.active)
await this.reloadComponentStack(); )
});
this.reloadComponentContext();
this.reloadNoteTagsComponent(); this.reloadNoteTagsComponent();
/** Observe editor changes to see if the current note should update its editor */ /** Observe editor changes to see if the current note should update its editor */
const components = items as SNComponent[];
const editors = components.filter((component) => { const editors = components.filter((component) => {
return component.isEditor(); return component.isEditor();
}); });
if (editors.length === 0) { if (editors.length) {
return; /** Find the most recent editor for note */
} const { editor, changed } = await this.reloadComponentEditorState();
/** Find the most recent editor for note */
this.reloadComponentEditorState().then(({ editor, changed }) => {
if (!editor && changed) { if (!editor && changed) {
this.reloadFont(); this.reloadFont();
} }
}); }
} }
); );
} }
setMenuState(menu: string, state: boolean) { setMenuState(menu: string, state: boolean) {
this.setEditorState({ this.setState({
[menu]: state [menu]: state
}); });
this.closeAllMenus(menu); this.closeAllMenus(menu);
} }
toggleMenu(menu: string) { toggleMenu(menu: keyof EditorState) {
this.setMenuState(menu, !this.state[menu]); this.setMenuState(menu, !this.state[menu]);
} }
@@ -429,7 +432,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
menuState[candidate] = false; menuState[candidate] = false;
} }
} }
this.setEditorState(menuState); this.setState(menuState);
} }
async editorMenuOnSelect(component?: SNComponent) { async editorMenuOnSelect(component?: SNComponent) {
@@ -490,7 +493,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
* immediately. * immediately.
* @param isUserModified This field determines if the item will be saved as a user * @param isUserModified This field determines if the item will be saved as a user
* modification, thus updating the user modified date displayed in the UI * modification, thus updating the user modified date displayed in the UI
* @param dontUpdatePreviews Whether this change should update the note's plain and HTML * @param dontUpdatePreviews Whether this change should update the note's plain and HTML
* preview. * preview.
* @param customMutate A custom mutator function. * @param customMutate A custom mutator function.
* @param closeAfterSync Whether this editor should be closed after the sync starts. * @param closeAfterSync Whether this editor should be closed after the sync starts.
@@ -569,7 +572,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
} }
showAllChangesSavedStatus() { showAllChangesSavedStatus() {
this.setEditorState({ this.setState({
saveError: false, saveError: false,
syncTakingTooLong: false syncTakingTooLong: false
}); });
@@ -585,7 +588,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
desc: "Changes saved offline" desc: "Changes saved offline"
}; };
} }
this.setEditorState({ this.setState({
saveError: true, saveError: true,
syncTakingTooLong: false syncTakingTooLong: false
}); });
@@ -609,7 +612,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
} }
this.statusTimeout = this.$timeout(() => { this.statusTimeout = this.$timeout(() => {
status.date = new Date(); status.date = new Date();
this.setEditorState({ this.setState({
noteStatus: status noteStatus: status
}); });
}, waitForMs); }, waitForMs);
@@ -876,7 +879,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
public async saveTagsFromStrings(strings?: string[]) { public async saveTagsFromStrings(strings?: string[]) {
if ( if (
!strings !strings
&& this.editorValues.tagsInputValue === this.getState().tagsAsStrings && this.editorValues.tagsInputValue === this.state.tagsAsStrings
) { ) {
return; return;
} }
@@ -965,7 +968,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
WebPrefKey.EditorResizersEnabled, WebPrefKey.EditorResizersEnabled,
true true
); );
await this.setEditorState({ await this.setState({
monospaceFont, monospaceFont,
spellcheck, spellcheck,
marginResizersEnabled marginResizersEnabled
@@ -979,7 +982,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
this.reloadFont(); this.reloadFont();
if ( if (
this.getState().marginResizersEnabled && this.state.marginResizersEnabled &&
this.leftPanelPuppet!.ready && this.leftPanelPuppet!.ready &&
this.rightPanelPuppet!.ready this.rightPanelPuppet!.ready
) { ) {
@@ -1009,8 +1012,8 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
if (!editor) { if (!editor) {
return; return;
} }
if (this.getState().monospaceFont) { if (this.state.monospaceFont) {
if (this.getState().isDesktop) { if (this.state.isDesktop) {
editor.style.fontFamily = Fonts.DesktopMonospaceFamily; editor.style.fontFamily = Fonts.DesktopMonospaceFamily;
} else { } else {
editor.style.fontFamily = Fonts.WebMonospaceFamily; editor.style.fontFamily = Fonts.WebMonospaceFamily;
@@ -1021,21 +1024,21 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
} }
async toggleWebPrefKey(key: WebPrefKey) { async toggleWebPrefKey(key: WebPrefKey) {
const currentValue = this.state[key]; const currentValue = (this.state as any)[key];
await this.application.getPrefsService().setUserPrefValue( await this.application.getPrefsService().setUserPrefValue(
key, key,
!currentValue, !currentValue,
true true
); );
await this.setEditorState({ await this.setState({
[key]: !currentValue [key]: !currentValue
}) })
this.reloadFont(); this.reloadFont();
if (key === WebPrefKey.EditorSpellcheck) { if (key === WebPrefKey.EditorSpellcheck) {
/** Allows textarea to reload */ /** Allows textarea to reload */
await this.setEditorState({ textareaUnloading: true }); await this.setState({ textareaUnloading: true });
await this.setEditorState({ textareaUnloading: false }); await this.setState({ textareaUnloading: false });
this.reloadFont(); this.reloadFont();
} else if (key === WebPrefKey.EditorResizersEnabled && this.state[key] === true) { } else if (key === WebPrefKey.EditorResizersEnabled && this.state[key] === true) {
this.$timeout(() => { this.$timeout(() => {
@@ -1060,7 +1063,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
if ( if (
componentUuid === currentEditor?.uuid || componentUuid === currentEditor?.uuid ||
componentUuid === this.activeTagsComponent?.uuid || componentUuid === this.activeTagsComponent?.uuid ||
Uuids(this.getState().activeStackComponents).includes(componentUuid) Uuids(this.state.allStackComponents).includes(componentUuid)
) { ) {
return this.note; return this.note;
} }
@@ -1128,12 +1131,12 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
.sort((a, b) => { .sort((a, b) => {
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1; return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
}); });
await this.setEditorState({ await this.setState({
allStackComponents: components allStackComponents: components
}); });
this.reloadComponentContext(); this.reloadComponentContext();
/** component.active is a persisted state. So if we download a stack component /** component.active is a persisted state. So if we download a stack component
* whose .active is true, it doesn't mean it was explicitely activated by us. So * whose .active is true, it doesn't mean it was explicitely activated by us. So
* we need to do that here. */ * we need to do that here. */
for (const component of components) { for (const component of components) {
if (component.active) { if (component.active) {
@@ -1144,7 +1147,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
reloadComponentContext() { reloadComponentContext() {
if (this.note) { if (this.note) {
for (const component of this.getState().allStackComponents!) { for (const component of this.state.allStackComponents!) {
if (component.active) { if (component.active) {
this.application.componentManager!.setComponentHidden( this.application.componentManager!.setComponentHidden(
component, component,
@@ -1203,12 +1206,12 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
KeyboardModifier.Alt KeyboardModifier.Alt
], ],
onKeyDown: () => { onKeyDown: () => {
this.setEditorState({ this.setState({
altKeyDown: true altKeyDown: true
}); });
}, },
onKeyUp: () => { onKeyUp: () => {
this.setEditorState({ this.setState({
altKeyDown: false altKeyDown: false
}); });
} }