2020-05-17 09:48:24 +02:00
|
|
|
"use strict";
|
|
|
|
|
2022-12-09 16:13:22 +01:00
|
|
|
const becca = require('./becca');
|
2021-05-17 22:10:00 +02:00
|
|
|
const cls = require('../services/cls');
|
|
|
|
const log = require('../services/log');
|
2020-05-17 09:48:24 +02:00
|
|
|
|
2020-05-16 23:12:29 +02:00
|
|
|
function isNotePathArchived(notePath) {
|
|
|
|
const noteId = notePath[notePath.length - 1];
|
2021-04-16 23:00:08 +02:00
|
|
|
const note = becca.notes[noteId];
|
2020-05-16 23:12:29 +02:00
|
|
|
|
2021-05-18 20:56:49 +02:00
|
|
|
if (note.isArchived) {
|
2020-05-16 23:12:29 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < notePath.length - 1; i++) {
|
2021-04-16 23:00:08 +02:00
|
|
|
const note = becca.notes[notePath[i]];
|
2020-05-16 23:12:29 +02:00
|
|
|
|
|
|
|
// this is going through parents so archived must be inheritable
|
2023-01-27 08:46:04 +01:00
|
|
|
if (note.hasInheritableArchivedLabel()) {
|
2020-05-16 23:12:29 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This assumes that note is available. "archived" note means that there isn't a single non-archived note-path
|
|
|
|
* leading to this note.
|
|
|
|
*
|
|
|
|
* @param noteId
|
|
|
|
*/
|
|
|
|
function isArchived(noteId) {
|
|
|
|
const notePath = getSomePath(noteId);
|
|
|
|
|
|
|
|
return isNotePathArchived(notePath);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string} noteId
|
|
|
|
* @param {string} ancestorNoteId
|
2023-01-05 23:38:41 +01:00
|
|
|
* @returns {boolean} - true if given noteId has ancestorNoteId in any of its paths (even archived)
|
2020-05-16 23:12:29 +02:00
|
|
|
*/
|
|
|
|
function isInAncestor(noteId, ancestorNoteId) {
|
|
|
|
if (ancestorNoteId === 'root' || ancestorNoteId === noteId) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-04-16 23:00:08 +02:00
|
|
|
const note = becca.notes[noteId];
|
2020-05-16 23:12:29 +02:00
|
|
|
|
2020-10-05 21:31:57 +02:00
|
|
|
if (!note) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-05-16 23:12:29 +02:00
|
|
|
for (const parentNote of note.parents) {
|
|
|
|
if (isInAncestor(parentNote.noteId, ancestorNoteId)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getNoteTitle(childNoteId, parentNoteId) {
|
2021-04-16 23:00:08 +02:00
|
|
|
const childNote = becca.notes[childNoteId];
|
|
|
|
const parentNote = becca.notes[parentNoteId];
|
2020-05-16 23:12:29 +02:00
|
|
|
|
2020-08-17 23:54:18 +02:00
|
|
|
if (!childNote) {
|
2022-04-19 23:36:21 +02:00
|
|
|
log.info(`Cannot find note in cache for noteId '${childNoteId}'`);
|
2020-08-17 23:54:18 +02:00
|
|
|
return "[error fetching title]";
|
|
|
|
}
|
|
|
|
|
2022-01-08 21:50:16 +01:00
|
|
|
const title = childNote.getTitleOrProtected();
|
2020-05-16 23:12:29 +02:00
|
|
|
|
2021-04-26 22:18:14 +02:00
|
|
|
const branch = parentNote ? becca.getBranchFromChildAndParent(childNote.noteId, parentNote.noteId) : null;
|
2020-05-16 23:12:29 +02:00
|
|
|
|
2022-12-21 15:19:05 +01:00
|
|
|
return `${(branch && branch.prefix) ? `${branch.prefix} - ` : ''}${title}`;
|
2020-05-16 23:12:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function getNoteTitleArrayForPath(notePathArray) {
|
2021-09-07 22:37:03 +02:00
|
|
|
if (!notePathArray || !Array.isArray(notePathArray)) {
|
|
|
|
throw new Error(`${notePathArray} is not an array.`);
|
|
|
|
}
|
|
|
|
|
2023-01-17 21:15:05 +01:00
|
|
|
if (notePathArray.length === 1) {
|
|
|
|
return [getNoteTitle(notePathArray[0])];
|
2020-11-23 23:11:21 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 23:12:29 +02:00
|
|
|
const titles = [];
|
|
|
|
|
|
|
|
let parentNoteId = 'root';
|
|
|
|
let hoistedNotePassed = false;
|
|
|
|
|
2022-11-26 14:57:39 +01:00
|
|
|
// this is a notePath from outside of hoisted subtree so full title path needs to be returned
|
2023-01-17 21:15:05 +01:00
|
|
|
const hoistedNoteId = cls.getHoistedNoteId();
|
2022-11-26 14:57:39 +01:00
|
|
|
const outsideOfHoistedSubtree = !notePathArray.includes(hoistedNoteId);
|
|
|
|
|
2020-05-16 23:12:29 +02:00
|
|
|
for (const noteId of notePathArray) {
|
|
|
|
// start collecting path segment titles only after hoisted note
|
|
|
|
if (hoistedNotePassed) {
|
|
|
|
const title = getNoteTitle(noteId, parentNoteId);
|
|
|
|
|
|
|
|
titles.push(title);
|
|
|
|
}
|
|
|
|
|
2022-11-26 14:57:39 +01:00
|
|
|
if (!hoistedNotePassed && (noteId === hoistedNoteId || outsideOfHoistedSubtree)) {
|
2020-05-16 23:12:29 +02:00
|
|
|
hoistedNotePassed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
parentNoteId = noteId;
|
|
|
|
}
|
|
|
|
|
|
|
|
return titles;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getNoteTitleForPath(notePathArray) {
|
|
|
|
const titles = getNoteTitleArrayForPath(notePathArray);
|
|
|
|
|
|
|
|
return titles.join(' / ');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-04-15 00:06:13 +02:00
|
|
|
* Returns notePath for noteId. Note hoisting is respected.
|
2021-10-24 14:37:41 +02:00
|
|
|
* Archived (and hidden) notes are also returned, but non-archived paths are preferred if available
|
2020-05-16 23:12:29 +02:00
|
|
|
* - this means that archived paths is returned only if there's no non-archived path
|
2021-05-18 20:56:49 +02:00
|
|
|
* - you can check whether returned path is archived using isArchived
|
2023-02-28 23:23:17 +01:00
|
|
|
*
|
|
|
|
* @param {BNote} note
|
|
|
|
* @param {string[]} path
|
2020-05-16 23:12:29 +02:00
|
|
|
*/
|
2021-04-07 21:12:55 +02:00
|
|
|
function getSomePath(note, path = []) {
|
2021-04-06 21:56:46 +02:00
|
|
|
// first try to find note within hoisted note, otherwise take any existing note path
|
2023-02-28 23:23:17 +01:00
|
|
|
return getSomePathInner(note, path, true)
|
|
|
|
|| getSomePathInner(note, path, false);
|
2021-04-06 21:56:46 +02:00
|
|
|
}
|
|
|
|
|
2023-02-28 23:23:17 +01:00
|
|
|
/**
|
|
|
|
* @param {BNote} note
|
2023-04-15 00:06:13 +02:00
|
|
|
* @param {string[]} parentPath
|
|
|
|
* @param {boolean} respectHoisting
|
2023-02-28 23:23:17 +01:00
|
|
|
* @returns {string[]|false}
|
|
|
|
*/
|
2023-04-15 00:06:13 +02:00
|
|
|
function getSomePathInner(note, parentPath, respectHoisting) {
|
|
|
|
const childPath = [...parentPath, note.noteId];
|
2021-07-20 13:29:11 +02:00
|
|
|
if (note.isRoot()) {
|
2023-04-15 00:06:13 +02:00
|
|
|
childPath.reverse();
|
2020-05-16 23:12:29 +02:00
|
|
|
|
2023-04-15 00:06:13 +02:00
|
|
|
if (respectHoisting && !childPath.includes(cls.getHoistedNoteId())) {
|
2020-05-16 23:12:29 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-04-15 00:06:13 +02:00
|
|
|
return childPath;
|
2020-05-16 23:12:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const parents = note.parents;
|
|
|
|
if (parents.length === 0) {
|
2023-02-28 23:23:17 +01:00
|
|
|
console.log(`Note '${note.noteId}' - '${note.title}' has no parents.`);
|
2021-09-07 22:37:03 +02:00
|
|
|
|
2020-05-16 23:12:29 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-04-15 00:06:13 +02:00
|
|
|
const completeNotePaths = parents.map(parentNote => getSomePathInner(parentNote, childPath, respectHoisting));
|
2020-05-16 23:12:29 +02:00
|
|
|
|
2023-04-15 00:06:13 +02:00
|
|
|
if (completeNotePaths.length === 0) {
|
|
|
|
return false;
|
|
|
|
} else if (completeNotePaths.length === 1) {
|
|
|
|
return completeNotePaths[0];
|
|
|
|
} else {
|
|
|
|
completeNotePaths.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 if (a.isHidden !== b.isHidden) {
|
|
|
|
return a.isHidden ? 1 : -1;
|
|
|
|
} else {
|
|
|
|
return a.notePath.length - b.notePath.length;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// if there are multiple valid paths, take the shortest one
|
|
|
|
const shortestNotePath = completeNotePaths.reduce((shortestPath, nextPath) =>
|
|
|
|
nextPath.length < shortestPath.length
|
|
|
|
? nextPath
|
|
|
|
: shortestPath, completeNotePaths[0]);
|
|
|
|
|
|
|
|
return shortestNotePath;
|
2020-05-16 23:12:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getNotePath(noteId) {
|
2021-04-16 23:00:08 +02:00
|
|
|
const note = becca.notes[noteId];
|
2020-08-28 23:20:22 +02:00
|
|
|
|
|
|
|
if (!note) {
|
2022-04-19 23:36:21 +02:00
|
|
|
console.trace(`Cannot find note '${noteId}' in cache.`);
|
2020-08-28 23:20:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-05-16 23:12:29 +02:00
|
|
|
const retPath = getSomePath(note);
|
|
|
|
|
|
|
|
if (retPath) {
|
|
|
|
const noteTitle = getNoteTitleForPath(retPath);
|
2021-07-08 13:06:22 +02:00
|
|
|
|
|
|
|
let branchId;
|
|
|
|
|
2021-07-20 13:29:11 +02:00
|
|
|
if (note.isRoot()) {
|
2022-12-28 13:09:49 +01:00
|
|
|
branchId = 'none_root';
|
2021-07-08 13:06:22 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
const parentNote = note.parents[0];
|
2021-07-08 13:11:25 +02:00
|
|
|
branchId = becca.getBranchFromChildAndParent(noteId, parentNote.noteId).branchId;
|
2021-07-08 13:06:22 +02:00
|
|
|
}
|
2020-05-16 23:12:29 +02:00
|
|
|
|
|
|
|
return {
|
|
|
|
noteId: noteId,
|
2021-07-08 13:06:22 +02:00
|
|
|
branchId: branchId,
|
2020-05-16 23:12:29 +02:00
|
|
|
title: noteTitle,
|
|
|
|
notePath: retPath,
|
|
|
|
path: retPath.join('/')
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param noteId
|
|
|
|
* @returns {boolean} - true if note exists (is not deleted) and is available in current note hoisting
|
|
|
|
*/
|
|
|
|
function isAvailable(noteId) {
|
|
|
|
const notePath = getNotePath(noteId);
|
|
|
|
|
|
|
|
return !!notePath;
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
2020-05-17 10:11:19 +02:00
|
|
|
getSomePath,
|
2020-05-16 23:12:29 +02:00
|
|
|
getNotePath,
|
2020-05-17 10:11:19 +02:00
|
|
|
getNoteTitle,
|
2020-05-16 23:12:29 +02:00
|
|
|
getNoteTitleForPath,
|
|
|
|
isAvailable,
|
|
|
|
isArchived,
|
|
|
|
isInAncestor,
|
2020-09-15 16:46:03 +02:00
|
|
|
isNotePathArchived
|
2020-05-16 23:12:29 +02:00
|
|
|
};
|