mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	basic lazy loading of tree now works, still WIP
This commit is contained in:
		| @@ -16,13 +16,18 @@ class NoteShort { | ||||
|     async getBranches() { | ||||
|         const branches = []; | ||||
|  | ||||
|         for (const parent of this.treeCache.parents[this.noteId]) { | ||||
|             branches.push(await this.treeCache.getBranchByChildParent(this.noteId, parent.noteId)); | ||||
|         for (const parentNoteId of this.treeCache.parents[this.noteId]) { | ||||
|             branches.push(await this.treeCache.getBranchByChildParent(this.noteId, parentNoteId)); | ||||
|         } | ||||
|  | ||||
|         return branches; | ||||
|     } | ||||
|  | ||||
|     hasChildren() { | ||||
|         return this.treeCache.children[this.noteId] | ||||
|             && this.treeCache.children[this.noteId].length > 0; | ||||
|     } | ||||
|  | ||||
|     async getChildBranches() { | ||||
|         if (!this.treeCache.children[this.noteId]) { | ||||
|             return []; | ||||
| @@ -30,19 +35,33 @@ class NoteShort { | ||||
|  | ||||
|         const branches = []; | ||||
|  | ||||
|         for (const child of this.treeCache.children[this.noteId]) { | ||||
|             branches.push(await this.treeCache.getBranchByChildParent(child.noteId, this.noteId)); | ||||
|         for (const childNoteId of this.treeCache.children[this.noteId]) { | ||||
|             branches.push(await this.treeCache.getBranchByChildParent(childNoteId, this.noteId)); | ||||
|         } | ||||
|  | ||||
|         return branches; | ||||
|     } | ||||
|  | ||||
|     async __getNotes(noteIds) { | ||||
|         if (!noteIds) { | ||||
|             return []; | ||||
|         } | ||||
|  | ||||
|         const notes = []; | ||||
|  | ||||
|         for (const noteId of noteIds) { | ||||
|             notes.push(await this.treeCache.getNote(noteId)); | ||||
|         } | ||||
|  | ||||
|         return notes; | ||||
|     } | ||||
|  | ||||
|     async getParentNotes() { | ||||
|         return this.treeCache.parents[this.noteId] || []; | ||||
|         return this.__getNotes(this.treeCache.parents[this.noteId]); | ||||
|     } | ||||
|  | ||||
|     async getChildNotes() { | ||||
|         return this.treeCache.children[this.noteId] || []; | ||||
|         return this.__getNotes(this.treeCache.children[this.noteId]); | ||||
|     } | ||||
|  | ||||
|     get toString() { | ||||
|   | ||||
| @@ -285,14 +285,14 @@ async function treeInitialized() { | ||||
|     } | ||||
| } | ||||
|  | ||||
| function initFancyTree(branch) { | ||||
|     utils.assertArguments(branch); | ||||
| function initFancyTree(tree) { | ||||
|     utils.assertArguments(tree); | ||||
|  | ||||
|     $tree.fancytree({ | ||||
|         autoScroll: true, | ||||
|         keyboard: false, // we takover keyboard handling in the hotkeys plugin | ||||
|         extensions: ["hotkeys", "filter", "dnd", "clones"], | ||||
|         source: branch, | ||||
|         source: tree, | ||||
|         scrollParent: $tree, | ||||
|         click: (event, data) => { | ||||
|             const targetType = data.targetType; | ||||
| @@ -375,7 +375,7 @@ async function loadTree() { | ||||
|         startNotePath = getNotePathFromAddress(); | ||||
|     } | ||||
|  | ||||
|     return await treeBuilder.prepareTree(resp.notes, resp.branches); | ||||
|     return await treeBuilder.prepareTree(resp.notes, resp.branches, resp.parentToChildren); | ||||
| } | ||||
|  | ||||
| function collapseTree(node = null) { | ||||
|   | ||||
| @@ -5,10 +5,10 @@ import server from "./server.js"; | ||||
| import treeCache from "./tree_cache.js"; | ||||
| import messagingService from "./messaging.js"; | ||||
|  | ||||
| async function prepareTree(noteRows, branchRows) { | ||||
|     utils.assertArguments(noteRows); | ||||
| async function prepareTree(noteRows, branchRows, parentToChildren) { | ||||
|     utils.assertArguments(noteRows, branchRows, parentToChildren); | ||||
|  | ||||
|     treeCache.load(noteRows, branchRows); | ||||
|     treeCache.load(noteRows, branchRows, parentToChildren); | ||||
|  | ||||
|     return await prepareRealBranch(await treeCache.getNote('root')); | ||||
| } | ||||
| @@ -49,9 +49,7 @@ async function prepareRealBranch(parentNote) { | ||||
|             expanded: note.type !== 'search' && branch.isExpanded | ||||
|         }; | ||||
|  | ||||
|         const hasChildren = (await note.getChildNotes()).length > 0; | ||||
|  | ||||
|         if (hasChildren || note.type === 'search') { | ||||
|         if (note.hasChildren() || note.type === 'search') { | ||||
|             node.folder = true; | ||||
|  | ||||
|             if (node.expanded && note.type !== 'search') { | ||||
|   | ||||
| @@ -2,45 +2,82 @@ import utils from "./utils.js"; | ||||
| import Branch from "../entities/branch.js"; | ||||
| import NoteShort from "../entities/note_short.js"; | ||||
| import infoService from "./info.js"; | ||||
| import server from "./server.js"; | ||||
|  | ||||
| class TreeCache { | ||||
|     load(noteRows, branchRows) { | ||||
|         this.parents = []; | ||||
|         this.children = []; | ||||
|     load(noteRows, branchRows, parentToChildren) { | ||||
|         this.parents = {}; | ||||
|         this.children = {}; | ||||
|         this.childParentToBranch = {}; | ||||
|  | ||||
|         /** @type {Object.<string, NoteShort>} */ | ||||
|         this.notes = {}; | ||||
|  | ||||
|         /** @type {Object.<string, Branch>} */ | ||||
|         this.branches = {}; | ||||
|  | ||||
|         this.addResp(noteRows, branchRows, parentToChildren); | ||||
|     } | ||||
|  | ||||
|     addResp(noteRows, branchRows, parentToChildren) { | ||||
|         for (const noteRow of noteRows) { | ||||
|             const note = new NoteShort(this, noteRow); | ||||
|  | ||||
|             this.notes[note.noteId] = note; | ||||
|         } | ||||
|  | ||||
|         /** @type {Object.<string, Branch>} */ | ||||
|         this.branches = {}; | ||||
|         for (const branchRow of branchRows) { | ||||
|             const branch = new Branch(this, branchRow); | ||||
|  | ||||
|             this.addBranch(branch); | ||||
|         } | ||||
|  | ||||
|         for (const relation of parentToChildren) { | ||||
|             this.addBranchRelationship(relation.branchId, relation.childNoteId, relation.parentNoteId); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** @return NoteShort */ | ||||
|     async getNote(noteId) { | ||||
|         if (this.notes[noteId] === undefined) { | ||||
|             const resp = await server.post('tree/load', { | ||||
|                 noteIds: [noteId] | ||||
|             }); | ||||
|  | ||||
|             this.addResp(resp.notes, resp.branches, resp.parentToChildren); | ||||
|         } | ||||
|  | ||||
|         if (!this.notes[noteId]) { | ||||
|             throw new Error(`Can't find note ${noteId}`); | ||||
|         } | ||||
|  | ||||
|         return this.notes[noteId]; | ||||
|     } | ||||
|  | ||||
|     addBranch(branch) { | ||||
|         this.branches[branch.branchId] = branch; | ||||
|  | ||||
|         this.parents[branch.noteId] = this.parents[branch.noteId] || []; | ||||
|         this.parents[branch.noteId].push(this.notes[branch.parentNoteId]); | ||||
|         this.addBranchRelationship(branch.branchId, branch.noteId, branch.parentNoteId); | ||||
|     } | ||||
|  | ||||
|         this.children[branch.parentNoteId] = this.children[branch.parentNoteId] || []; | ||||
|         this.children[branch.parentNoteId].push(this.notes[branch.noteId]); | ||||
|     addBranchRelationship(branchId, childNoteId, parentNoteId) { | ||||
|         this.addParentChildRelationship(parentNoteId, childNoteId); | ||||
|  | ||||
|         this.childParentToBranch[branch.noteId + '-' + branch.parentNoteId] = branch; | ||||
|         this.childParentToBranch[childNoteId + '-' + parentNoteId] = branchId; | ||||
|     } | ||||
|  | ||||
|     addParentChildRelationship(parentNoteId, childNoteId) { | ||||
|         this.parents[childNoteId] = this.parents[childNoteId] || []; | ||||
|  | ||||
|         if (!this.parents[childNoteId].includes(parentNoteId)) { | ||||
|             this.parents[childNoteId].push(parentNoteId); | ||||
|         } | ||||
|  | ||||
|         this.children[parentNoteId] = this.children[parentNoteId] || []; | ||||
|  | ||||
|         if (!this.children[parentNoteId].includes(childNoteId)) { | ||||
|             this.children[parentNoteId].push(childNoteId); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     add(note, branch) { | ||||
| @@ -51,19 +88,34 @@ class TreeCache { | ||||
|  | ||||
|     /** @return Branch */ | ||||
|     async getBranch(branchId) { | ||||
|         if (this.branches[branchId] === undefined) { | ||||
|             const resp = await server.post('tree/load', { | ||||
|                 branchIds: [branchId] | ||||
|             }); | ||||
|  | ||||
|             this.addResp(resp.notes, resp.branches, resp.parentToChildren); | ||||
|         } | ||||
|  | ||||
|         if (!this.branches[branchId]) { | ||||
|             throw new Error(`Can't find branch ${branchId}`); | ||||
|         } | ||||
|  | ||||
|         return this.branches[branchId]; | ||||
|     } | ||||
|  | ||||
|     /** @return Branch */ | ||||
|     async getBranchByChildParent(childNoteId, parentNoteId) { | ||||
|         const key = (childNoteId + '-' + parentNoteId); | ||||
|         const branch = this.childParentToBranch[key]; | ||||
|         // this will make sure the note and its relationships are loaded | ||||
|         await this.getNote(parentNoteId); | ||||
|  | ||||
|         if (!branch) { | ||||
|         const key = (childNoteId + '-' + parentNoteId); | ||||
|         const branchId = this.childParentToBranch[key]; | ||||
|  | ||||
|         if (!branchId) { | ||||
|             infoService.throwError("Cannot find branch for child-parent=" + key); | ||||
|         } | ||||
|  | ||||
|         return branch; | ||||
|         return await this.getBranch(branchId); | ||||
|     } | ||||
|  | ||||
|     /* Move note from one parent to another. */ | ||||
| @@ -78,33 +130,14 @@ class TreeCache { | ||||
|         delete treeCache.childParentToBranch[childNoteId + '-' + oldParentNoteId]; // this is correct because we know that oldParentId isn't same as newParentId | ||||
|  | ||||
|         // remove old associations | ||||
|         treeCache.parents[childNoteId] = treeCache.parents[childNoteId].filter(p => p.noteId !== oldParentNoteId); | ||||
|         treeCache.children[oldParentNoteId] = treeCache.children[oldParentNoteId].filter(ch => ch.noteId !== childNoteId); | ||||
|         treeCache.parents[childNoteId] = treeCache.parents[childNoteId].filter(p => p !== oldParentNoteId); | ||||
|         treeCache.children[oldParentNoteId] = treeCache.children[oldParentNoteId].filter(ch => ch !== childNoteId); | ||||
|  | ||||
|         // add new associations | ||||
|         treeCache.parents[childNoteId].push(await treeCache.getNote(newParentNoteId)); | ||||
|         treeCache.parents[childNoteId].push(newParentNoteId); | ||||
|  | ||||
|         treeCache.children[newParentNoteId] = treeCache.children[newParentNoteId] || []; // this might be first child | ||||
|         treeCache.children[newParentNoteId].push(await treeCache.getNote(childNoteId)); | ||||
|     } | ||||
|  | ||||
|     removeParentChildRelation(parentNoteId, childNoteId) { | ||||
|         utils.assertArguments(parentNoteId, childNoteId); | ||||
|  | ||||
|         treeCache.parents[childNoteId] = treeCache.parents[childNoteId].filter(p => p.noteId !== parentNoteId); | ||||
|         treeCache.children[parentNoteId] = treeCache.children[parentNoteId].filter(ch => ch.noteId !== childNoteId); | ||||
|  | ||||
|         delete treeCache.childParentToBranch[childNoteId + '-' + parentNoteId]; | ||||
|     } | ||||
|  | ||||
|     async setParentChildRelation(branchId, parentNoteId, childNoteId) { | ||||
|         treeCache.parents[childNoteId] = treeCache.parents[childNoteId] || []; | ||||
|         treeCache.parents[childNoteId].push(await treeCache.getNote(parentNoteId)); | ||||
|  | ||||
|         treeCache.children[parentNoteId] = treeCache.children[parentNoteId] || []; | ||||
|         treeCache.children[parentNoteId].push(await treeCache.getNote(childNoteId)); | ||||
|  | ||||
|         treeCache.childParentToBranch[childNoteId + '-' + parentNoteId] = await treeCache.getBranch(branchId); | ||||
|         treeCache.children[newParentNoteId].push(childNoteId); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -3,7 +3,26 @@ | ||||
| const sql = require('../../services/sql'); | ||||
| const optionService = require('../../services/options'); | ||||
| const protectedSessionService = require('../../services/protected_session'); | ||||
| const utils = require('../../services/utils'); | ||||
|  | ||||
| async function getNotes(noteIds) { | ||||
|     const questionMarks = noteIds.map(() => "?").join(","); | ||||
|  | ||||
|     const notes = await sql.getRows(` | ||||
|       SELECT noteId, title, isProtected, type, mime | ||||
|       FROM notes WHERE isDeleted = 0 AND noteId IN (${questionMarks})`, noteIds); | ||||
|  | ||||
|     protectedSessionService.decryptNotes(notes); | ||||
|  | ||||
|     notes.forEach(note => note.isProtected = !!note.isProtected); | ||||
|     return notes; | ||||
| } | ||||
|  | ||||
| async function getParentToChildren(noteIds) { | ||||
|     const questionMarks = noteIds.map(() => "?").join(","); | ||||
|  | ||||
|     return await sql.getRows(`SELECT branchId, noteId AS 'childNoteId', parentNoteId FROM branches WHERE isDeleted = 0  | ||||
|          AND parentNoteId IN (${questionMarks})`, noteIds); | ||||
| } | ||||
|  | ||||
| async function getTree() { | ||||
|     const branches = await sql.getRows(` | ||||
| @@ -18,34 +37,43 @@ async function getTree() { | ||||
|         SELECT branches.* FROM tree JOIN branches USING(branchId);`); | ||||
|  | ||||
|     const noteIds = branches.map(b => b.noteId); | ||||
|     const questionMarks = branches.map(() => "?").join(","); | ||||
|  | ||||
|     const notes = await sql.getRows(` | ||||
|       SELECT noteId, title, isProtected, type, mime | ||||
|       FROM notes WHERE isDeleted = 0 AND noteId IN (${questionMarks})`, noteIds); | ||||
|     const notes = await getNotes(noteIds); | ||||
|  | ||||
|     protectedSessionService.decryptNotes(notes); | ||||
|  | ||||
|     notes.forEach(note => note.isProtected = !!note.isProtected); | ||||
|  | ||||
|     const relationships = await sql.getRows(`SELECT noteId, parentNoteId FROM branches WHERE isDeleted = 0  | ||||
|          AND parentNoteId IN (${questionMarks})`, noteIds); | ||||
|  | ||||
|     const parentToChild = {}; | ||||
|  | ||||
|     for (const rel of relationships) { | ||||
|         parentToChild[rel.parentNoteId] = parentToChild[rel.parentNoteId] || []; | ||||
|         parentToChild[rel.parentNoteId].push(rel.noteId); | ||||
|     } | ||||
|     const parentToChildren = await getParentToChildren(noteIds); | ||||
|  | ||||
|     return { | ||||
|         startNotePath: await optionService.getOption('startNotePath'), | ||||
|         branches: branches, | ||||
|         notes: notes, | ||||
|         parentToChild | ||||
|         branches, | ||||
|         notes, | ||||
|         parentToChildren | ||||
|     }; | ||||
| } | ||||
|  | ||||
| async function load(req) { | ||||
|     let noteIds = req.body.noteIds; | ||||
|     const branchIds = req.body.branchIds; | ||||
|  | ||||
|     if (branchIds && branchIds.length > 0) { | ||||
|         noteIds = await sql.getColumn(`SELECT noteId FROM branches WHERE isDeleted = 0 AND branchId IN(${branchIds.map(() => "?").join(",")})`, branchIds); | ||||
|     } | ||||
|  | ||||
|     const questionMarks = noteIds.map(() => "?").join(","); | ||||
|  | ||||
|     const branches = await sql.getRows(`SELECT * FROM branches WHERE isDeleted = 0 AND noteId IN (${questionMarks})`, noteIds); | ||||
|  | ||||
|     const notes = await getNotes(noteIds); | ||||
|  | ||||
|     const parentToChildren = await getParentToChildren(noteIds); | ||||
|  | ||||
|     return { | ||||
|         branches, | ||||
|         notes, | ||||
|         parentToChildren | ||||
|     }; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     getTree | ||||
|     getTree, | ||||
|     load | ||||
| }; | ||||
|   | ||||
| @@ -99,6 +99,7 @@ function register(app) { | ||||
|     route(GET, '/setup', [auth.checkAppNotInitialized], setupRoute.setupPage); | ||||
|  | ||||
|     apiRoute(GET, '/api/tree', treeApiRoute.getTree); | ||||
|     apiRoute(POST, '/api/tree/load', treeApiRoute.load); | ||||
|     apiRoute(PUT, '/api/branches/:branchId/set-prefix', branchesApiRoute.setPrefix); | ||||
|  | ||||
|     apiRoute(PUT, '/api/branches/:branchId/move-to/:parentNoteId', branchesApiRoute.moveBranchToParent); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user