mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02: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