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(`