From 4a89df7ebf34fb416fb7ef8ba192ad7000752e9d Mon Sep 17 00:00:00 2001 From: zadam Date: Wed, 18 Mar 2020 22:35:54 +0100 Subject: [PATCH] always keep all the ancestors in the tree WIP --- src/public/javascripts/entities/note_short.js | 96 +++++++++---------- src/public/javascripts/services/tree.js | 4 +- src/public/javascripts/services/tree_cache.js | 61 +++++++++++- src/public/javascripts/widgets/note_paths.js | 2 +- src/public/javascripts/widgets/search_box.js | 2 +- src/routes/api/tree.js | 2 +- 6 files changed, 109 insertions(+), 58 deletions(-) diff --git a/src/public/javascripts/entities/note_short.js b/src/public/javascripts/entities/note_short.js index 8424e797b..9d2ce7eb3 100644 --- a/src/public/javascripts/entities/note_short.js +++ b/src/public/javascripts/entities/note_short.js @@ -108,8 +108,8 @@ class NoteShort { return Object.values(this.parentToBranch); } - /** @returns {Promise} */ - async getBranches() { + /** @returns {Branch[]} */ + getBranches() { const branchIds = Object.values(this.parentToBranch); return this.treeCache.getBranches(branchIds); @@ -120,8 +120,8 @@ class NoteShort { return this.children.length > 0; } - /** @returns {Promise} */ - async getChildBranches() { + /** @returns {Branch[]} */ + getChildBranches() { // don't use Object.values() to guarantee order const branchIds = this.children.map(childNoteId => this.childToBranch[childNoteId]); @@ -133,9 +133,9 @@ class NoteShort { return this.parents; } - /** @returns {Promise} */ - async getParentNotes() { - return await this.treeCache.getNotes(this.parents); + /** @returns {NoteShort[]} */ + getParentNotes() { + return this.treeCache.getNotesFromCache(this.parents); } /** @returns {string[]} */ @@ -164,9 +164,9 @@ class NoteShort { /** * @param {string} [type] - (optional) attribute type to filter * @param {string} [name] - (optional) attribute name to filter - * @returns {Promise} all note's attributes, including inherited ones + * @returns {Attribute[]} all note's attributes, including inherited ones */ - async getAttributes(type, name) { + getAttributes(type, name) { const ownedAttributes = this.getOwnedAttributes(); const attrArrs = [ @@ -174,16 +174,16 @@ class NoteShort { ]; for (const templateAttr of ownedAttributes.filter(oa => oa.type === 'relation' && oa.name === 'template')) { - const templateNote = await this.treeCache.getNote(templateAttr.value); + const templateNote = this.treeCache.getNoteFromCache(templateAttr.value); if (templateNote) { - attrArrs.push(await templateNote.getAttributes()); + attrArrs.push(templateNote.getAttributes()); } } if (this.noteId !== 'root') { - for (const parentNote of await this.getParentNotes()) { - attrArrs.push(await parentNote.getInheritableAttributes()); + for (const parentNote of this.getParentNotes()) { + attrArrs.push(parentNote.getInheritableAttributes()); } } @@ -204,8 +204,8 @@ class NoteShort { } } - async getInheritableAttributes() { - const attrs = await this.getAttributes(); + getInheritableAttributes() { + const attrs = this.getAttributes(); return attrs.filter(attr => attr.isInheritable); } @@ -220,18 +220,18 @@ class NoteShort { /** * @param {string} [name] - label name to filter - * @returns {Promise} all note's labels (attributes with type label), including inherited ones + * @returns {Attribute[]} all note's labels (attributes with type label), including inherited ones */ - async getLabels(name) { - return await this.getAttributes(LABEL, name); + getLabels(name) { + return this.getAttributes(LABEL, name); } /** * @param {string} [name] - label name to filter - * @returns {Promise} all note's label definitions, including inherited ones + * @returns {Attribute[]} all note's label definitions, including inherited ones */ - async getLabelDefinitions(name) { - return await this.getAttributes(LABEL_DEFINITION, name); + getLabelDefinitions(name) { + return this.getAttributes(LABEL_DEFINITION, name); } /** @@ -244,27 +244,27 @@ class NoteShort { /** * @param {string} [name] - relation name to filter - * @returns {Promise} all note's relations (attributes with type relation), including inherited ones + * @returns {Attribute[]} all note's relations (attributes with type relation), including inherited ones */ - async getRelations(name) { - return await this.getAttributes(RELATION, name); + getRelations(name) { + return this.getAttributes(RELATION, name); } /** * @param {string} [name] - relation name to filter - * @returns {Promise} all note's relation definitions including inherited ones + * @returns {Attribute[]} all note's relation definitions including inherited ones */ - async getRelationDefinitions(name) { - return await this.getAttributes(RELATION_DEFINITION, name); + getRelationDefinitions(name) { + return this.getAttributes(RELATION_DEFINITION, name); } /** * @param {string} type - attribute type (label, relation, etc.) * @param {string} name - attribute name - * @returns {Promise} true if note has an attribute with given type and name (including inherited) + * @returns {boolean} true if note has an attribute with given type and name (including inherited) */ - async hasAttribute(type, name) { - return !!await this.getAttribute(type, name); + hasAttribute(type, name) { + return !!this.getAttribute(type, name); } /** @@ -290,10 +290,10 @@ class NoteShort { /** * @param {string} type - attribute type (label, relation, etc.) * @param {string} name - attribute name - * @returns {Promise} attribute of given type and name. If there's more such attributes, first is returned. Returns null if there's no such attribute belonging to this note. + * @returns {Attribute} attribute of given type and name. If there's more such attributes, first is returned. Returns null if there's no such attribute belonging to this note. */ - async getAttribute(type, name) { - const attributes = await this.getAttributes(type, name); + getAttribute(type, name) { + const attributes = this.getAttributes(type, name); return attributes.length > 0 ? attributes[0] : 0; } @@ -312,10 +312,10 @@ class NoteShort { /** * @param {string} type - attribute type (label, relation, etc.) * @param {string} name - attribute name - * @returns {Promise} attribute value of given type and name or null if no such attribute exists. + * @returns {string} attribute value of given type and name or null if no such attribute exists. */ - async getAttributeValue(type, name) { - const attr = await this.getAttribute(type, name); + getAttributeValue(type, name) { + const attr = this.getAttribute(type, name); return attr ? attr.value : null; } @@ -328,9 +328,9 @@ class NoteShort { /** * @param {string} name - label name - * @returns {Promise} true if label exists (including inherited) + * @returns {boolean} true if label exists (including inherited) */ - async hasLabel(name) { return await this.hasAttribute(LABEL, name); } + hasLabel(name) { return this.hasAttribute(LABEL, name); } /** * @param {string} name - relation name @@ -340,9 +340,9 @@ class NoteShort { /** * @param {string} name - relation name - * @returns {Promise} true if relation exists (including inherited) + * @returns {boolean} true if relation exists (including inherited) */ - async hasRelation(name) { return await this.hasAttribute(RELATION, name); } + hasRelation(name) { return this.hasAttribute(RELATION, name); } /** * @param {string} name - label name @@ -352,9 +352,9 @@ class NoteShort { /** * @param {string} name - label name - * @returns {Promise} label if it exists, null otherwise + * @returns {Attribute} label if it exists, null otherwise */ - async getLabel(name) { return await this.getAttribute(LABEL, name); } + getLabel(name) { return this.getAttribute(LABEL, name); } /** * @param {string} name - relation name @@ -364,9 +364,9 @@ class NoteShort { /** * @param {string} name - relation name - * @returns {Promise} relation if it exists, null otherwise + * @returns {Attribute} relation if it exists, null otherwise */ - async getRelation(name) { return await this.getAttribute(RELATION, name); } + getRelation(name) { return this.getAttribute(RELATION, name); } /** * @param {string} name - label name @@ -376,9 +376,9 @@ class NoteShort { /** * @param {string} name - label name - * @returns {Promise} label value if label exists, null otherwise + * @returns {string} label value if label exists, null otherwise */ - async getLabelValue(name) { return await this.getAttributeValue(LABEL, name); } + getLabelValue(name) { return this.getAttributeValue(LABEL, name); } /** * @param {string} name - relation name @@ -388,9 +388,9 @@ class NoteShort { /** * @param {string} name - relation name - * @returns {Promise} relation value if relation exists, null otherwise + * @returns {string} relation value if relation exists, null otherwise */ - async getRelationValue(name) { return await this.getAttributeValue(RELATION, name); } + getRelationValue(name) { return this.getAttributeValue(RELATION, name); } /** * @param {string} name diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js index 05a7ca2cb..2f87dae87 100644 --- a/src/public/javascripts/services/tree.js +++ b/src/public/javascripts/services/tree.js @@ -58,7 +58,7 @@ async function getRunPath(notePath) { return; } - const parents = await child.getParentNotes(); + const parents = child.getParentNotes(); if (!parents) { ws.logError("No parents found for " + childNoteId); @@ -113,7 +113,7 @@ async function getSomeNotePath(note) { while (cur.noteId !== 'root') { path.push(cur.noteId); - const parents = await cur.getParentNotes(); + const parents = cur.getParentNotes(); if (!parents.length) { console.error(`Can't find parents for note ${cur.noteId}`); diff --git a/src/public/javascripts/services/tree_cache.js b/src/public/javascripts/services/tree_cache.js index a81da05bf..7f4095f40 100644 --- a/src/public/javascripts/services/tree_cache.js +++ b/src/public/javascripts/services/tree_cache.js @@ -18,7 +18,7 @@ class TreeCache { } async loadInitialTree() { - const {notes, branches, attributes} = await server.get('tree'); + const resp = await server.get('tree'); // clear the cache only directly before adding new content which is important for e.g. switching to protected session @@ -34,10 +34,42 @@ class TreeCache { /** @type {Object.>} */ this.noteComplementPromises = {}; - this.addResp(notes, branches, attributes); + await this.loadParents(resp); + this.addResp(resp); } - addResp(noteRows, branchRows, attributeRows) { + async loadParents(resp) { + const noteIds = new Set(resp.notes.map(note => note.noteId)); + const missingNoteIds = []; + + for (const branch of resp.branches) { + if (!(branch.parentNoteId in this.notes) && !noteIds.has(branch.parentNoteId) && branch.parentNoteId !== 'none') { + missingNoteIds.push(branch.parentNoteId); + } + } + + for (const attr of resp.attributes) { + if (attr.type === 'relation' && attr.name === 'template' && !(attr.value in this.notes) && !noteIds.has(attr.value)) { + missingNoteIds.push(attr.value); + } + } + + if (missingNoteIds.length > 0) { + const newResp = await server.post('tree/load', { noteIds: missingNoteIds }); + + resp.notes = resp.notes.concat(newResp.notes); + resp.branches = resp.branches.concat(newResp.branches); + resp.attributes = resp.attributes.concat(newResp.attributes); + + await this.loadParents(resp); + } + } + + addResp(resp) { + const noteRows = resp.notes; + const branchRows = resp.branches; + const attributeRows = resp.attributes; + for (const noteRow of noteRows) { const {noteId} = noteRow; @@ -122,7 +154,8 @@ class TreeCache { const resp = await server.post('tree/load', { noteIds }); - this.addResp(resp.notes, resp.branches, resp.attributes); + await this.loadParents(resp); + this.addResp(resp); for (const note of resp.notes) { if (note.type === 'search') { @@ -147,11 +180,29 @@ class TreeCache { })); // update this note with standard (parent) branches + virtual (children) branches - this.addResp([note], branches, []); + this.addResp({ + notes: [note], + branches, + attributes: [] + }); } } } + /** @return {NoteShort[]} */ + getNotesFromCache(noteIds, silentNotFoundError = false) { + return noteIds.map(noteId => { + if (!this.notes[noteId] && !silentNotFoundError) { + console.log(`Can't find note "${noteId}"`); + + return null; + } + else { + return this.notes[noteId]; + } + }).filter(note => !!note); + } + /** @return {Promise} */ async getNotes(noteIds, silentNotFoundError = false) { const missingNoteIds = noteIds.filter(noteId => !this.notes[noteId]); diff --git a/src/public/javascripts/widgets/note_paths.js b/src/public/javascripts/widgets/note_paths.js index 9016c8e9e..a413c34e1 100644 --- a/src/public/javascripts/widgets/note_paths.js +++ b/src/public/javascripts/widgets/note_paths.js @@ -50,7 +50,7 @@ export default class NotePathsWidget extends TabAwareWidget { const pathSegments = this.notePath.split("/"); const activeNoteParentNoteId = pathSegments[pathSegments.length - 2]; // we know this is not root so there must be a parent - for (const parentNote of await this.note.getParentNotes()) { + for (const parentNote of this.note.getParentNotes()) { const parentNotePath = await treeService.getSomeNotePath(parentNote); // this is to avoid having root notes leading '/' const notePath = parentNotePath ? (parentNotePath + '/' + this.noteId) : this.noteId; diff --git a/src/public/javascripts/widgets/search_box.js b/src/public/javascripts/widgets/search_box.js index c980b9519..d53202671 100644 --- a/src/public/javascripts/widgets/search_box.js +++ b/src/public/javascripts/widgets/search_box.js @@ -110,7 +110,7 @@ export default class SearchBoxWidget extends BasicWidget { let activeNote = appContext.tabManager.getActiveTabNote(); if (activeNote.type === 'search') { - activeNote = (await activeNote.getParentNotes())[0]; + activeNote = activeNote.getParentNotes()[0]; } await noteCreateService.createNote(activeNote.noteId, { diff --git a/src/routes/api/tree.js b/src/routes/api/tree.js index d5c04c9b2..e180dfef6 100644 --- a/src/routes/api/tree.js +++ b/src/routes/api/tree.js @@ -55,7 +55,7 @@ async function getTree() { // 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 - let noteIds = await sql.getColumn(` + const noteIds = await sql.getColumn(` WITH RECURSIVE tree(branchId, noteId, isExpanded) AS ( SELECT branchId, noteId, isExpanded FROM branches WHERE noteId = ?