From 4bd86a6dfa94b6aef9d3e6f045cddba4aabf4722 Mon Sep 17 00:00:00 2001 From: zadam Date: Sun, 22 Nov 2020 23:05:02 +0100 Subject: [PATCH] WIP per-tab hoisting --- src/public/app/services/entrypoints.js | 11 ++--- src/public/app/services/hoisted_note.js | 4 -- src/public/app/services/tab_context.js | 17 ++++++- src/public/app/services/tab_manager.js | 20 ++------ src/public/app/services/tree_cache.js | 3 +- src/public/app/services/tree_context_menu.js | 7 ++- src/public/app/widgets/note_detail.js | 6 ++- src/public/app/widgets/note_tree.js | 48 ++++++++------------ src/public/app/widgets/type_widgets/book.js | 2 +- src/public/stylesheets/style.css | 10 ++++ src/routes/api/tree.js | 2 +- 11 files changed, 64 insertions(+), 66 deletions(-) diff --git a/src/public/app/services/entrypoints.js b/src/public/app/services/entrypoints.js index e62559f3f..c89f3efc3 100644 --- a/src/public/app/services/entrypoints.js +++ b/src/public/app/services/entrypoints.js @@ -80,14 +80,13 @@ export default class Entrypoints extends Component { } async toggleNoteHoistingCommand() { - const note = appContext.tabManager.getActiveTabNote(); + const tabContext = appContext.tabManager.getActiveTabContext(); - const hoistedNoteId = hoistedNoteService.getHoistedNoteId(); - if (note.noteId === hoistedNoteId) { - hoistedNoteService.unhoist(); + if (tabContext.note.noteId === tabContext.hoistedNoteId) { + await tabContext.unhoist(); } - else if (note.type !== 'search') { - hoistedNoteService.setHoistedNoteId(note.noteId); + else if (tabContext.note.type !== 'search') { + await tabContext.setHoistedNoteId(tabContext.note.noteId); } } diff --git a/src/public/app/services/hoisted_note.js b/src/public/app/services/hoisted_note.js index c6ad0d626..089a6868c 100644 --- a/src/public/app/services/hoisted_note.js +++ b/src/public/app/services/hoisted_note.js @@ -1,7 +1,6 @@ import options from './options.js'; import appContext from "./app_context.js"; import treeService from "./tree.js"; -import treeCache from "./tree_cache.js"; function getHoistedNoteId() { return options.get('hoistedNoteId'); @@ -14,9 +13,6 @@ async function setHoistedNoteId(noteId) { await options.save('hoistedNoteId', noteId); - await treeCache.loadInitialTree(); - - // FIXME - just use option load event appContext.triggerEvent('hoistedNoteChanged', {noteId}); } diff --git a/src/public/app/services/tab_context.js b/src/public/app/services/tab_context.js index 43e45ef23..bfc280d01 100644 --- a/src/public/app/services/tab_context.js +++ b/src/public/app/services/tab_context.js @@ -11,10 +11,11 @@ class TabContext extends Component { /** * @param {string|null} tabId */ - constructor(tabId = null) { + constructor(tabId = null, hoistedNoteId = 'root') { super(); this.tabId = tabId || utils.randomString(4); + this.hoistedNoteId = hoistedNoteId; } setEmpty() { @@ -123,10 +124,24 @@ class TabContext extends Component { return { tabId: this.tabId, notePath: this.notePath, + hoistedNoteId: this.hoistedNoteId, active: this.isActive() } } + async unhoist() { + await this.setHoistedNoteId('root'); + } + + async setHoistedNoteId(noteIdToHoist) { + this.hoistedNoteId = noteIdToHoist; + + await this.triggerEvent('hoistedNoteChanged', { + noteId: noteIdToHoist, + tabId: this.tabId + }); + } + async entitiesReloadedEvent({loadResults}) { if (loadResults.isNoteReloaded(this.noteId)) { const note = await treeCache.getNote(this.noteId); diff --git a/src/public/app/services/tab_manager.js b/src/public/app/services/tab_manager.js index b6fd5475d..b874de29e 100644 --- a/src/public/app/services/tab_manager.js +++ b/src/public/app/services/tab_manager.js @@ -93,7 +93,7 @@ export default class TabManager extends Component { await this.tabsUpdate.allowUpdateWithoutChange(async () => { for (const tab of filteredTabs) { - await this.openTabWithNote(tab.notePath, tab.active, tab.tabId); + await this.openTabWithNote(tab.notePath, tab.active, tab.tabId, tab.hoistedNoteId); } }); } @@ -184,8 +184,8 @@ export default class TabManager extends Component { await tabContext.setEmpty(); } - async openEmptyTab(tabId) { - const tabContext = new TabContext(tabId); + async openEmptyTab(tabId, hoistedNoteId) { + const tabContext = new TabContext(tabId, hoistedNoteId); this.child(tabContext); await this.triggerEvent('newTabOpened', {tabContext}); @@ -193,7 +193,7 @@ export default class TabManager extends Component { return tabContext; } - async openTabWithNote(notePath, activate, tabId = null) { + async openTabWithNote(notePath, activate, tabId = null, hoistedNoteId = 'root') { const tabContext = await this.openEmptyTab(tabId); if (notePath) { @@ -336,16 +336,4 @@ export default class TabManager extends Component { this.triggerCommand('openInWindow', {notePath}); } - - async hoistedNoteChangedEvent({hoistedNoteId}) { - if (hoistedNoteId === 'root') { - return; - } - - for (const tc of this.tabContexts.splice()) { - if (tc.notePath && !tc.notePath.split("/").includes(hoistedNoteId)) { - await this.removeTab(tc.tabId); - } - } - } } diff --git a/src/public/app/services/tree_cache.js b/src/public/app/services/tree_cache.js index e3649d18f..81b4fae3d 100644 --- a/src/public/app/services/tree_cache.js +++ b/src/public/app/services/tree_cache.js @@ -254,8 +254,7 @@ class TreeCache { console.trace(`Can't find note "${noteId}"`); return null; - } - else { + } else { return this.notes[noteId]; } }).filter(note => !!note); diff --git a/src/public/app/services/tree_context_menu.js b/src/public/app/services/tree_context_menu.js index 058dac8e6..95cbb9c1e 100644 --- a/src/public/app/services/tree_context_menu.js +++ b/src/public/app/services/tree_context_menu.js @@ -1,10 +1,9 @@ import treeService from './tree.js'; import treeCache from "./tree_cache.js"; -import hoistedNoteService from './hoisted_note.js'; import clipboard from './clipboard.js'; -import protectedSessionHolder from "./protected_session_holder.js"; import noteCreateService from "./note_create.js"; import contextMenu from "./context_menu.js"; +import appContext from "./app_context.js"; class TreeContextMenu { /** @@ -40,7 +39,7 @@ class TreeContextMenu { const note = await treeCache.getNote(this.node.data.noteId); const branch = treeCache.getBranch(this.node.data.branchId); const isNotRoot = note.noteId !== 'root'; - const isHoisted = note.noteId === hoistedNoteService.getHoistedNoteId(); + const isHoisted = note.noteId === appContext.tabManager.getActiveTabContext().hoistedNoteId; const parentNote = isNotRoot ? await treeCache.getNote(branch.parentNoteId) : null; // some actions don't support multi-note so they are disabled when notes are selected @@ -69,7 +68,7 @@ class TreeContextMenu { { title: 'Search in subtree ', command: "searchInSubtree", uiIcon: "search", enabled: notSearch && noSelectedNotes }, isHoisted ? null : { title: 'Hoist note ', command: "toggleNoteHoisting", uiIcon: "empty", enabled: noSelectedNotes && notSearch }, - !isHoisted || !isNotRoot ? null : { title: 'Unhoist note ', command: "toggleNoteHoisting", uiIcon: "arrow-from-bottom" }, + !isHoisted || !isNotRoot ? null : { title: 'Unhoist note ', command: "toggleNoteHoisting", uiIcon: "arrow-from-bottom" }, { title: 'Edit branch prefix ', command: "editBranchPrefix", uiIcon: "empty", enabled: isNotRoot && parentNotSearch && noSelectedNotes}, { title: "Advanced", uiIcon: "empty", enabled: true, items: [ diff --git a/src/public/app/widgets/note_detail.js b/src/public/app/widgets/note_detail.js index a6cada6c2..98a818cd4 100644 --- a/src/public/app/widgets/note_detail.js +++ b/src/public/app/widgets/note_detail.js @@ -263,8 +263,10 @@ export default class NoteDetailWidget extends TabAwareWidget { }); } - hoistedNoteChangedEvent() { - this.refresh(); + hoistedNoteChangedEvent({tabId}) { + if (this.isTab(tabId)) { + this.refresh(); + } } async entitiesReloadedEvent({loadResults}) { diff --git a/src/public/app/widgets/note_tree.js b/src/public/app/widgets/note_tree.js index e6a1bcc5f..7e3952c4c 100644 --- a/src/public/app/widgets/note_tree.js +++ b/src/public/app/widgets/note_tree.js @@ -290,7 +290,7 @@ export default class NoteTreeWidget extends TabAwareWidget { this.$tree.fancytree({ titlesTabbable: true, keyboard: true, - extensions: ["dnd5", "clones"], + extensions: ["dnd5", "clones", "filter"], source: treeData, scrollOfs: { top: 100, @@ -341,6 +341,11 @@ export default class NoteTreeWidget extends TabAwareWidget { }, expand: (event, data) => this.setExpanded(data.node.data.branchId, true), collapse: (event, data) => this.setExpanded(data.node.data.branchId, false), + filter: { + counter: false, + mode: "hide", + autoExpand: true + }, dnd5: { autoExpandMS: 600, dragStart: (node, data) => { @@ -446,21 +451,6 @@ export default class NoteTreeWidget extends TabAwareWidget { const node = data.node; const $span = $(node.span); - if (node.data.noteId !== 'root' - && node.data.noteId === hoistedNoteService.getHoistedNoteId() - && $span.find('.unhoist-button').length === 0) { - - const action = await keyboardActionsService.getAction('unhoist'); - let shortcuts = action.effectiveShortcuts.join(','); - shortcuts = shortcuts ? `(${shortcuts})` : ''; - - const unhoistButton = $(`[unhoist]`); - - // prepending since appending could push out (when note title is too long) - // the button too much to the right so that it's not visible - $span.prepend(unhoistButton); - } - const note = await treeCache.getNote(node.data.noteId); if (note.type === 'search' && $span.find('.refresh-search-button').length === 0) { @@ -512,17 +502,7 @@ export default class NoteTreeWidget extends TabAwareWidget { prepareRootNode() { const hoistedNoteId = hoistedNoteService.getHoistedNoteId(); - let hoistedBranch; - - if (hoistedNoteId === 'root') { - hoistedBranch = treeCache.getBranch('root'); - } - else { - const hoistedNote = treeCache.getNoteFromCache(hoistedNoteId); - hoistedBranch = hoistedNote.getBranches()[0]; - } - - return this.prepareNode(hoistedBranch); + return this.prepareNode(treeCache.getBranch('root')); } /** @@ -897,6 +877,8 @@ export default class NoteTreeWidget extends TabAwareWidget { newActiveNode.makeVisible({scrollIntoView: true}); } } + + this.filterHoistedBranch(); } async refreshSearch() { @@ -1156,8 +1138,16 @@ export default class NoteTreeWidget extends TabAwareWidget { } } - hoistedNoteChangedEvent() { - this.reloadTreeFromCache(); + async hoistedNoteChangedEvent({tabId}) { + if (this.isTab(tabId)) { + this.filterHoistedBranch(); + } + } + + filterHoistedBranch() { + if (this.tabContext) { + this.tree.filterBranches(node => node.data.noteId === this.tabContext.hoistedNoteId); + } } treeCacheReloadedEvent() { diff --git a/src/public/app/widgets/type_widgets/book.js b/src/public/app/widgets/type_widgets/book.js index 184af7657..6fda5cb60 100644 --- a/src/public/app/widgets/type_widgets/book.js +++ b/src/public/app/widgets/type_widgets/book.js @@ -60,7 +60,7 @@ export default class BookTypeWidget extends TypeWidget { .append(' if you want to add some text.')); } - const noteListRenderer = new NoteListRenderer(note, await note.getChildNotes()); + const noteListRenderer = new NoteListRenderer(note, note.getChildNoteIds()); this.$content.append(await noteListRenderer.renderList()); } diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index d62d12805..53fd80990 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -81,6 +81,16 @@ span.fancytree-node.dotted .fancytree-title { text-decoration: dotted; } span.fancytree-node.bold .fancytree-title { font-weight: bold; } span.fancytree-node.muted { opacity: 0.6; } +/** following will hide ancestors of hoisted (filtered) note */ +.fancytree-submatch:not(.fancytree-match) { + display: none !important; +} + +/** resets indent of hoisted note */ +.fancytree-submatch:not(.fancytree-match) + ul { + padding: 0 !important; +} + .note-title[readonly] { background: inherit; } diff --git a/src/routes/api/tree.js b/src/routes/api/tree.js index eca5313e0..f9f68b381 100644 --- a/src/routes/api/tree.js +++ b/src/routes/api/tree.js @@ -52,7 +52,7 @@ function getNotesAndBranchesAndAttributes(noteIds) { } function getTree(req) { - const subTreeNoteId = req.query.subTreeNoteId || optionService.getOption('hoistedNoteId'); + const subTreeNoteId = req.query.subTreeNoteId || 'root'; // FIXME: this query does not return ascendants of template notes const noteIds = sql.getColumn(`