333 lines
8.7 KiB
JavaScript

import ws from './ws.js';
import utils from './utils.js';
import server from './server.js';
import froca from './froca.js';
import hoistedNoteService from '../services/hoisted_note.js';
import appContext from "./app_context.js";
/**
* @return {string|null}
*/
async function resolveNotePath(notePath, hoistedNoteId = 'root') {
const runPath = await resolveNotePathToSegments(notePath, hoistedNoteId);
return runPath ? runPath.join("/") : null;
}
/**
* 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
* path change) or other corruption, in that case this will try to get some other valid path to the correct note.
*
* @return {string[]}
*/
async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logErrors = true) {
utils.assertArguments(notePath);
// we might get notePath with the ntxId suffix, remove it if present
notePath = notePath.split("-")[0].trim();
if (notePath.length === 0) {
return;
}
const path = notePath.split("/").reverse();
if (!path.includes("root")) {
path.push('root');
}
const effectivePathSegments = [];
let childNoteId = null;
let i = 0;
while (true) {
if (i >= path.length) {
break;
}
const parentNoteId = path[i++];
if (childNoteId !== null) {
const child = await froca.getNote(childNoteId, !logErrors);
if (!child) {
if (logErrors) {
ws.logError(`Can't find note ${childNoteId}`);
}
return;
}
child.resortParents();
const parents = child.getParentNotes();
if (!parents.length) {
if (logErrors) {
ws.logError(`No parents found for note ${childNoteId} (${child.title}) for path ${notePath}`);
}
return;
}
if (!parents.some(p => p.noteId === parentNoteId)) {
if (logErrors) {
const parent = froca.getNoteFromCache(parentNoteId);
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.`);
}
const someNotePath = getSomeNotePath(child, hoistedNoteId);
if (someNotePath) { // in case it's root the path may be empty
const pathToRoot = someNotePath.split("/").reverse().slice(1);
if (!pathToRoot.includes("root")) {
pathToRoot.push('root');
}
for (const noteId of pathToRoot) {
effectivePathSegments.push(noteId);
}
}
break;
}
}
effectivePathSegments.push(parentNoteId);
childNoteId = parentNoteId;
}
effectivePathSegments.reverse();
if (effectivePathSegments.includes(hoistedNoteId)) {
return effectivePathSegments;
}
else {
const note = await froca.getNote(getNoteIdFromNotePath(notePath));
const someNotePathSegments = getSomeNotePathSegments(note, hoistedNoteId);
if (!someNotePathSegments) {
throw new Error(`Did not find any path segments for ${note.toString()}, hoisted note ${hoistedNoteId}`);
}
// if there isn't actually any note path with hoisted note then return the original resolved note path
return someNotePathSegments.includes(hoistedNoteId) ? someNotePathSegments : effectivePathSegments;
}
}
function getSomeNotePathSegments(note, hoistedNotePath = 'root') {
utils.assertArguments(note);
const notePaths = note.getSortedNotePaths(hoistedNotePath);
return notePaths.length > 0 ? notePaths[0].notePath : null;
}
function getSomeNotePath(note, hoistedNotePath = 'root') {
const notePath = getSomeNotePathSegments(note, hoistedNotePath);
return notePath === null ? null : notePath.join('/');
}
ws.subscribeToMessages(message => {
if (message.type === 'openNote') {
appContext.tabManager.activateOrOpenNote(message.noteId);
if (utils.isElectron()) {
const currentWindow = utils.dynamicRequire('@electron/remote').getCurrentWindow();
currentWindow.show();
}
}
});
function getParentProtectedStatus(node) {
return hoistedNoteService.isHoistedNode(node) ? 0 : node.getParent().data.isProtected;
}
function getNoteIdFromNotePath(notePath) {
if (!notePath) {
return null;
}
const path = notePath.split("/");
const lastSegment = path[path.length - 1];
// path could have also ntxId suffix
return lastSegment.split("-")[0];
}
async function getBranchIdFromNotePath(notePath) {
const {noteId, parentNoteId} = getNoteIdAndParentIdFromNotePath(notePath);
return await froca.getBranchId(parentNoteId, noteId);
}
function getNoteIdAndParentIdFromNotePath(notePath) {
if (notePath === 'root') {
return {
noteId: 'root',
parentNoteId: 'none'
};
}
let parentNoteId = 'root';
let noteId = '';
if (notePath) {
const path = notePath.split("/");
const lastSegment = path[path.length - 1];
// path could have also ntxId suffix
noteId = lastSegment.split("-")[0];
if (path.length > 1) {
parentNoteId = path[path.length - 2];
}
}
return {
parentNoteId,
noteId
};
}
function getNotePath(node) {
if (!node) {
logError("Node is null");
return "";
}
const path = [];
while (node) {
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);
const note = await froca.getNote(noteId);
if (!note) {
return "[not found]";
}
let {title} = note;
if (parentNoteId !== null) {
const branchId = note.parentToBranch[parentNoteId];
if (branchId) {
const branch = froca.getBranch(branchId);
if (branch && branch.prefix) {
title = `${branch.prefix} - ${title}`;
}
}
}
return title;
}
async function getNotePathTitleComponents(notePath) {
const titleComponents = [];
if (notePath.startsWith('root/')) {
notePath = notePath.substr(5);
}
// special case when we want just root's title
if (notePath === 'root') {
titleComponents.push(await getNoteTitle(notePath));
} else {
let parentNoteId = 'root';
for (const noteId of notePath.split('/')) {
titleComponents.push(await getNoteTitle(noteId, parentNoteId));
parentNoteId = noteId;
}
}
return titleComponents;
}
async function getNotePathTitle(notePath) {
utils.assertArguments(notePath);
const titlePath = await getNotePathTitleComponents(notePath);
return titlePath.join(' / ');
}
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
.append($('<span class="note-path">').text(' (' + path.join(' / ') + ')'));
}
return $titleWithPath;
}
function getHashValueFromAddress() {
const str = document.location.hash ? document.location.hash.substr(1) : ""; // strip initial #
return str.split("-");
}
function parseNotePath(notePath) {
let noteIds = notePath.split('/');
if (noteIds[0] !== 'root') {
noteIds = ['root'].concat(noteIds);
}
return noteIds;
}
export default {
resolveNotePath,
resolveNotePathToSegments,
getSomeNotePath,
getSomeNotePathSegments,
getParentProtectedStatus,
getNotePath,
getNoteIdFromNotePath,
getNoteIdAndParentIdFromNotePath,
getBranchIdFromNotePath,
getNoteTitle,
getNotePathTitle,
getNoteTitleWithPathAsSuffix,
getHashValueFromAddress,
parseNotePath
};