diff --git a/src/public/javascripts/services/app_context.js b/src/public/javascripts/services/app_context.js index e4e7779dc..27f0fb25b 100644 --- a/src/public/javascripts/services/app_context.js +++ b/src/public/javascripts/services/app_context.js @@ -30,6 +30,7 @@ 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"; +import bundleService from "./bundle.js"; class AppContext { constructor() { @@ -39,6 +40,7 @@ class AppContext { this.tabsChangedTaskId = null; /** @type {TabRowWidget} */ this.tabRow = null; + this.activeTabId = null; } showWidgets() { @@ -180,9 +182,7 @@ class AppContext { /** @returns {TabContext} */ getActiveTabContext() { - const tabId = this.tabRow.activeTabId; - - return this.tabContexts.find(tc => tc.tabId === tabId); + return this.tabContexts.find(tc => tc.tabId === this.activeTabId); } /** @returns {string|null} */ @@ -268,10 +268,10 @@ class AppContext { } async openEmptyTab() { - const ctx = new TabContext(this, this.tabRow); - this.tabContexts.push(ctx); + const tabContext = new TabContext(this, this.tabRow); + this.tabContexts.push(tabContext); - await this.tabRow.activateTab(ctx.$tab[0]); + await this.activateTab(tabContext.tabId); } async filterTabs(noteId) { @@ -291,8 +291,7 @@ class AppContext { async saveOpenTabs() { const openTabs = []; - for (const tabEl of this.tabRow.tabEls) { - const tabId = tabEl.getAttribute('data-tab-id'); + for (const tabId of this.tabRow.getTabIdsInOrder()) { const tabContext = appContext.getTabContexts().find(tc => tc.tabId === tabId); if (tabContext) { @@ -324,23 +323,42 @@ class AppContext { this.tabsChangedTaskId = setTimeout(() => this.saveOpenTabs(), 1000); } - async activateTab(tabContext) { - return this.tabRow.activateTab(tabContext.$tab[0]); + async activateTab(tabId) { + this.activeTabId = tabId; + + this.trigger('activeTabChanged', { tabId: this.activeTabId }); } newTabListener() { this.openEmptyTab(); } - async tabRemoveListener({tabId}) { - this.tabContexts.filter(nc => nc.tabId === tabId) - .forEach(tc => tc.remove()); + async removeTab(tabId) { + const tabContextToRemove = this.tabContexts.find(tc => tc.tabId === tabId); + const tabIdsInOrder = this.tabRow.getTabIdsInOrder(); - this.tabContexts = this.tabContexts.filter(nc => nc.tabId !== tabId); + if (!tabContextToRemove) { + return; + } if (this.tabContexts.length === 0) { this.openEmptyTab(); } + else { + const oldIdx = tabIdsInOrder.findIndex(tid => tid === tabId); + const newActiveTabId = tabIdsInOrder[oldIdx === tabIdsInOrder.length ? oldIdx - 1 : oldIdx + 1]; + + if (newActiveTabId) { + this.activateTab(newActiveTabId); + } + else { + console.log("Failed to find next tabcontext to activate"); + } + } + + await tabContextToRemove.remove(); + + this.tabContexts = this.tabContexts.filter(tc => tc.tabId === tabId); this.openTabsChanged(); } @@ -359,6 +377,9 @@ class AppContext { if (activeTabContext.note.isProtected && protectedSessionHolder.isProtectedSessionAvailable()) { protectedSessionHolder.touchProtectedSession(); } + + // run async + bundleService.executeRelationBundles(activeTabContext.note, 'runOnNoteChange', activeTabContext); } } @@ -378,6 +399,7 @@ keyboardActionService.setGlobalActionHandler('ActivateNextTab', () => { const nextTab = this.tabRow.nextTabEl; if (nextTab) { + // FIXME this.tabRow.activateTab(nextTab); } }); @@ -386,6 +408,7 @@ keyboardActionService.setGlobalActionHandler('ActivatePreviousTab', () => { const prevTab = this.tabRow.previousTabEl; if (prevTab) { + // FIXME this.tabRow.activateTab(prevTab); } }); diff --git a/src/public/javascripts/services/note_detail.js b/src/public/javascripts/services/note_detail.js index 9f4c00b42..7145ab674 100644 --- a/src/public/javascripts/services/note_detail.js +++ b/src/public/javascripts/services/note_detail.js @@ -113,7 +113,7 @@ async function loadNoteDetail(origNotePath, options = {}) { const loadPromise = loadNoteDetailToContext(ctx, notePath).then(() => { if (activate) { - return appContext.activateTab(ctx); + return appContext.activateTab(ctx.tabId); } else { return Promise.resolve(); diff --git a/src/public/javascripts/services/tab_context.js b/src/public/javascripts/services/tab_context.js index af7d50a01..7bc50ce28 100644 --- a/src/public/javascripts/services/tab_context.js +++ b/src/public/javascripts/services/tab_context.js @@ -81,6 +81,12 @@ class TabContext extends Component { return this.tabId === this.tabRow.activeTabId; } + async remove() { + await this.trigger('beforeTabRemove', {tabId: this.tabId}, true); + + this.trigger('tabRemoved', {tabId: this.tabId}); + } + setupClasses() { for (const clazz of Array.from(this.$tab[0].classList)) { // create copy to safely iterate over while removing classes if (clazz !== 'note-tab') { @@ -123,8 +129,7 @@ class TabContext extends Component { // FIXME trigger "noteSaved" event so that title indicator is triggered this.eventReceived('noteSaved'); - // run async - bundleService.executeRelationBundles(this.note, 'runOnNoteChange', this); + } async saveNoteIfChanged() { diff --git a/src/public/javascripts/widgets/basic_widget.js b/src/public/javascripts/widgets/basic_widget.js index c553c4599..ce55fe607 100644 --- a/src/public/javascripts/widgets/basic_widget.js +++ b/src/public/javascripts/widgets/basic_widget.js @@ -18,6 +18,12 @@ class BasicWidget extends Component { this.$widget.toggle(show); } + remove() { + if (this.$widget) { + this.$widget.remove(); + } + } + cleanup() {} } diff --git a/src/public/javascripts/widgets/note_title.js b/src/public/javascripts/widgets/note_title.js index dd1cbaf72..48309efbc 100644 --- a/src/public/javascripts/widgets/note_title.js +++ b/src/public/javascripts/widgets/note_title.js @@ -132,7 +132,13 @@ export default class NoteTitleWidget extends TabAwareWidget { } } - async beforeNoteSwitch({tabId}) { + async beforeNoteSwitchListener({tabId}) { + if (this.isTab(tabId)) { + await this.spacedUpdate.updateNowIfNecessary(); + } + } + + async beforeTabRemoveListener({tabId}) { if (this.isTab(tabId)) { await this.spacedUpdate.updateNowIfNecessary(); } diff --git a/src/public/javascripts/widgets/note_tree.js b/src/public/javascripts/widgets/note_tree.js index 4b1e2f2e3..5740cd804 100644 --- a/src/public/javascripts/widgets/note_tree.js +++ b/src/public/javascripts/widgets/note_tree.js @@ -408,6 +408,7 @@ export default class NoteTreeWidget extends TabAwareWidget { if (oldActiveNode) { oldActiveNode.setActive(false); + oldActiveNode.setFocus(false); } if (this.tabContext && this.tabContext.notePath) { diff --git a/src/public/javascripts/widgets/tab_caching_widget.js b/src/public/javascripts/widgets/tab_caching_widget.js index eadb69ccb..6b89abeec 100644 --- a/src/public/javascripts/widgets/tab_caching_widget.js +++ b/src/public/javascripts/widgets/tab_caching_widget.js @@ -35,6 +35,14 @@ export default class TabCachingWidget extends TabAwareWidget { return false; // stop propagation to children } + tabRemovedListener({tabId}) { + const widget = this.widgets[tabId]; + + if (widget) { + widget.remove(); + } + } + toggle(show) { for (const tabId in this.widgets) { this.widgets[tabId].toggle(show && this.tabContext && tabId === this.tabContext.tabId); diff --git a/src/public/javascripts/widgets/tab_row.js b/src/public/javascripts/widgets/tab_row.js index f1fd5d703..3233e03a9 100644 --- a/src/public/javascripts/widgets/tab_row.js +++ b/src/public/javascripts/widgets/tab_row.js @@ -399,11 +399,11 @@ export default class TabRowWidget extends BasicWidget { setTabCloseEventListener(tabEl) { tabEl.querySelector('.note-tab-close') - .addEventListener('click', _ => this.removeTab(tabEl.getAttribute('data-tab-id'))); + .addEventListener('click', _ => this.appContext.removeTab(tabEl.getAttribute('data-tab-id'))); tabEl.addEventListener('mousedown', e => { if (e.which === 2) { - this.removeTab(tabEl.getAttribute('data-tab-id')); + this.appContext.removeTab(tabEl.getAttribute('data-tab-id')); return true; // event has been handled } @@ -464,44 +464,26 @@ export default class TabRowWidget extends BasicWidget { return !!this.activeTabEl; } - activateTab(tabEl) { + activeTabChangedListener({tabId}) { + const tabEl = this.getTabById(tabId)[0]; const activeTabEl = this.activeTabEl; if (activeTabEl === tabEl) return; if (activeTabEl) activeTabEl.removeAttribute('active'); tabEl.setAttribute('active', ''); - this.trigger('activeTabChanged', { tabId: tabEl.getAttribute('data-tab-id') }); } removeTab(tabId) { - const tabEl = this.$widget.find(`[data-tab-id='${tabId}']`)[0]; + const tabEl = this.getTabById(tabId)[0]; - if (tabEl === this.activeTabEl) { - if (tabEl.nextElementSibling && tabEl.nextElementSibling.classList.contains("note-tab")) { - this.activateTab(tabEl.nextElementSibling) - } else if (tabEl.previousElementSibling && tabEl.previousElementSibling.classList.contains("note-tab")) { - this.activateTab(tabEl.previousElementSibling) - } - } tabEl.parentNode.removeChild(tabEl); - this.trigger('tabRemove', { tabId: tabEl.getAttribute('data-tab-id') }); this.cleanUpPreviouslyDraggedTabs(); this.layoutTabs(); this.setupDraggabilly(); this.setVisibility(); } - removeAllTabs() { - for (const tabEl of this.tabEls) { - this.removeTab(tabEl.getAttribute('data-tab-id')); - } - } - - removeAllTabsExceptForThis(remainingTabEl) { - for (const tabEl of this.tabEls) { - if (remainingTabEl !== tabEl) { - this.removeTab(tabEl.getAttribute('data-tab-id')); - } - } + getTabIdsInOrder() { + return this.tabEls.map(el => el.getAttribute('data-tab-id')); } updateTab(tabEl, tabProperties) { @@ -519,6 +501,10 @@ export default class TabRowWidget extends BasicWidget { .forEach($el => $el.find('.note-tab-title').text(title)); } + tabRemovedListener({tabId}) { + this.removeTab(tabId); + } + cleanUpPreviouslyDraggedTabs() { this.tabEls.forEach((tabEl) => tabEl.classList.remove('note-tab-was-just-dragged')); } @@ -552,7 +538,7 @@ export default class TabRowWidget extends BasicWidget { this.draggabillies.push(draggabilly); draggabilly.on('pointerDown', _ => { - this.activateTab(tabEl) + this.appContext.activateTab(tabEl.getAttribute('data-tab-id')); }); draggabilly.on('dragStart', _ => {