change the heuristics to choose the best note path when ambiguous/incomplete/just noteId is provided, #1711

This commit is contained in:
zadam 2021-03-06 20:23:29 +01:00
parent 060d4fc27b
commit 9f002fa802
5 changed files with 60 additions and 43 deletions

View File

@ -253,6 +253,32 @@ class NoteShort {
return noteAttributeCache.attributes[this.noteId];
}
getAllNotePaths() {
if (this.noteId === 'root') {
return [['root']];
}
const parentNotes = this.getParentNotes();
let paths;
if (parentNotes.length === 1) { // optimization for the most common case
paths = parentNotes[0].getAllNotePaths();
}
else {
paths = [];
for (const parentNote of parentNotes) {
paths.push(...parentNote.getAllNotePaths());
}
}
for (const path of paths) {
path.push(this.noteId);
}
return paths;
}
__filterAttrs(attributes, type, name) {
if (!type && !name) {
return attributes;

View File

@ -125,7 +125,7 @@ async function deleteNotes(branchIdsToDelete) {
}
async function moveNodeUpInHierarchy(node) {
if (hoistedNoteService.isRootNode(node)
if (hoistedNoteService.isHoistedNode(node)
|| hoistedNoteService.isTopLevelNode(node)
|| node.getParent().data.noteType === 'search') {
return;

View File

@ -16,10 +16,10 @@ async function unhoist() {
}
function isTopLevelNode(node) {
return isRootNode(node.getParent());
return isHoistedNode(node.getParent());
}
function isRootNode(node) {
function isHoistedNode(node) {
// even though check for 'root' should not be necessary, we keep it just in case
return node.data.noteId === "root"
|| node.data.noteId === getHoistedNoteId();
@ -55,6 +55,6 @@ export default {
getHoistedNoteId,
unhoist,
isTopLevelNode,
isRootNode,
isHoistedNode,
checkNoteAccess
}

View File

@ -75,14 +75,10 @@ async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logEr
console.log(utils.now(), `Did not find parent ${parentNoteId} (${parent ? parent.title : 'n/a'}) for child ${childNoteId} (${child.title}), available parents: ${parents.map(p => `${p.noteId} (${p.title})`)}`);
}
const hoistedNote = await treeCache.getNote(hoistedNoteId);
const chosenParent = parents.find(parent => parent.hasAncestor(hoistedNote))
|| parents[0]; // if no parent is in hoisted subtree then it just doesn't matter and pick any
const someNotePath = getSomeNotePath(chosenParent);
const someNotePath = getSomeNotePath(child, hoistedNoteId);
if (someNotePath) { // in case it's root the path may be empty
const pathToRoot = someNotePath.split("/").reverse();
const pathToRoot = someNotePath.split("/").reverse().slice(1);
for (const noteId of pathToRoot) {
effectivePath.push(noteId);
@ -100,29 +96,35 @@ async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logEr
return effectivePath.reverse();
}
function getSomeNotePath(note) {
function getSomeNotePathSegments(note, hoistedNotePath = 'root') {
utils.assertArguments(note);
const path = [];
const notePaths = note.getAllNotePaths().map(path => ({
notePath: path,
isInHoistedSubTree: path.includes(hoistedNotePath),
isArchived: path.find(noteId => treeCache.notes[noteId].hasLabel('archived')),
isSearch: path.find(noteId => treeCache.notes[noteId].type === 'search')
}));
let cur = note;
while (cur.noteId !== 'root') {
path.push(cur.noteId);
const parents = cur.getParentNotes().filter(note => note.type !== 'search');
if (!parents.length) {
logError(`Can't find parents for note ${cur.noteId}`);
return;
notePaths.sort((a, b) => {
if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
return a.isInHoistedSubTree ? -1 : 1;
} else if (a.isSearch !== b.isSearch) {
return a.isSearch ? 1 : -1;
} else if (a.isArchived !== b.isArchived) {
return a.isArchived ? 1 : -1;
} else {
return a.notePath.length - b.notePath.length;
}
});
cur = parents[0];
}
return notePaths[0].notePath;
}
path.push('root');
function getSomeNotePath(note, hoistedNotePath = 'root') {
const notePath = getSomeNotePathSegments(note, hoistedNotePath);
return path.reverse().join('/');
return notePath.join('/');
}
async function sortAlphabetically(noteId) {
@ -142,7 +144,7 @@ ws.subscribeToMessages(message => {
});
function getParentProtectedStatus(node) {
return hoistedNoteService.isRootNode(node) ? 0 : node.getParent().data.isProtected;
return hoistedNoteService.isHoistedNode(node) ? 0 : node.getParent().data.isProtected;
}
function getNoteIdFromNotePath(notePath) {
@ -202,7 +204,7 @@ function getNotePath(node) {
const path = [];
while (node && !hoistedNoteService.isRootNode(node)) {
while (node) {
if (node.data.noteId) {
path.push(node.data.noteId);
}
@ -210,10 +212,6 @@ function getNotePath(node) {
node = node.getParent();
}
if (node) { // null node can happen directly after unhoisting when tree is still hoisted but option has been changed already
path.push(node.data.noteId); // root or hoisted noteId
}
return path.reverse().join("/");
}
@ -317,6 +315,7 @@ export default {
resolveNotePath,
resolveNotePathToSegments,
getSomeNotePath,
getSomeNotePathSegments,
getParentProtectedStatus,
getNotePath,
getNoteIdFromNotePath,

View File

@ -1210,18 +1210,10 @@ export default class NoteTreeWidget extends TabAwareWidget {
this.tree.clearFilter();
}
else {
let found = false;console.log(this.tabContext.notePath, this.tabContext.hoistedNoteId, "ZZZ");
// hack when hoisted note is cloned then it could be filtered multiple times while we want only 1
this.tree.filterBranches(node => {
if (found) {
return false;
}
found = node.data.noteId === this.tabContext.hoistedNoteId;
return found;
});
this.tree.filterBranches(node =>
node.data.noteId === this.tabContext.hoistedNoteId // optimization to not having always resolve the node path
&& treeService.getNotePath(node) === hoistedNotePath);
}
}
}