2019-08-26 20:21:43 +02:00
|
|
|
import ws from './ws.js';
|
2018-03-25 11:09:17 -04:00
|
|
|
import utils from './utils.js';
|
2021-04-16 23:01:56 +02:00
|
|
|
import froca from './froca.js';
|
2018-12-12 20:39:56 +01:00
|
|
|
import hoistedNoteService from '../services/hoisted_note.js';
|
2022-12-01 13:07:23 +01:00
|
|
|
import appContext from "../components/app_context.js";
|
2020-01-12 09:12:13 +01:00
|
|
|
|
2019-05-11 21:27:27 +02:00
|
|
|
/**
|
2023-01-05 23:38:41 +01:00
|
|
|
* @returns {string|null}
|
2019-05-11 21:27:27 +02:00
|
|
|
*/
|
2021-03-03 21:49:57 +01:00
|
|
|
async function resolveNotePath(notePath, hoistedNoteId = 'root') {
|
|
|
|
const runPath = await resolveNotePathToSegments(notePath, hoistedNoteId);
|
2019-05-11 21:27:27 +02:00
|
|
|
|
2019-05-14 22:29:47 +02:00
|
|
|
return runPath ? runPath.join("/") : null;
|
2019-05-11 21:27:27 +02:00
|
|
|
}
|
|
|
|
|
2018-03-25 11:09:17 -04:00
|
|
|
/**
|
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.
|
2019-10-20 13:09:00 +02:00
|
|
|
*
|
2023-05-05 23:17:23 +02:00
|
|
|
* @returns {Promise<string[]>}
|
2018-03-25 11:09:17 -04:00
|
|
|
*/
|
2021-03-03 21:49:57 +01:00
|
|
|
async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logErrors = true) {
|
2018-03-25 11:09:17 -04:00
|
|
|
utils.assertArguments(notePath);
|
2017-12-23 11:02:38 -05:00
|
|
|
|
2023-04-11 21:41:55 +02:00
|
|
|
// we might get notePath with the params suffix, remove it if present
|
|
|
|
notePath = notePath.split("?")[0].trim();
|
2019-07-28 14:08:05 +02:00
|
|
|
|
|
|
|
if (notePath.length === 0) {
|
2023-06-29 23:32:19 +02:00
|
|
|
return null;
|
2019-07-28 14:08:05 +02:00
|
|
|
}
|
2019-07-09 22:12:05 +02:00
|
|
|
|
2018-03-25 11:09:17 -04:00
|
|
|
const path = notePath.split("/").reverse();
|
2018-05-26 16:16:34 -04:00
|
|
|
|
|
|
|
if (!path.includes("root")) {
|
|
|
|
path.push('root');
|
|
|
|
}
|
2017-11-19 08:47:22 -05:00
|
|
|
|
2021-03-09 20:37:56 +01:00
|
|
|
const effectivePathSegments = [];
|
2018-03-25 11:09:17 -04:00
|
|
|
let childNoteId = null;
|
|
|
|
let i = 0;
|
2017-11-19 18:16:50 -05:00
|
|
|
|
2018-03-25 11:09:17 -04:00
|
|
|
while (true) {
|
|
|
|
if (i >= path.length) {
|
|
|
|
break;
|
|
|
|
}
|
2017-11-30 00:02:32 -05:00
|
|
|
|
2018-03-25 11:09:17 -04:00
|
|
|
const parentNoteId = path[i++];
|
2017-11-19 11:28:46 -05:00
|
|
|
|
2018-03-25 11:09:17 -04:00
|
|
|
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();
|
2021-02-24 22:38:26 +01:00
|
|
|
|
2020-03-18 22:35:54 +01:00
|
|
|
const parents = child.getParentNotes();
|
2017-11-19 11:28:46 -05:00
|
|
|
|
2020-08-24 23:33:27 +02:00
|
|
|
if (!parents.length) {
|
|
|
|
if (logErrors) {
|
2022-02-08 23:38:54 +01:00
|
|
|
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;
|
2018-03-25 11:09:17 -04:00
|
|
|
}
|
2017-11-26 21:00:42 -05:00
|
|
|
|
2018-03-25 11:09:17 -04:00
|
|
|
if (!parents.some(p => p.noteId === parentNoteId)) {
|
2020-06-10 23:43:59 +02:00
|
|
|
if (logErrors) {
|
2021-04-16 22:57:37 +02:00
|
|
|
const parent = froca.getNoteFromCache(parentNoteId);
|
2020-10-12 21:05:34 +02:00
|
|
|
|
2021-12-21 16:12:59 +01: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.`);
|
2020-06-10 23:43:59 +02:00
|
|
|
}
|
2017-11-19 11:28:46 -05:00
|
|
|
|
2023-04-15 00:06:13 +02:00
|
|
|
const bestNotePath = child.getBestNotePath(hoistedNoteId);
|
2017-11-19 18:16:50 -05:00
|
|
|
|
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) {
|
2021-03-09 20:37:56 +01:00
|
|
|
effectivePathSegments.push(noteId);
|
2017-11-19 11:28:46 -05:00
|
|
|
}
|
2018-03-25 11:09:17 -04:00
|
|
|
}
|
2020-06-10 23:43:59 +02:00
|
|
|
|
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
|
|
|
|
2021-03-09 20:37:56 +01:00
|
|
|
effectivePathSegments.push(parentNoteId);
|
2018-12-12 20:39:56 +01:00
|
|
|
childNoteId = parentNoteId;
|
2017-11-19 08:47:22 -05:00
|
|
|
}
|
|
|
|
|
2021-03-09 20:37:56 +01:00
|
|
|
effectivePathSegments.reverse();
|
|
|
|
|
|
|
|
if (effectivePathSegments.includes(hoistedNoteId)) {
|
|
|
|
return effectivePathSegments;
|
|
|
|
}
|
|
|
|
else {
|
2023-05-29 22:37:19 +02:00
|
|
|
const note = await froca.getNote(getNoteIdFromUrl(notePath));
|
2021-03-09 20:37:56 +01:00
|
|
|
|
2023-04-15 00:06:13 +02:00
|
|
|
const bestNotePath = note.getBestNotePath(hoistedNoteId);
|
2021-03-09 20:37:56 +01:00
|
|
|
|
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;
|
2021-03-09 20:37:56 +01:00
|
|
|
}
|
2018-03-25 11:09:17 -04:00
|
|
|
}
|
2017-12-23 11:02:38 -05:00
|
|
|
|
2019-08-26 20:21:43 +02:00
|
|
|
ws.subscribeToMessages(message => {
|
2021-04-24 11:39:44 +02:00
|
|
|
if (message.type === 'openNote') {
|
2020-02-07 21:08:55 +01:00
|
|
|
appContext.tabManager.activateOrOpenNote(message.noteId);
|
2019-06-23 13:25:00 +02:00
|
|
|
|
|
|
|
if (utils.isElectron()) {
|
2021-11-16 22:43:08 +01:00
|
|
|
const currentWindow = utils.dynamicRequire('@electron/remote').getCurrentWindow();
|
2019-06-23 13:25:00 +02:00
|
|
|
|
|
|
|
currentWindow.show();
|
|
|
|
}
|
|
|
|
}
|
2018-08-01 09:26:02 +02:00
|
|
|
});
|
|
|
|
|
2020-02-10 20:57:56 +01:00
|
|
|
function getParentProtectedStatus(node) {
|
2023-05-05 23:17:23 +02:00
|
|
|
return hoistedNoteService.isHoistedNode(node) ? false : node.getParent().data.isProtected;
|
2020-01-25 09:56:08 +01:00
|
|
|
}
|
|
|
|
|
2023-05-29 22:37:19 +02:00
|
|
|
function getNoteIdFromUrl(url) {
|
|
|
|
if (!url) {
|
2020-01-25 09:56:08 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-05-29 22:37:19 +02:00
|
|
|
const [notePath] = url.split("?");
|
|
|
|
const segments = notePath.split("/");
|
2020-01-25 09:56:08 +01:00
|
|
|
|
2023-05-29 22:37:19 +02:00
|
|
|
return segments[segments.length - 1];
|
2020-01-25 09:56:08 +01:00
|
|
|
}
|
|
|
|
|
2023-05-29 22:37:19 +02:00
|
|
|
async function getBranchIdFromUrl(url) {
|
|
|
|
const {noteId, parentNoteId} = getNoteIdAndParentIdFromUrl(url);
|
2020-09-13 21:12:22 +02:00
|
|
|
|
2021-04-16 22:57:37 +02:00
|
|
|
return await froca.getBranchId(parentNoteId, noteId);
|
2020-09-13 21:12:22 +02:00
|
|
|
}
|
|
|
|
|
2023-05-29 22:37:19 +02:00
|
|
|
function getNoteIdAndParentIdFromUrl(url) {
|
|
|
|
if (!url) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
const [notePath] = url.split("?");
|
|
|
|
|
2020-05-17 21:07:54 +02:00
|
|
|
if (notePath === 'root') {
|
|
|
|
return {
|
|
|
|
noteId: 'root',
|
|
|
|
parentNoteId: 'none'
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-01-25 09:56:08 +01:00
|
|
|
let parentNoteId = 'root';
|
|
|
|
let noteId = '';
|
|
|
|
|
|
|
|
if (notePath) {
|
2023-05-29 22:37:19 +02:00
|
|
|
const segments = notePath.split("/");
|
2020-01-25 09:56:08 +01:00
|
|
|
|
2023-05-29 22:37:19 +02:00
|
|
|
noteId = segments[segments.length - 1];
|
2020-01-25 09:56:08 +01:00
|
|
|
|
2023-05-29 22:37:19 +02:00
|
|
|
if (segments.length > 1) {
|
|
|
|
parentNoteId = segments[segments.length - 2];
|
2020-01-25 09:56:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
parentNoteId,
|
|
|
|
noteId
|
2020-09-13 21:12:22 +02:00
|
|
|
};
|
2020-01-25 09:56:08 +01:00
|
|
|
}
|
|
|
|
|
2020-02-10 20:57:56 +01:00
|
|
|
function getNotePath(node) {
|
2020-01-25 09:56:08 +01:00
|
|
|
if (!node) {
|
2020-10-12 21:05:34 +02:00
|
|
|
logError("Node is null");
|
2020-01-25 09:56:08 +01:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
const path = [];
|
|
|
|
|
2021-03-06 20:23:29 +01:00
|
|
|
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("/");
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getNoteTitle(noteId, parentNoteId = null) {
|
|
|
|
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]";
|
|
|
|
}
|
|
|
|
|
|
|
|
let {title} = note;
|
|
|
|
|
|
|
|
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
|
|
|
|
2023-10-29 01:02:55 +02: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;
|
|
|
|
}
|
|
|
|
|
2021-01-21 21:48:06 +01:00
|
|
|
async function getNotePathTitleComponents(notePath) {
|
|
|
|
const titleComponents = [];
|
2020-01-25 09:56:08 +01:00
|
|
|
|
|
|
|
if (notePath.startsWith('root/')) {
|
|
|
|
notePath = notePath.substr(5);
|
|
|
|
}
|
|
|
|
|
|
|
|
// special case when we want just root's title
|
|
|
|
if (notePath === 'root') {
|
2021-01-21 21:48:06 +01:00
|
|
|
titleComponents.push(await getNoteTitle(notePath));
|
|
|
|
} else {
|
|
|
|
let parentNoteId = 'root';
|
|
|
|
|
|
|
|
for (const noteId of notePath.split('/')) {
|
|
|
|
titleComponents.push(await getNoteTitle(noteId, parentNoteId));
|
|
|
|
|
|
|
|
parentNoteId = noteId;
|
|
|
|
}
|
2020-01-25 09:56:08 +01:00
|
|
|
}
|
|
|
|
|
2021-01-21 21:48:06 +01:00
|
|
|
return titleComponents;
|
|
|
|
}
|
2020-01-25 09:56:08 +01:00
|
|
|
|
2021-01-21 21:48:06 +01:00
|
|
|
async function getNotePathTitle(notePath) {
|
|
|
|
utils.assertArguments(notePath);
|
2020-01-25 09:56:08 +01:00
|
|
|
|
2021-01-21 21:48:06 +01:00
|
|
|
const titlePath = await getNotePathTitleComponents(notePath);
|
2020-01-25 09:56:08 +01:00
|
|
|
|
|
|
|
return titlePath.join(' / ');
|
|
|
|
}
|
|
|
|
|
2021-01-21 21:48:06 +01:00
|
|
|
async function getNoteTitleWithPathAsSuffix(notePath) {
|
|
|
|
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);
|
|
|
|
|
|
|
|
const $titleWithPath = $('<span class="note-title-with-path">')
|
|
|
|
.append($('<span class="note-title">').text(title));
|
|
|
|
|
|
|
|
if (path.length > 0) {
|
|
|
|
$titleWithPath
|
2022-12-21 15:19:05 +01:00
|
|
|
.append($('<span class="note-path">').text(` (${path.join(' / ')})`));
|
2021-01-21 21:48:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $titleWithPath;
|
|
|
|
}
|
|
|
|
|
2022-10-16 23:11:46 +02:00
|
|
|
function isNotePathInHiddenSubtree(notePath) {
|
2022-12-21 16:11:00 +01:00
|
|
|
return notePath?.includes("root/_hidden");
|
2022-10-16 23:11:46 +02:00
|
|
|
}
|
|
|
|
|
2018-03-25 11:09:17 -04:00
|
|
|
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,
|
2023-05-29 22:37:19 +02:00
|
|
|
getNoteIdFromUrl,
|
|
|
|
getNoteIdAndParentIdFromUrl,
|
|
|
|
getBranchIdFromUrl,
|
2020-01-25 09:56:08 +01:00
|
|
|
getNoteTitle,
|
2020-02-09 21:13:05 +01:00
|
|
|
getNotePathTitle,
|
2021-01-21 21:48:06 +01:00
|
|
|
getNoteTitleWithPathAsSuffix,
|
2022-10-16 23:11:46 +02:00
|
|
|
isNotePathInHiddenSubtree
|
2020-05-09 14:25:27 +02:00
|
|
|
};
|