327 lines
8.8 KiB
TypeScript
Raw Normal View History

2025-01-09 18:07:02 +02:00
import ws from "./ws.js";
import utils from "./utils.js";
import froca from "./froca.js";
import hoistedNoteService from "../services/hoisted_note.js";
2022-12-01 13:07:23 +01:00
import appContext from "../components/app_context.js";
/**
2023-01-05 23:38:41 +01:00
* @returns {string|null}
*/
2025-01-09 18:07:02 +02:00
async function resolveNotePath(notePath: string, hoistedNoteId = "root") {
const runPath = await resolveNotePathToSegments(notePath, hoistedNoteId);
2019-05-14 22:29:47 +02:00
return runPath ? runPath.join("/") : null;
}
/**
2020-08-24 23:33:27 +02:00
* Accepts notePath which might or might not be valid and returns an existing path as close to the original
* notePath as possible. Part of the path might not be valid because of note moving (which causes
2023-06-29 23:32:19 +02:00
* path change) or other corruption, in that case, this will try to get some other valid path to the correct note.
*/
2025-01-09 18:07:02 +02:00
async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root", logErrors = true) {
utils.assertArguments(notePath);
// we might get notePath with the params suffix, remove it if present
notePath = notePath.split("?")[0].trim();
if (notePath.length === 0) {
2023-06-29 23:32:19 +02:00
return null;
}
const path = notePath.split("/").reverse();
2018-05-26 16:16:34 -04:00
if (!path.includes("root")) {
2025-01-09 18:07:02 +02:00
path.push("root");
2018-05-26 16:16:34 -04:00
}
2017-11-19 08:47:22 -05:00
const effectivePathSegments = [];
let childNoteId = null;
let i = 0;
while (true) {
if (i >= path.length) {
break;
}
const parentNoteId = path[i++];
if (childNoteId !== null) {
2021-05-11 22:00:16 +02:00
const child = await froca.getNote(childNoteId, !logErrors);
2018-11-12 23:34:22 +01:00
if (!child) {
2021-05-11 22:00:16 +02:00
if (logErrors) {
ws.logError(`Can't find note ${childNoteId}`);
}
2023-06-29 23:32:19 +02:00
return null;
2018-11-12 23:34:22 +01:00
}
2023-02-28 23:23:17 +01:00
child.sortParents();
const parents = child.getParentNotes();
2020-08-24 23:33:27 +02:00
if (!parents.length) {
if (logErrors) {
ws.logError(`No parents found for note ${childNoteId} (${child.title}) for path ${notePath}`);
2020-08-24 23:33:27 +02:00
}
2023-06-29 23:32:19 +02:00
return null;
}
2017-11-26 21:00:42 -05:00
2025-01-09 18:07:02 +02:00
if (!parents.some((p) => p.noteId === parentNoteId)) {
if (logErrors) {
2021-04-16 22:57:37 +02:00
const parent = froca.getNoteFromCache(parentNoteId);
2025-01-09 18:07:02 +02:00
console.debug(
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})`)}.
You can ignore this message as it is mostly harmless.`
);
}
2023-04-15 00:06:13 +02:00
const bestNotePath = child.getBestNotePath(hoistedNoteId);
2023-04-15 00:06:13 +02:00
if (bestNotePath) {
const pathToRoot = bestNotePath.reverse().slice(1);
2021-12-21 16:12:59 +01:00
2020-08-24 23:33:27 +02:00
for (const noteId of pathToRoot) {
effectivePathSegments.push(noteId);
}
}
2020-08-24 23:33:27 +02:00
break;
2017-11-19 08:47:22 -05:00
}
}
2017-12-03 17:46:56 -05:00
effectivePathSegments.push(parentNoteId);
2018-12-12 20:39:56 +01:00
childNoteId = parentNoteId;
2017-11-19 08:47:22 -05:00
}
effectivePathSegments.reverse();
if (effectivePathSegments.includes(hoistedNoteId)) {
return effectivePathSegments;
2025-01-09 18:07:02 +02:00
} else {
2024-08-04 13:32:29 +03:00
const noteId = getNoteIdFromUrl(notePath);
if (!noteId) {
throw new Error(`Unable to find note with ID: ${noteId}.`);
}
const note = await froca.getNote(noteId);
if (!note) {
throw new Error(`Unable to find note: ${notePath}.`);
}
2023-04-15 00:06:13 +02:00
const bestNotePath = note.getBestNotePath(hoistedNoteId);
2023-04-15 00:06:13 +02:00
if (!bestNotePath) {
throw new Error(`Did not find any path segments for '${note.toString()}', hoisted note '${hoistedNoteId}'`);
2021-10-29 21:37:12 +02:00
}
2023-06-30 11:18:34 +02:00
// if there isn't actually any note path with hoisted note, then return the original resolved note path
2023-04-15 00:06:13 +02:00
return bestNotePath.includes(hoistedNoteId) ? bestNotePath : effectivePathSegments;
}
}
2025-01-09 18:07:02 +02:00
ws.subscribeToMessages((message) => {
if (message.type === "openNote") {
appContext.tabManager.activateOrOpenNote(message.noteId);
2019-06-23 13:25:00 +02:00
2025-01-09 18:07:02 +02:00
if (utils.isElectron()) {
const currentWindow = utils.dynamicRequire("@electron/remote").getCurrentWindow();
2019-06-23 13:25:00 +02:00
2025-01-09 18:07:02 +02:00
currentWindow.show();
}
}
});
2025-01-28 15:44:15 +02:00
function getParentProtectedStatus(node: Fancytree.FancytreeNode) {
return hoistedNoteService.isHoistedNode(node) ? false : node.getParent().data.isProtected;
2020-01-25 09:56:08 +01:00
}
function getNoteIdFromUrl(urlOrNotePath: string | undefined) {
if (!urlOrNotePath) {
2020-01-25 09:56:08 +01:00
return null;
}
const [notePath] = urlOrNotePath.split("?");
const segments = notePath.split("/");
2020-01-25 09:56:08 +01:00
return segments[segments.length - 1];
2020-01-25 09:56:08 +01:00
}
2024-08-04 13:32:29 +03:00
async function getBranchIdFromUrl(urlOrNotePath: string) {
2025-01-09 18:07:02 +02:00
const { noteId, parentNoteId } = getNoteIdAndParentIdFromUrl(urlOrNotePath);
2024-08-04 13:32:29 +03:00
if (!parentNoteId) {
return null;
}
2021-04-16 22:57:37 +02:00
return await froca.getBranchId(parentNoteId, noteId);
}
2024-08-04 13:32:29 +03:00
function getNoteIdAndParentIdFromUrl(urlOrNotePath: string) {
if (!urlOrNotePath) {
return {};
}
const [notePath] = urlOrNotePath.split("?");
2025-01-09 18:07:02 +02:00
if (notePath === "root") {
2020-05-17 21:07:54 +02:00
return {
2025-01-09 18:07:02 +02:00
noteId: "root",
parentNoteId: "none"
2020-05-17 21:07:54 +02:00
};
}
2025-01-09 18:07:02 +02:00
let parentNoteId = "root";
let noteId = "";
2020-01-25 09:56:08 +01:00
if (notePath) {
const segments = notePath.split("/");
2020-01-25 09:56:08 +01:00
noteId = segments[segments.length - 1];
2020-01-25 09:56:08 +01:00
if (segments.length > 1) {
parentNoteId = segments[segments.length - 2];
2020-01-25 09:56:08 +01:00
}
}
return {
parentNoteId,
noteId
};
2020-01-25 09:56:08 +01:00
}
2025-01-28 15:44:15 +02:00
function getNotePath(node: Fancytree.FancytreeNode) {
2020-01-25 09:56:08 +01:00
if (!node) {
logError("Node is null");
2020-01-25 09:56:08 +01:00
return "";
}
const path = [];
while (node) {
2020-01-25 09:56:08 +01:00
if (node.data.noteId) {
path.push(node.data.noteId);
}
node = node.getParent();
}
return path.reverse().join("/");
}
2024-08-04 13:32:29 +03:00
async function getNoteTitle(noteId: string, parentNoteId: string | null = null) {
2020-01-25 09:56:08 +01:00
utils.assertArguments(noteId);
2021-04-16 22:57:37 +02:00
const note = await froca.getNote(noteId);
2020-01-25 09:56:08 +01:00
if (!note) {
return "[not found]";
}
2025-01-09 18:07:02 +02:00
let { title } = note;
2020-01-25 09:56:08 +01:00
if (parentNoteId !== null) {
const branchId = note.parentToBranch[parentNoteId];
if (branchId) {
2021-04-16 22:57:37 +02:00
const branch = froca.getBranch(branchId);
2020-01-25 09:56:08 +01:00
if (branch?.prefix) {
2020-08-24 23:33:27 +02:00
title = `${branch.prefix} - ${title}`;
2020-01-25 09:56:08 +01:00
}
}
}
return title;
}
2024-08-04 13:32:29 +03:00
async function getNotePathTitleComponents(notePath: string) {
const titleComponents = [];
2020-01-25 09:56:08 +01:00
2025-01-09 18:07:02 +02:00
if (notePath.startsWith("root/")) {
2020-01-25 09:56:08 +01:00
notePath = notePath.substr(5);
}
// special case when we want just root's title
2025-01-09 18:07:02 +02:00
if (notePath === "root") {
titleComponents.push(await getNoteTitle(notePath));
} else {
2025-01-09 18:07:02 +02:00
let parentNoteId = "root";
2025-01-09 18:07:02 +02:00
for (const noteId of notePath.split("/")) {
titleComponents.push(await getNoteTitle(noteId, parentNoteId));
parentNoteId = noteId;
}
2020-01-25 09:56:08 +01:00
}
return titleComponents;
}
2020-01-25 09:56:08 +01:00
2024-08-04 13:32:29 +03:00
async function getNotePathTitle(notePath: string) {
utils.assertArguments(notePath);
2020-01-25 09:56:08 +01:00
const titlePath = await getNotePathTitleComponents(notePath);
2020-01-25 09:56:08 +01:00
2025-01-09 18:07:02 +02:00
return titlePath.join(" / ");
2020-01-25 09:56:08 +01:00
}
2024-08-04 13:32:29 +03:00
async function getNoteTitleWithPathAsSuffix(notePath: string) {
utils.assertArguments(notePath);
const titleComponents = await getNotePathTitleComponents(notePath);
if (!titleComponents || titleComponents.length === 0) {
return "";
}
const title = titleComponents[titleComponents.length - 1];
const path = titleComponents.slice(0, titleComponents.length - 1);
2025-01-09 18:07:02 +02:00
const $titleWithPath = $('<span class="note-title-with-path">').append($('<span class="note-title">').text(title));
2024-12-10 21:43:23 +02:00
2024-12-10 21:49:42 +02:00
$titleWithPath.append(formatNotePath(path));
2024-12-10 21:43:23 +02:00
return $titleWithPath;
}
function formatNotePath(path: string[]) {
2024-12-10 21:43:23 +02:00
const $notePath = $('<span class="note-path">');
if (path.length > 0) {
$notePath.append($(`<span class="path-bracket"> (</span>)`));
for (let segmentIndex = 0; segmentIndex < path.length; segmentIndex++) {
$notePath.append($(`<span>`).text(path[segmentIndex]));
if (segmentIndex < path.length - 1) {
2024-12-05 02:09:04 +02:00
$notePath.append($(`<span class="path-delimiter">`).text(" / "));
}
}
2024-12-05 02:09:04 +02:00
$notePath.append($(`<span class="path-bracket">)</span>)`));
}
2024-12-10 21:43:23 +02:00
return $notePath;
}
2024-08-04 13:32:29 +03:00
function isNotePathInHiddenSubtree(notePath: string) {
2022-12-21 16:11:00 +01:00
return notePath?.includes("root/_hidden");
}
export default {
2019-05-19 18:21:29 +02:00
resolveNotePath,
2020-08-24 23:33:27 +02:00
resolveNotePathToSegments,
2020-01-25 09:56:08 +01:00
getParentProtectedStatus,
getNotePath,
getNotePathTitleComponents,
getNoteIdFromUrl,
getNoteIdAndParentIdFromUrl,
getBranchIdFromUrl,
2020-01-25 09:56:08 +01:00
getNoteTitle,
2020-02-09 21:13:05 +01:00
getNotePathTitle,
getNoteTitleWithPathAsSuffix,
2024-12-10 21:49:42 +02:00
isNotePathInHiddenSubtree,
formatNotePath
};