diff --git a/src/public/javascripts/services/app_context.js b/src/public/javascripts/services/app_context.js index ba4d6061a..e4e7779dc 100644 --- a/src/public/javascripts/services/app_context.js +++ b/src/public/javascripts/services/app_context.js @@ -29,6 +29,7 @@ import RunScriptButtonsWidget from "../widgets/run_script_buttons.js"; import ProtectedNoteSwitchWidget from "../widgets/protected_note_switch.js"; import NoteTypeWidget from "../widgets/note_type.js"; import NoteActionsWidget from "../widgets/note_actions.js"; +import protectedSessionHolder from "./protected_session_holder.js"; class AppContext { constructor() { @@ -116,23 +117,23 @@ class AppContext { ]; } - trigger(name, data) { + trigger(name, data, sync = false) { this.eventReceived(name, data); for (const tabContext of this.tabContexts) { - tabContext.eventReceived(name, data); + tabContext.eventReceived(name, data, sync); } for (const widget of this.widgets) { - widget.eventReceived(name, data); + widget.eventReceived(name, data, sync); } } - eventReceived(name, data) { + async eventReceived(name, data, sync) { const fun = this[name + 'Listener']; if (typeof fun === 'function') { - fun.call(this, data); + await fun.call(this, data, sync); } } @@ -347,6 +348,18 @@ class AppContext { tabReorderListener() { this.openTabsChanged(); } + + noteChangesSavedListener() { + const activeTabContext = this.getActiveTabContext(); + + if (!activeTabContext || !activeTabContext.note) { + return; + } + + if (activeTabContext.note.isProtected && protectedSessionHolder.isProtectedSessionAvailable()) { + protectedSessionHolder.touchProtectedSession(); + } + } } const appContext = new AppContext(); diff --git a/src/public/javascripts/services/tab_context.js b/src/public/javascripts/services/tab_context.js index 1c8080ca7..af7d50a01 100644 --- a/src/public/javascripts/services/tab_context.js +++ b/src/public/javascripts/services/tab_context.js @@ -36,6 +36,8 @@ class TabContext extends Component { } async setNote(notePath) { + await this.trigger('beforeNoteSwitch', {tabId: this.tabId}, true); + this.notePath = notePath; const noteId = treeUtils.getNoteIdFromNotePath(notePath); diff --git a/src/public/javascripts/widgets/component.js b/src/public/javascripts/widgets/component.js index 78fc28fb9..6b78547c1 100644 --- a/src/public/javascripts/widgets/component.js +++ b/src/public/javascripts/widgets/component.js @@ -8,7 +8,7 @@ export default class Component { this.initialized = Promise.resolve(); } - async eventReceived(name, data) { + async eventReceived(name, data, sync = false) { await this.initialized; // console.log(`Received ${name} to ${this.componentId}`); @@ -23,12 +23,16 @@ export default class Component { if (propagateToChildren) { for (const child of this.children) { - child.eventReceived(name, data); + let promise = child.eventReceived(name, data, sync); + + if (sync) { + await promise; + } } } } - trigger(name, data) { - this.appContext.trigger(name, data); + trigger(name, data, sync = false) { + this.appContext.trigger(name, data, sync); } } \ No newline at end of file diff --git a/src/public/javascripts/widgets/note_info.js b/src/public/javascripts/widgets/note_info.js index cc1f392ec..a8680de17 100644 --- a/src/public/javascripts/widgets/note_info.js +++ b/src/public/javascripts/widgets/note_info.js @@ -31,15 +31,13 @@ class NoteInfoWidget extends StandardWidget { this.$body.html(TPL); } - refreshWithNote() { + refreshWithNote(note) { const $noteId = this.$body.find(".note-info-note-id"); const $dateCreated = this.$body.find(".note-info-date-created"); const $dateModified = this.$body.find(".note-info-date-modified"); const $type = this.$body.find(".note-info-type"); const $mime = this.$body.find(".note-info-mime"); - const note = this.tabContext.note; - $noteId.text(note.noteId); $dateCreated .text(note.dateCreated) @@ -56,6 +54,15 @@ class NoteInfoWidget extends StandardWidget { .attr("title", note.mime); } + // this is interesting for this widget since dateModified had to change after update + noteChangesSavedListener({noteId}) { + const note = this.tabContext.note; + + if (note && note.noteId === noteId) { + this.refreshWithNote(note); + } + } + syncDataListener({data}) { if (data.find(sd => sd.entityName === 'notes' && sd.entityId === this.tabContext.note.noteId)) { this.refresh(); diff --git a/src/public/javascripts/widgets/note_title.js b/src/public/javascripts/widgets/note_title.js index 9e5946e95..dd1cbaf72 100644 --- a/src/public/javascripts/widgets/note_title.js +++ b/src/public/javascripts/widgets/note_title.js @@ -2,6 +2,7 @@ import TabAwareWidget from "./tab_aware_widget.js"; import utils from "../services/utils.js"; import protectedSessionHolder from "../services/protected_session_holder.js"; import treeCache from "../services/tree_cache.js"; +import server from "../services/server.js"; const TPL = `
@@ -23,7 +24,61 @@ const TPL = `
`; +class SpacedUpdate { + constructor(updater, updateInterval = 1000) { + this.updater = updater; + this.lastUpdated = Date.now(); + this.changed = false; + this.updateInterval = updateInterval; + } + + scheduleUpdate() { + this.changed = true; + setTimeout(() => this.triggerUpdate()) + } + + async updateNowIfNecessary() { + if (this.changed) { + this.changed = false; + await this.updater(); + } + } + + triggerUpdate() { + if (!this.changed) { + return; + } + + if (Date.now() - this.lastUpdated > this.updateInterval) { + this.updater(); + this.lastUpdated = Date.now(); + this.changed = false; + } + else { + // update not triggered but changes are still pending so we need to schedule another check + this.scheduleUpdate(); + } + } +} + export default class NoteTitleWidget extends TabAwareWidget { + constructor(appContext) { + super(appContext); + + this.spacedUpdate = new SpacedUpdate(async () => { + const noteId = this.tabContext.note.noteId; + const title = this.$noteTitle.val(); + + const resp = await server.put(`notes/${noteId}/change-title`, {title}); + + // FIXME: minor - does not propagate to other tab contexts with this note though + this.tabContext.note.dateModified = resp.dateModified; + this.tabContext.note.utcDateModified = resp.utcDateModified; + + this.trigger('noteChangesSaved', {noteId}) + }); + } + doRender() { this.$widget = $(TPL); this.$noteTitle = this.$widget.find(".note-title"); @@ -46,6 +101,8 @@ export default class NoteTitleWidget extends TabAwareWidget { note.title = this.$noteTitle.val(); + this.spacedUpdate.scheduleUpdate(); + const noteFromCache = await treeCache.getNote(note.noteId); noteFromCache.title = note.title; @@ -74,4 +131,10 @@ export default class NoteTitleWidget extends TabAwareWidget { this.$noteTitle.prop("readonly", true); } } + + async beforeNoteSwitch({tabId}) { + if (this.isTab(tabId)) { + await this.spacedUpdate.updateNowIfNecessary(); + } + } } \ No newline at end of file diff --git a/src/public/javascripts/widgets/note_tree.js b/src/public/javascripts/widgets/note_tree.js index 470219b7e..4b1e2f2e3 100644 --- a/src/public/javascripts/widgets/note_tree.js +++ b/src/public/javascripts/widgets/note_tree.js @@ -450,8 +450,6 @@ export default class NoteTreeWidget extends TabAwareWidget { noteTitleChangedListener({noteId}) { for (const node of this.getNodesByNoteId(noteId)) { - console.log("Setting to", node); - treeService.setNodeTitleWithPrefix(node); } } diff --git a/src/public/javascripts/widgets/tab_aware_widget.js b/src/public/javascripts/widgets/tab_aware_widget.js index c32e26b2a..d621eccb6 100644 --- a/src/public/javascripts/widgets/tab_aware_widget.js +++ b/src/public/javascripts/widgets/tab_aware_widget.js @@ -8,8 +8,16 @@ export default class TabAwareWidget extends BasicWidget { this.noteSwitched(); } + isTab(tabId) { + return this.tabContext && this.tabContext.tabId === tabId; + } + + isNote(noteId) { + return this.tabContext && this.tabContext.note && this.tabContext.note.noteId === noteId; + } + tabNoteSwitchedListener({tabId}) { - if (this.tabContext && tabId === this.tabContext.tabId) { + if (this.isTab(tabId)) { this.noteSwitched(); } } diff --git a/src/routes/api/notes.js b/src/routes/api/notes.js index 8ef3559db..a57ca8a81 100644 --- a/src/routes/api/notes.js +++ b/src/routes/api/notes.js @@ -170,6 +170,8 @@ async function changeTitle(req) { note.title = title; await note.save(); + + return note; } async function duplicateNote(req) {