mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 13:01:31 +08:00 
			
		
		
		
	always keep all the ancestors in the tree WIP
This commit is contained in:
		
							parent
							
								
									6295a1825d
								
							
						
					
					
						commit
						4a89df7ebf
					
				| @ -108,8 +108,8 @@ class NoteShort { | |||||||
|         return Object.values(this.parentToBranch); |         return Object.values(this.parentToBranch); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** @returns {Promise<Branch[]>} */ |     /** @returns {Branch[]} */ | ||||||
|     async getBranches() { |     getBranches() { | ||||||
|         const branchIds = Object.values(this.parentToBranch); |         const branchIds = Object.values(this.parentToBranch); | ||||||
| 
 | 
 | ||||||
|         return this.treeCache.getBranches(branchIds); |         return this.treeCache.getBranches(branchIds); | ||||||
| @ -120,8 +120,8 @@ class NoteShort { | |||||||
|         return this.children.length > 0; |         return this.children.length > 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** @returns {Promise<Branch[]>} */ |     /** @returns {Branch[]} */ | ||||||
|     async getChildBranches() { |     getChildBranches() { | ||||||
|         // don't use Object.values() to guarantee order
 |         // don't use Object.values() to guarantee order
 | ||||||
|         const branchIds = this.children.map(childNoteId => this.childToBranch[childNoteId]); |         const branchIds = this.children.map(childNoteId => this.childToBranch[childNoteId]); | ||||||
| 
 | 
 | ||||||
| @ -133,9 +133,9 @@ class NoteShort { | |||||||
|         return this.parents; |         return this.parents; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** @returns {Promise<NoteShort[]>} */ |     /** @returns {NoteShort[]} */ | ||||||
|     async getParentNotes() { |     getParentNotes() { | ||||||
|         return await this.treeCache.getNotes(this.parents); |         return this.treeCache.getNotesFromCache(this.parents); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** @returns {string[]} */ |     /** @returns {string[]} */ | ||||||
| @ -164,9 +164,9 @@ class NoteShort { | |||||||
|     /** |     /** | ||||||
|      * @param {string} [type] - (optional) attribute type to filter |      * @param {string} [type] - (optional) attribute type to filter | ||||||
|      * @param {string} [name] - (optional) attribute name to filter |      * @param {string} [name] - (optional) attribute name to filter | ||||||
|      * @returns {Promise<Attribute[]>} 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 ownedAttributes = this.getOwnedAttributes(); | ||||||
| 
 | 
 | ||||||
|         const attrArrs = [ |         const attrArrs = [ | ||||||
| @ -174,16 +174,16 @@ class NoteShort { | |||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
|         for (const templateAttr of ownedAttributes.filter(oa => oa.type === 'relation' && oa.name === 'template')) { |         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) { |             if (templateNote) { | ||||||
|                 attrArrs.push(await templateNote.getAttributes()); |                 attrArrs.push(templateNote.getAttributes()); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (this.noteId !== 'root') { |         if (this.noteId !== 'root') { | ||||||
|             for (const parentNote of await this.getParentNotes()) { |             for (const parentNote of this.getParentNotes()) { | ||||||
|                 attrArrs.push(await parentNote.getInheritableAttributes()); |                 attrArrs.push(parentNote.getInheritableAttributes()); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -204,8 +204,8 @@ class NoteShort { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getInheritableAttributes() { |     getInheritableAttributes() { | ||||||
|         const attrs = await this.getAttributes(); |         const attrs = this.getAttributes(); | ||||||
| 
 | 
 | ||||||
|         return attrs.filter(attr => attr.isInheritable); |         return attrs.filter(attr => attr.isInheritable); | ||||||
|     } |     } | ||||||
| @ -220,18 +220,18 @@ class NoteShort { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param {string} [name] - label name to filter |      * @param {string} [name] - label name to filter | ||||||
|      * @returns {Promise<Attribute[]>} 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) { |     getLabels(name) { | ||||||
|         return await this.getAttributes(LABEL, name); |         return this.getAttributes(LABEL, name); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param {string} [name] - label name to filter |      * @param {string} [name] - label name to filter | ||||||
|      * @returns {Promise<Attribute[]>} all note's label definitions, including inherited ones |      * @returns {Attribute[]} all note's label definitions, including inherited ones | ||||||
|      */ |      */ | ||||||
|     async getLabelDefinitions(name) { |     getLabelDefinitions(name) { | ||||||
|         return await this.getAttributes(LABEL_DEFINITION, name); |         return this.getAttributes(LABEL_DEFINITION, name); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -244,27 +244,27 @@ class NoteShort { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param {string} [name] - relation name to filter |      * @param {string} [name] - relation name to filter | ||||||
|      * @returns {Promise<Attribute[]>} 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) { |     getRelations(name) { | ||||||
|         return await this.getAttributes(RELATION, name); |         return this.getAttributes(RELATION, name); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param {string} [name] - relation name to filter |      * @param {string} [name] - relation name to filter | ||||||
|      * @returns {Promise<Attribute[]>} all note's relation definitions including inherited ones |      * @returns {Attribute[]} all note's relation definitions including inherited ones | ||||||
|      */ |      */ | ||||||
|     async getRelationDefinitions(name) { |     getRelationDefinitions(name) { | ||||||
|         return await this.getAttributes(RELATION_DEFINITION, name); |         return this.getAttributes(RELATION_DEFINITION, name); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param {string} type - attribute type (label, relation, etc.) |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|      * @param {string} name - attribute name |      * @param {string} name - attribute name | ||||||
|      * @returns {Promise<boolean>} 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) { |     hasAttribute(type, name) { | ||||||
|         return !!await this.getAttribute(type, name); |         return !!this.getAttribute(type, name); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -290,10 +290,10 @@ class NoteShort { | |||||||
|     /** |     /** | ||||||
|      * @param {string} type - attribute type (label, relation, etc.) |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|      * @param {string} name - attribute name |      * @param {string} name - attribute name | ||||||
|      * @returns {Promise<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. |      * @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) { |     getAttribute(type, name) { | ||||||
|         const attributes = await this.getAttributes(type, name); |         const attributes = this.getAttributes(type, name); | ||||||
| 
 | 
 | ||||||
|         return attributes.length > 0 ? attributes[0] : 0; |         return attributes.length > 0 ? attributes[0] : 0; | ||||||
|     } |     } | ||||||
| @ -312,10 +312,10 @@ class NoteShort { | |||||||
|     /** |     /** | ||||||
|      * @param {string} type - attribute type (label, relation, etc.) |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|      * @param {string} name - attribute name |      * @param {string} name - attribute name | ||||||
|      * @returns {Promise<string>} 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) { |     getAttributeValue(type, name) { | ||||||
|         const attr = await this.getAttribute(type, name); |         const attr = this.getAttribute(type, name); | ||||||
| 
 | 
 | ||||||
|         return attr ? attr.value : null; |         return attr ? attr.value : null; | ||||||
|     } |     } | ||||||
| @ -328,9 +328,9 @@ class NoteShort { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param {string} name - label name |      * @param {string} name - label name | ||||||
|      * @returns {Promise<boolean>} 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 |      * @param {string} name - relation name | ||||||
| @ -340,9 +340,9 @@ class NoteShort { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param {string} name - relation name |      * @param {string} name - relation name | ||||||
|      * @returns {Promise<boolean>} 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 |      * @param {string} name - label name | ||||||
| @ -352,9 +352,9 @@ class NoteShort { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param {string} name - label name |      * @param {string} name - label name | ||||||
|      * @returns {Promise<Attribute>} 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 |      * @param {string} name - relation name | ||||||
| @ -364,9 +364,9 @@ class NoteShort { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param {string} name - relation name |      * @param {string} name - relation name | ||||||
|      * @returns {Promise<Attribute>} 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 |      * @param {string} name - label name | ||||||
| @ -376,9 +376,9 @@ class NoteShort { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param {string} name - label name |      * @param {string} name - label name | ||||||
|      * @returns {Promise<string>} 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 |      * @param {string} name - relation name | ||||||
| @ -388,9 +388,9 @@ class NoteShort { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param {string} name - relation name |      * @param {string} name - relation name | ||||||
|      * @returns {Promise<string>} 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 |      * @param {string} name | ||||||
|  | |||||||
| @ -58,7 +58,7 @@ async function getRunPath(notePath) { | |||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             const parents = await child.getParentNotes(); |             const parents = child.getParentNotes(); | ||||||
| 
 | 
 | ||||||
|             if (!parents) { |             if (!parents) { | ||||||
|                 ws.logError("No parents found for " + childNoteId); |                 ws.logError("No parents found for " + childNoteId); | ||||||
| @ -113,7 +113,7 @@ async function getSomeNotePath(note) { | |||||||
|     while (cur.noteId !== 'root') { |     while (cur.noteId !== 'root') { | ||||||
|         path.push(cur.noteId); |         path.push(cur.noteId); | ||||||
| 
 | 
 | ||||||
|         const parents = await cur.getParentNotes(); |         const parents = cur.getParentNotes(); | ||||||
| 
 | 
 | ||||||
|         if (!parents.length) { |         if (!parents.length) { | ||||||
|             console.error(`Can't find parents for note ${cur.noteId}`); |             console.error(`Can't find parents for note ${cur.noteId}`); | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ class TreeCache { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async loadInitialTree() { |     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
 |         // 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.<string, Promise<NoteComplement>>} */ |         /** @type {Object.<string, Promise<NoteComplement>>} */ | ||||||
|         this.noteComplementPromises = {}; |         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) { |         for (const noteRow of noteRows) { | ||||||
|             const {noteId} = noteRow; |             const {noteId} = noteRow; | ||||||
| 
 | 
 | ||||||
| @ -122,7 +154,8 @@ class TreeCache { | |||||||
| 
 | 
 | ||||||
|         const resp = await server.post('tree/load', { noteIds }); |         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) { |         for (const note of resp.notes) { | ||||||
|             if (note.type === 'search') { |             if (note.type === 'search') { | ||||||
| @ -147,11 +180,29 @@ class TreeCache { | |||||||
|                 })); |                 })); | ||||||
| 
 | 
 | ||||||
|                 // update this note with standard (parent) branches + virtual (children) branches
 |                 // 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<NoteShort[]>} */ |     /** @return {Promise<NoteShort[]>} */ | ||||||
|     async getNotes(noteIds, silentNotFoundError = false) { |     async getNotes(noteIds, silentNotFoundError = false) { | ||||||
|         const missingNoteIds = noteIds.filter(noteId => !this.notes[noteId]); |         const missingNoteIds = noteIds.filter(noteId => !this.notes[noteId]); | ||||||
|  | |||||||
| @ -50,7 +50,7 @@ export default class NotePathsWidget extends TabAwareWidget { | |||||||
|         const pathSegments = this.notePath.split("/"); |         const pathSegments = this.notePath.split("/"); | ||||||
|         const activeNoteParentNoteId = pathSegments[pathSegments.length - 2]; // we know this is not root so there must be a parent
 |         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); |             const parentNotePath = await treeService.getSomeNotePath(parentNote); | ||||||
|             // this is to avoid having root notes leading '/'
 |             // this is to avoid having root notes leading '/'
 | ||||||
|             const notePath = parentNotePath ? (parentNotePath + '/' + this.noteId) : this.noteId; |             const notePath = parentNotePath ? (parentNotePath + '/' + this.noteId) : this.noteId; | ||||||
|  | |||||||
| @ -110,7 +110,7 @@ export default class SearchBoxWidget extends BasicWidget { | |||||||
|         let activeNote = appContext.tabManager.getActiveTabNote(); |         let activeNote = appContext.tabManager.getActiveTabNote(); | ||||||
| 
 | 
 | ||||||
|         if (activeNote.type === 'search') { |         if (activeNote.type === 'search') { | ||||||
|             activeNote = (await activeNote.getParentNotes())[0]; |             activeNote = activeNote.getParentNotes()[0]; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         await noteCreateService.createNote(activeNote.noteId, { |         await noteCreateService.createNote(activeNote.noteId, { | ||||||
|  | |||||||
| @ -55,7 +55,7 @@ async function getTree() { | |||||||
| 
 | 
 | ||||||
|     // we fetch all branches of notes, even if that particular branch isn't visible
 |     // 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
 |     // this allows us to e.g. detect and properly display clones
 | ||||||
|     let noteIds = await sql.getColumn(` |     const noteIds = await sql.getColumn(` | ||||||
|         WITH RECURSIVE |         WITH RECURSIVE | ||||||
|             tree(branchId, noteId, isExpanded) AS ( |             tree(branchId, noteId, isExpanded) AS ( | ||||||
|             SELECT branchId, noteId, isExpanded FROM branches WHERE noteId = ?  |             SELECT branchId, noteId, isExpanded FROM branches WHERE noteId = ?  | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zadam
						zadam