diff --git a/apps/client/src/entities/fnote.ts b/apps/client/src/entities/fnote.ts index bcb6c408e..6d0a15506 100644 --- a/apps/client/src/entities/fnote.ts +++ b/apps/client/src/entities/fnote.ts @@ -417,7 +417,7 @@ export default class FNote { return notePaths; } - getSortedNotePathRecords(hoistedNoteId = "root"): NotePathRecord[] { + getSortedNotePathRecords(hoistedNoteId = "root", activeNotePath: string | null = null): NotePathRecord[] { const isHoistedRoot = hoistedNoteId === "root"; const notePaths: NotePathRecord[] = this.getAllNotePaths().map((path) => ({ @@ -428,7 +428,23 @@ export default class FNote { isHidden: path.includes("_hidden") })); + // Calculate the length of the prefix match between two arrays + const prefixMatchLength = (path: string[], target: string[]) => { + const diffIndex = path.findIndex((seg, i) => seg !== target[i]); + return diffIndex === -1 ? Math.min(path.length, target.length) : diffIndex; + }; + notePaths.sort((a, b) => { + if (activeNotePath) { + const activeSegments = activeNotePath.split('/'); + const aOverlap = prefixMatchLength(a.notePath, activeSegments); + const bOverlap = prefixMatchLength(b.notePath, activeSegments); + // Paths with more matching prefix segments are prioritized + // when the match count is equal, other criteria are used for sorting + if (bOverlap !== aOverlap) { + return bOverlap - aOverlap; + } + } if (a.isInHoistedSubTree !== b.isInHoistedSubTree) { return a.isInHoistedSubTree ? -1 : 1; } else if (a.isArchived !== b.isArchived) { @@ -449,10 +465,11 @@ export default class FNote { * Returns the note path considered to be the "best" * * @param {string} [hoistedNoteId='root'] + * @param {string|null} [activeNotePath=null] * @return {string[]} array of noteIds constituting the particular note path */ - getBestNotePath(hoistedNoteId = "root") { - return this.getSortedNotePathRecords(hoistedNoteId)[0]?.notePath; + getBestNotePath(hoistedNoteId = "root", activeNotePath: string | null = null) { + return this.getSortedNotePathRecords(hoistedNoteId, activeNotePath)[0]?.notePath; } /** diff --git a/apps/client/src/services/tree.ts b/apps/client/src/services/tree.ts index fc54c3c75..ec5bc0191 100644 --- a/apps/client/src/services/tree.ts +++ b/apps/client/src/services/tree.ts @@ -26,21 +26,12 @@ async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root } const path = notePath.split("/").reverse(); - - if (!path.includes("root")) { - path.push("root"); - } - const effectivePathSegments: string[] = []; let childNoteId: string | null = null; let i = 0; - while (true) { - if (i >= path.length) { - break; - } - - const parentNoteId = path[i++]; + for (let i = 0; i < path.length; i++) { + const parentNoteId = path[i]; if (childNoteId !== null) { const child = await froca.getNote(childNoteId, !logErrors); @@ -65,7 +56,7 @@ async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root return null; } - if (!parents.some((p) => p.noteId === parentNoteId)) { + if (!parents.some(p => p.noteId === parentNoteId) || (i === path.length - 1 && parentNoteId !== 'root')) { if (logErrors) { const parent = froca.getNoteFromCache(parentNoteId); @@ -77,7 +68,8 @@ async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root ); } - const bestNotePath = child.getBestNotePath(hoistedNoteId); + const activeNotePath = appContext.tabManager.getActiveContextNotePath(); + const bestNotePath = child.getBestNotePath(hoistedNoteId, activeNotePath); if (bestNotePath) { const pathToRoot = bestNotePath.reverse().slice(1); @@ -108,7 +100,9 @@ async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root if (!note) { throw new Error(`Unable to find note: ${notePath}.`); } - const bestNotePath = note.getBestNotePath(hoistedNoteId); + + const activeNotePath = appContext.tabManager.getActiveContextNotePath(); + const bestNotePath = note.getBestNotePath(hoistedNoteId, activeNotePath); if (!bestNotePath) { throw new Error(`Did not find any path segments for '${note.toString()}', hoisted note '${hoistedNoteId}'`);