diff --git a/src/public/app/entities/branch.js b/src/public/app/entities/branch.js index bbb6d312e..0cd72a03c 100644 --- a/src/public/app/entities/branch.js +++ b/src/public/app/entities/branch.js @@ -28,6 +28,11 @@ class Branch { return this.treeCache.getNote(this.noteId); } + /** @returns {NoteShort} */ + getNoteFromCache() { + return this.treeCache.getNoteFromCache(this.noteId); + } + /** @returns {NoteShort} */ async getParentNote() { return this.treeCache.getNote(this.parentNoteId); diff --git a/src/public/app/services/tree.js b/src/public/app/services/tree.js index 4c91b2c8d..9f8237542 100644 --- a/src/public/app/services/tree.js +++ b/src/public/app/services/tree.js @@ -70,7 +70,7 @@ async function resolveNotePathToSegments(notePath, logErrors = true) { if (!parents.some(p => p.noteId === parentNoteId)) { if (logErrors) { - console.log(utils.now(), `Did not find parent ${parentNoteId} for child ${childNoteId}, available parents: ${parents}`); + console.log(utils.now(), `Did not find parent ${parentNoteId} for child ${childNoteId}, available parents: ${parents.map(p => p.noteId)}`); } const someNotePath = getSomeNotePath(parents[0]); diff --git a/src/public/app/services/tree_cache.js b/src/public/app/services/tree_cache.js index ad5e76161..c64d631a6 100644 --- a/src/public/app/services/tree_cache.js +++ b/src/public/app/services/tree_cache.js @@ -20,8 +20,6 @@ class TreeCache { async loadInitialTree() { const resp = await server.get('tree'); - await this.loadParents(resp, false); - // clear the cache only directly before adding new content which is important for e.g. switching to protected session /** @type {Object.} */ @@ -39,6 +37,14 @@ class TreeCache { this.addResp(resp); } + async loadSubTree(subTreeNoteId) { + const resp = await server.get('tree?subTreeNoteId=' + subTreeNoteId); + + this.addResp(resp); + + return this.notes[subTreeNoteId]; + } + async loadParents(resp, additiveLoad) { const noteIds = new Set(resp.notes.map(note => note.noteId)); const missingNoteIds = []; diff --git a/src/public/app/widgets/note_paths.js b/src/public/app/widgets/note_paths.js index a3ef35d80..f74679007 100644 --- a/src/public/app/widgets/note_paths.js +++ b/src/public/app/widgets/note_paths.js @@ -50,7 +50,7 @@ const TPL = ` export default class NotePathsWidget extends TabAwareWidget { doRender() { this.$widget = $(TPL); - this.contentSized(); + this.overflowing(); this.$currentPath = this.$widget.find('.current-path'); this.$dropdown = this.$widget.find(".dropdown"); diff --git a/src/public/app/widgets/note_tree.js b/src/public/app/widgets/note_tree.js index 30af73106..3626bc032 100644 --- a/src/public/app/widgets/note_tree.js +++ b/src/public/app/widgets/note_tree.js @@ -297,8 +297,8 @@ export default class NoteTreeWidget extends TabAwareWidget { await options.save("hideIncludedImages_" + this.treeName, val.toString()); } - async initFancyTree() { - const treeData = [await this.prepareRootNode()]; + initFancyTree() { + const treeData = [this.prepareRootNode()]; this.$tree.fancytree({ titlesTabbable: true, @@ -354,7 +354,7 @@ 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), - hotkeys: utils.isMobile() ? undefined : { keydown: await this.getHotKeys() }, + //hotkeys: utils.isMobile() ? undefined : { keydown: await this.getHotKeys() }, dnd5: { autoExpandMS: 600, dragStart: (node, data) => { @@ -443,34 +443,41 @@ export default class NoteTreeWidget extends TabAwareWidget { data.result = []; return; } - } - data.result = treeCache.getNote(noteId).then(note => this.prepareChildren(note)); + data.result = treeCache.reloadNotes([noteId]).then(() => { + const note = treeCache.getNoteFromCache(noteId); + + return this.prepareChildren(note); + }); + } + else { + data.result = treeCache.loadSubTree(noteId).then(note => this.prepareChildren(note)); + } }, clones: { highlightActiveClones: true }, - enhanceTitle: async function (event, data) { - 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 unhoistButton = $('  (unhoist)'); - - $span.append(unhoistButton); - } - - const note = await treeCache.getNote(node.data.noteId); - - if (note.type === 'search' && $span.find('.refresh-search-button').length === 0) { - const refreshSearchButton = $('  '); - - $span.append(refreshSearchButton); - } - }, + // enhanceTitle: async function (event, data) { + // 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 unhoistButton = $('  (unhoist)'); + // + // $span.append(unhoistButton); + // } + // + // const note = await treeCache.getNote(node.data.noteId); + // + // if (note.type === 'search' && $span.find('.refresh-search-button').length === 0) { + // const refreshSearchButton = $('  '); + // + // $span.append(refreshSearchButton); + // } + // }, // this is done to automatically lazy load all expanded notes after tree load loadChildren: (event, data) => { data.node.visit((subNode) => { @@ -497,9 +504,7 @@ export default class NoteTreeWidget extends TabAwareWidget { this.tree = $.ui.fancytree.getTree(this.$tree); } - async prepareRootNode() { - await treeCache.initializedPromise; - + prepareRootNode() { const hoistedNoteId = hoistedNoteService.getHoistedNoteId(); let hoistedBranch; @@ -508,31 +513,17 @@ export default class NoteTreeWidget extends TabAwareWidget { hoistedBranch = treeCache.getBranch('root'); } else { - const hoistedNote = await treeCache.getNote(hoistedNoteId); - hoistedBranch = (await hoistedNote.getBranches())[0]; + const hoistedNote = treeCache.getNoteFromCache(hoistedNoteId); + hoistedBranch = hoistedNote.getBranches()[0]; } - return await this.prepareNode(hoistedBranch); + return this.prepareNode(hoistedBranch); } - async prepareChildren(note) { - if (note.type === 'search') { - return await this.prepareSearchNoteChildren(note); - } - else { - return await this.prepareNormalNoteChildren(note); - } - } - - async prepareSearchNoteChildren(note) { - await treeCache.reloadNotes([note.noteId]); - - const newNote = await treeCache.getNote(note.noteId); - - return await this.prepareNormalNoteChildren(newNote); - } - - async prepareNormalNoteChildren(parentNote) { + /** + * @param {NoteShort} parentNote + */ + prepareChildren(parentNote) { utils.assertArguments(parentNote); const noteList = []; @@ -541,14 +532,14 @@ export default class NoteTreeWidget extends TabAwareWidget { for (const branch of this.getChildBranches(parentNote)) { if (hideArchivedNotes) { - const note = await branch.getNote(); + const note = branch.getNoteFromCache(); if (note.hasLabel('archived')) { continue; } } - const node = await this.prepareNode(branch); + const node = this.prepareNode(branch); noteList.push(node); } @@ -613,10 +604,15 @@ export default class NoteTreeWidget extends TabAwareWidget { node.renderTitle(); } - async prepareNode(branch) { - const note = await branch.getNote(); + /** + * @param {Branch} branch + */ + prepareNode(branch) { + const note = branch.getNoteFromCache(); if (!note) { + console.log("branch", branch); + throw new Error(`Branch has no note "${branch.noteId}": ${JSON.stringify(note)}`); } @@ -642,7 +638,7 @@ export default class NoteTreeWidget extends TabAwareWidget { }; if (node.folder && node.expanded) { - node.children = await this.prepareChildren(note); + node.children = this.prepareChildren(note); } return node; @@ -1141,9 +1137,6 @@ export default class NoteTreeWidget extends TabAwareWidget { } async setExpanded(branchId, isExpanded) { - console.log("expand", isExpanded); - - utils.assertArguments(branchId); const branch = treeCache.getBranch(branchId); @@ -1157,7 +1150,7 @@ export default class NoteTreeWidget extends TabAwareWidget { const activeNotePath = activeNode !== null ? treeService.getNotePath(activeNode) : null; - const rootNode = await this.prepareRootNode(); + const rootNode = this.prepareRootNode(); await this.batchUpdate(async () => { await this.tree.reload([rootNode]); diff --git a/src/routes/api/tree.js b/src/routes/api/tree.js index cba7e3648..37e8f00b7 100644 --- a/src/routes/api/tree.js +++ b/src/routes/api/tree.js @@ -50,23 +50,30 @@ function getNotesAndBranchesAndAttributes(noteIds) { }; } -function getTree() { - const hoistedNoteId = optionService.getOption('hoistedNoteId'); +function getTree(req) { + const subTreeNoteId = req.query.subTreeNoteId || optionService.getOption('hoistedNoteId'); - // we fetch all branches of notes, even if that particular branch isn't visible - // this allows us to e.g. detect and properly display clones const noteIds = sql.getColumn(` WITH RECURSIVE - tree(branchId, noteId, isExpanded) AS ( - SELECT branchId, noteId, isExpanded FROM branches WHERE noteId = ? - UNION ALL - SELECT branches.branchId, branches.noteId, branches.isExpanded FROM branches - JOIN tree ON branches.parentNoteId = tree.noteId - WHERE tree.isExpanded = 1 AND branches.isDeleted = 0 - ) - SELECT noteId FROM tree`, [hoistedNoteId]); + treeWithDescendants(noteId, isExpanded) AS ( + SELECT noteId, 1 FROM branches WHERE parentNoteId = ? AND isDeleted = 0 + UNION + SELECT branches.noteId, branches.isExpanded FROM branches + JOIN treeWithDescendants ON branches.parentNoteId = treeWithDescendants.noteId + WHERE treeWithDescendants.isExpanded = 1 + AND branches.isDeleted = 0 + ), + treeWithDescendantsAndAscendants AS ( + SELECT noteId FROM treeWithDescendants + UNION + SELECT branches.parentNoteId FROM branches + JOIN treeWithDescendantsAndAscendants ON branches.noteId = treeWithDescendantsAndAscendants.noteId + WHERE branches.isDeleted = 0 + AND branches.parentNoteId != ? + ) + SELECT noteId FROM treeWithDescendantsAndAscendants`, [subTreeNoteId, subTreeNoteId]); - noteIds.push('root'); + noteIds.push(subTreeNoteId); return getNotesAndBranchesAndAttributes(noteIds); }