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