mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
refactoring of "some path" WIP
This commit is contained in:
parent
5e88e24693
commit
7aa26580ba
@ -120,7 +120,7 @@ function getNoteTitleForPath(notePathArray) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns notePath for noteId from cache. Note hoisting is respected.
|
* Returns notePath for noteId. Note hoisting is respected.
|
||||||
* Archived (and hidden) notes are also returned, but non-archived paths are preferred if available
|
* Archived (and hidden) notes are also returned, but non-archived paths are preferred if available
|
||||||
* - this means that archived paths is returned only if there's no non-archived path
|
* - this means that archived paths is returned only if there's no non-archived path
|
||||||
* - you can check whether returned path is archived using isArchived
|
* - you can check whether returned path is archived using isArchived
|
||||||
@ -136,20 +136,20 @@ function getSomePath(note, path = []) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {BNote} note
|
* @param {BNote} note
|
||||||
* @param {string[]} path
|
* @param {string[]} parentPath
|
||||||
* @param {boolean} respectHoisting
|
* @param {boolean} respectHoisting
|
||||||
* @returns {string[]|false}
|
* @returns {string[]|false}
|
||||||
*/
|
*/
|
||||||
function getSomePathInner(note, path, respectHoisting) {
|
function getSomePathInner(note, parentPath, respectHoisting) {
|
||||||
|
const childPath = [...parentPath, note.noteId];
|
||||||
if (note.isRoot()) {
|
if (note.isRoot()) {
|
||||||
const foundPath = [...path, note.noteId];
|
childPath.reverse();
|
||||||
foundPath.reverse();
|
|
||||||
|
|
||||||
if (respectHoisting && !foundPath.includes(cls.getHoistedNoteId())) {
|
if (respectHoisting && !childPath.includes(cls.getHoistedNoteId())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return foundPath;
|
return childPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parents = note.parents;
|
const parents = note.parents;
|
||||||
@ -159,15 +159,35 @@ function getSomePathInner(note, path, respectHoisting) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const parentNote of parents) {
|
const completeNotePaths = parents.map(parentNote => getSomePathInner(parentNote, childPath, respectHoisting));
|
||||||
const retPath = getSomePathInner(parentNote, [...path, note.noteId], respectHoisting);
|
|
||||||
|
|
||||||
if (retPath) {
|
|
||||||
return retPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (completeNotePaths.length === 0) {
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNotePath(noteId) {
|
function getNotePath(noteId) {
|
||||||
|
@ -12,6 +12,7 @@ const TaskContext = require("../../services/task_context");
|
|||||||
const dayjs = require("dayjs");
|
const dayjs = require("dayjs");
|
||||||
const utc = require('dayjs/plugin/utc');
|
const utc = require('dayjs/plugin/utc');
|
||||||
const eventService = require("../../services/events");
|
const eventService = require("../../services/events");
|
||||||
|
const froca = require("../../public/app/services/froca.js");
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
const LABEL = 'label';
|
const LABEL = 'label';
|
||||||
@ -1150,6 +1151,8 @@ class BNote extends AbstractBeccaEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Gives all possible note paths leading to this note. Paths containing search note are ignored (could form cycles)
|
||||||
|
*
|
||||||
* @returns {string[][]} - array of notePaths (each represented by array of noteIds constituting the particular note path)
|
* @returns {string[][]} - array of notePaths (each represented by array of noteIds constituting the particular note path)
|
||||||
*/
|
*/
|
||||||
getAllNotePaths() {
|
getAllNotePaths() {
|
||||||
@ -1157,18 +1160,73 @@ class BNote extends AbstractBeccaEntity {
|
|||||||
return [['root']];
|
return [['root']];
|
||||||
}
|
}
|
||||||
|
|
||||||
const notePaths = [];
|
const parentNotes = this.getParentNotes();
|
||||||
|
let notePaths = [];
|
||||||
|
|
||||||
for (const parentNote of this.getParentNotes()) {
|
if (parentNotes.length === 1) { // optimization for most common case
|
||||||
for (const parentPath of parentNote.getAllNotePaths()) {
|
notePaths = parentNotes[0].getAllNotePaths();
|
||||||
parentPath.push(this.noteId);
|
} else {
|
||||||
notePaths.push(parentPath);
|
notePaths = parentNotes.flatMap(parentNote => parentNote.getAllNotePaths());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const notePath of notePaths) {
|
||||||
|
notePath.push(this.noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return notePaths;
|
return notePaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} [hoistedNoteId='root']
|
||||||
|
* @return {{isArchived: boolean, isInHoistedSubTree: boolean, notePath: string[], isHidden: boolean}[]}
|
||||||
|
*/
|
||||||
|
getSortedNotePathRecords(hoistedNoteId = 'root') {
|
||||||
|
const isHoistedRoot = hoistedNoteId === 'root';
|
||||||
|
|
||||||
|
const notePaths = this.getAllNotePaths().map(path => ({
|
||||||
|
notePath: path,
|
||||||
|
isInHoistedSubTree: isHoistedRoot || path.includes(hoistedNoteId),
|
||||||
|
isArchived: path.some(noteId => froca.notes[noteId].isArchived),
|
||||||
|
isHidden: path.includes('_hidden')
|
||||||
|
}));
|
||||||
|
|
||||||
|
notePaths.sort((a, b) => {
|
||||||
|
if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
|
||||||
|
return a.isInHoistedSubTree ? -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return notePaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns note path considered to be the "best"
|
||||||
|
*
|
||||||
|
* @param {string} [hoistedNoteId='root']
|
||||||
|
* @return {string[]} array of noteIds constituting the particular note path
|
||||||
|
*/
|
||||||
|
getBestNotePath(hoistedNoteId = 'root') {
|
||||||
|
return this.getSortedNotePathRecords(hoistedNoteId)[0]?.notePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns note path considered to be the "best"
|
||||||
|
*
|
||||||
|
* @param {string} [hoistedNoteId='root']
|
||||||
|
* @return {string} serialized note path (e.g. 'root/a1h315/js725h')
|
||||||
|
*/
|
||||||
|
getBestNotePathString(hoistedNoteId = 'root') {
|
||||||
|
const notePath = this.getBestNotePath(hoistedNoteId);
|
||||||
|
|
||||||
|
return notePath?.join("/");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree
|
* @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree
|
||||||
*/
|
*/
|
||||||
|
@ -247,6 +247,11 @@ class FNote {
|
|||||||
return this.__filterAttrs(this.__getCachedAttributes([]), type, name);
|
return this.__filterAttrs(this.__getCachedAttributes([]), type, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string[]} path
|
||||||
|
* @return {FAttribute[]}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
__getCachedAttributes(path) {
|
__getCachedAttributes(path) {
|
||||||
// notes/clones cannot form tree cycles, it is possible to create attribute inheritance cycle via templates
|
// notes/clones cannot form tree cycles, it is possible to create attribute inheritance cycle via templates
|
||||||
// when template instance is a parent of template itself
|
// when template instance is a parent of template itself
|
||||||
@ -299,63 +304,49 @@ class FNote {
|
|||||||
return this.noteId === 'root';
|
return this.noteId === 'root';
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllNotePaths(encounteredNoteIds = null) {
|
/**
|
||||||
|
* Gives all possible note paths leading to this note. Paths containing search note are ignored (could form cycles)
|
||||||
|
*
|
||||||
|
* @returns {string[][]} - array of notePaths (each represented by array of noteIds constituting the particular note path)
|
||||||
|
*/
|
||||||
|
getAllNotePaths() {
|
||||||
if (this.noteId === 'root') {
|
if (this.noteId === 'root') {
|
||||||
return [['root']];
|
return [['root']];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!encounteredNoteIds) {
|
|
||||||
encounteredNoteIds = new Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
encounteredNoteIds.add(this.noteId);
|
|
||||||
|
|
||||||
const parentNotes = this.getParentNotes();
|
const parentNotes = this.getParentNotes();
|
||||||
let paths;
|
let notePaths = [];
|
||||||
|
|
||||||
if (parentNotes.length === 1) { // optimization for the most common case
|
if (parentNotes.length === 1) { // optimization for most common case
|
||||||
if (encounteredNoteIds.has(parentNotes[0].noteId)) {
|
notePaths = parentNotes[0].getAllNotePaths();
|
||||||
return [];
|
} else {
|
||||||
}
|
notePaths = parentNotes.flatMap(parentNote => parentNote.getAllNotePaths());
|
||||||
else {
|
|
||||||
paths = parentNotes[0].getAllNotePaths(encounteredNoteIds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
paths = [];
|
|
||||||
|
|
||||||
for (const parentNote of parentNotes) {
|
|
||||||
if (encounteredNoteIds.has(parentNote.noteId)) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const newSet = new Set(encounteredNoteIds);
|
for (const notePath of notePaths) {
|
||||||
|
notePath.push(this.noteId);
|
||||||
paths.push(...parentNote.getAllNotePaths(newSet));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const path of paths) {
|
return notePaths;
|
||||||
path.push(this.noteId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return paths;
|
/**
|
||||||
}
|
* @param {string} [hoistedNoteId='root']
|
||||||
|
* @return {{isArchived: boolean, isInHoistedSubTree: boolean, notePath: string[], isHidden: boolean}[]}
|
||||||
|
*/
|
||||||
|
getSortedNotePathRecords(hoistedNoteId = 'root') {
|
||||||
|
const isHoistedRoot = hoistedNoteId === 'root';
|
||||||
|
|
||||||
getSortedNotePaths(hoistedNotePath = 'root') {
|
|
||||||
const notePaths = this.getAllNotePaths().map(path => ({
|
const notePaths = this.getAllNotePaths().map(path => ({
|
||||||
notePath: path,
|
notePath: path,
|
||||||
isInHoistedSubTree: path.includes(hoistedNotePath),
|
isInHoistedSubTree: isHoistedRoot || path.includes(hoistedNoteId),
|
||||||
isArchived: path.find(noteId => froca.notes[noteId].isArchived),
|
isArchived: path.some(noteId => froca.notes[noteId].isArchived),
|
||||||
isSearch: path.find(noteId => froca.notes[noteId].type === 'search'),
|
|
||||||
isHidden: path.includes('_hidden')
|
isHidden: path.includes('_hidden')
|
||||||
}));
|
}));
|
||||||
|
|
||||||
notePaths.sort((a, b) => {
|
notePaths.sort((a, b) => {
|
||||||
if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
|
if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
|
||||||
return a.isInHoistedSubTree ? -1 : 1;
|
return a.isInHoistedSubTree ? -1 : 1;
|
||||||
} else if (a.isSearch !== b.isSearch) {
|
|
||||||
return a.isSearch ? 1 : -1;
|
|
||||||
} else if (a.isArchived !== b.isArchived) {
|
} else if (a.isArchived !== b.isArchived) {
|
||||||
return a.isArchived ? 1 : -1;
|
return a.isArchived ? 1 : -1;
|
||||||
} else if (a.isHidden !== b.isHidden) {
|
} else if (a.isHidden !== b.isHidden) {
|
||||||
@ -368,6 +359,28 @@ class FNote {
|
|||||||
return notePaths;
|
return notePaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns note path considered to be the "best"
|
||||||
|
*
|
||||||
|
* @param {string} [hoistedNoteId='root']
|
||||||
|
* @return {string[]} array of noteIds constituting the particular note path
|
||||||
|
*/
|
||||||
|
getBestNotePath(hoistedNoteId = 'root') {
|
||||||
|
return this.getSortedNotePathRecords(hoistedNoteId)[0]?.notePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns note path considered to be the "best"
|
||||||
|
*
|
||||||
|
* @param {string} [hoistedNoteId='root']
|
||||||
|
* @return {string} serialized note path (e.g. 'root/a1h315/js725h')
|
||||||
|
*/
|
||||||
|
getBestNotePathString(hoistedNoteId = 'root') {
|
||||||
|
const notePath = this.getBestNotePath(hoistedNoteId);
|
||||||
|
|
||||||
|
return notePath?.join("/");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree
|
* @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree
|
||||||
*/
|
*/
|
||||||
@ -391,6 +404,13 @@ class FNote {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {FAttribute[]} attributes
|
||||||
|
* @param {string} type
|
||||||
|
* @param {string} name
|
||||||
|
* @return {FAttribute[]}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
__filterAttrs(attributes, type, name) {
|
__filterAttrs(attributes, type, name) {
|
||||||
this.__validateTypeName(type, name);
|
this.__validateTypeName(type, name);
|
||||||
|
|
||||||
@ -527,7 +547,9 @@ class FNote {
|
|||||||
* @returns {boolean} true if note has an attribute with given type and name (including inherited)
|
* @returns {boolean} true if note has an attribute with given type and name (including inherited)
|
||||||
*/
|
*/
|
||||||
hasAttribute(type, name) {
|
hasAttribute(type, name) {
|
||||||
return !!this.getAttribute(type, name);
|
const attributes = this.getAttributes();
|
||||||
|
|
||||||
|
return attributes.some(attr => attr.name === name && attr.type === type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -227,7 +227,7 @@ async function cloneNoteToBranch(childNoteId, parentBranchId, prefix) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cloneNoteToNote(childNoteId, parentNoteId, prefix) {
|
async function cloneNoteToParentNote(childNoteId, parentNoteId, prefix) {
|
||||||
const resp = await server.put(`notes/${childNoteId}/clone-to-note/${parentNoteId}`, {
|
const resp = await server.put(`notes/${childNoteId}/clone-to-note/${parentNoteId}`, {
|
||||||
prefix: prefix
|
prefix: prefix
|
||||||
});
|
});
|
||||||
@ -254,5 +254,5 @@ export default {
|
|||||||
moveNodeUpInHierarchy,
|
moveNodeUpInHierarchy,
|
||||||
cloneNoteAfter,
|
cloneNoteAfter,
|
||||||
cloneNoteToBranch,
|
cloneNoteToBranch,
|
||||||
cloneNoteToNote,
|
cloneNoteToParentNote,
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,6 @@ import server from "./server.js";
|
|||||||
import appContext from "../components/app_context.js";
|
import appContext from "../components/app_context.js";
|
||||||
import utils from './utils.js';
|
import utils from './utils.js';
|
||||||
import noteCreateService from './note_create.js';
|
import noteCreateService from './note_create.js';
|
||||||
import treeService from './tree.js';
|
|
||||||
import froca from "./froca.js";
|
import froca from "./froca.js";
|
||||||
|
|
||||||
// this key needs to have this value, so it's hit by the tooltip
|
// this key needs to have this value, so it's hit by the tooltip
|
||||||
@ -188,7 +187,8 @@ function initNoteAutocomplete($el, options) {
|
|||||||
templateNoteId: templateNoteId
|
templateNoteId: templateNoteId
|
||||||
});
|
});
|
||||||
|
|
||||||
suggestion.notePath = treeService.getSomeNotePath(note);
|
const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||||
|
suggestion.notePath = note.getBestNotePathString(hoistedNoteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
$el.setSelectedNotePath(suggestion.notePath);
|
$el.setSelectedNotePath(suggestion.notePath);
|
||||||
|
@ -4,6 +4,7 @@ import froca from "./froca.js";
|
|||||||
import utils from "./utils.js";
|
import utils from "./utils.js";
|
||||||
import attributeRenderer from "./attribute_renderer.js";
|
import attributeRenderer from "./attribute_renderer.js";
|
||||||
import noteContentRenderer from "./note_content_renderer.js";
|
import noteContentRenderer from "./note_content_renderer.js";
|
||||||
|
import appContext from "../components/app_context.js";
|
||||||
|
|
||||||
function setupGlobalTooltip() {
|
function setupGlobalTooltip() {
|
||||||
$(document).on("mouseenter", "a", mouseEnterHandler);
|
$(document).on("mouseenter", "a", mouseEnterHandler);
|
||||||
@ -83,13 +84,14 @@ async function renderTooltip(note) {
|
|||||||
return '<div>Note has been deleted.</div>';
|
return '<div>Note has been deleted.</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
const someNotePath = treeService.getSomeNotePath(note);
|
const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||||
|
const bestNotePath = note.getBestNotePathString(hoistedNoteId);
|
||||||
|
|
||||||
if (!someNotePath) {
|
if (!bestNotePath) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = `<h5 class="note-tooltip-title">${(await treeService.getNoteTitleWithPathAsSuffix(someNotePath)).prop('outerHTML')}</h5>`;
|
let content = `<h5 class="note-tooltip-title">${(await treeService.getNoteTitleWithPathAsSuffix(bestNotePath)).prop('outerHTML')}</h5>`;
|
||||||
|
|
||||||
const {$renderedAttributes} = await attributeRenderer.renderNormalAttributes(note);
|
const {$renderedAttributes} = await attributeRenderer.renderNormalAttributes(note);
|
||||||
|
|
||||||
|
@ -79,14 +79,10 @@ async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logEr
|
|||||||
You can ignore this message as it is mostly harmless.`);
|
You can ignore this message as it is mostly harmless.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const someNotePath = getSomeNotePath(child, hoistedNoteId);
|
const bestNotePath = child.getBestNotePath(hoistedNoteId);
|
||||||
|
|
||||||
if (someNotePath) { // in case it's root the path may be empty
|
if (bestNotePath) {
|
||||||
const pathToRoot = someNotePath.split("/").reverse().slice(1);
|
const pathToRoot = bestNotePath.reverse().slice(1);
|
||||||
|
|
||||||
if (!pathToRoot.includes("root")) {
|
|
||||||
pathToRoot.push('root');
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const noteId of pathToRoot) {
|
for (const noteId of pathToRoot) {
|
||||||
effectivePathSegments.push(noteId);
|
effectivePathSegments.push(noteId);
|
||||||
@ -109,31 +105,17 @@ async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logEr
|
|||||||
else {
|
else {
|
||||||
const note = await froca.getNote(getNoteIdFromNotePath(notePath));
|
const note = await froca.getNote(getNoteIdFromNotePath(notePath));
|
||||||
|
|
||||||
const someNotePathSegments = getSomeNotePathSegments(note, hoistedNoteId);
|
const bestNotePath = note.getBestNotePath(hoistedNoteId);
|
||||||
|
|
||||||
if (!someNotePathSegments) {
|
if (!bestNotePath) {
|
||||||
throw new Error(`Did not find any path segments for ${note.toString()}, hoisted note ${hoistedNoteId}`);
|
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
|
// if there isn't actually any note path with hoisted note then return the original resolved note path
|
||||||
return someNotePathSegments.includes(hoistedNoteId) ? someNotePathSegments : effectivePathSegments;
|
return bestNotePath.includes(hoistedNoteId) ? bestNotePath : 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 => {
|
ws.subscribeToMessages(message => {
|
||||||
if (message.type === 'openNote') {
|
if (message.type === 'openNote') {
|
||||||
appContext.tabManager.activateOrOpenNote(message.noteId);
|
appContext.tabManager.activateOrOpenNote(message.noteId);
|
||||||
@ -311,16 +293,6 @@ function isNotePathInAddress() {
|
|||||||
|| (notePath === '' && !!ntxId);
|
|| (notePath === '' && !!ntxId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseNotePath(notePath) {
|
|
||||||
let noteIds = notePath.split('/');
|
|
||||||
|
|
||||||
if (noteIds[0] !== 'root') {
|
|
||||||
noteIds = ['root'].concat(noteIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
return noteIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNotePathInHiddenSubtree(notePath) {
|
function isNotePathInHiddenSubtree(notePath) {
|
||||||
return notePath?.includes("root/_hidden");
|
return notePath?.includes("root/_hidden");
|
||||||
}
|
}
|
||||||
@ -328,8 +300,6 @@ function isNotePathInHiddenSubtree(notePath) {
|
|||||||
export default {
|
export default {
|
||||||
resolveNotePath,
|
resolveNotePath,
|
||||||
resolveNotePathToSegments,
|
resolveNotePathToSegments,
|
||||||
getSomeNotePath,
|
|
||||||
getSomeNotePathSegments,
|
|
||||||
getParentProtectedStatus,
|
getParentProtectedStatus,
|
||||||
getNotePath,
|
getNotePath,
|
||||||
getNoteIdFromNotePath,
|
getNoteIdFromNotePath,
|
||||||
@ -340,6 +310,5 @@ export default {
|
|||||||
getNoteTitleWithPathAsSuffix,
|
getNoteTitleWithPathAsSuffix,
|
||||||
getHashValueFromAddress,
|
getHashValueFromAddress,
|
||||||
isNotePathInAddress,
|
isNotePathInAddress,
|
||||||
parseNotePath,
|
|
||||||
isNotePathInHiddenSubtree
|
isNotePathInHiddenSubtree
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import server from "../../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import froca from "../../services/froca.js";
|
import froca from "../../services/froca.js";
|
||||||
import treeService from "../../services/tree.js";
|
|
||||||
import linkService from "../../services/link.js";
|
import linkService from "../../services/link.js";
|
||||||
import attributeAutocompleteService from "../../services/attribute_autocomplete.js";
|
import attributeAutocompleteService from "../../services/attribute_autocomplete.js";
|
||||||
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
||||||
@ -9,6 +8,7 @@ import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
|||||||
import SpacedUpdate from "../../services/spaced_update.js";
|
import SpacedUpdate from "../../services/spaced_update.js";
|
||||||
import utils from "../../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
import shortcutService from "../../services/shortcuts.js";
|
import shortcutService from "../../services/shortcuts.js";
|
||||||
|
import appContext from "../../components/app_context.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="attr-detail">
|
<div class="attr-detail">
|
||||||
@ -598,9 +598,10 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
const displayedResults = results.length <= DISPLAYED_NOTES ? results : results.slice(0, DISPLAYED_NOTES);
|
const displayedResults = results.length <= DISPLAYED_NOTES ? results : results.slice(0, DISPLAYED_NOTES);
|
||||||
const displayedNotes = await froca.getNotes(displayedResults.map(res => res.noteId));
|
const displayedNotes = await froca.getNotes(displayedResults.map(res => res.noteId));
|
||||||
|
const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||||
|
|
||||||
for (const note of displayedNotes) {
|
for (const note of displayedNotes) {
|
||||||
const notePath = treeService.getSomeNotePath(note);
|
const notePath = note.getBestNotePathString(hoistedNoteId);
|
||||||
const $noteLink = await linkService.createNoteLink(notePath, {showNotePath: true});
|
const $noteLink = await linkService.createNoteLink(notePath, {showNotePath: true});
|
||||||
|
|
||||||
this.$relatedNotesList.append(
|
this.$relatedNotesList.append(
|
||||||
|
@ -7,7 +7,6 @@ import libraryLoader from "../../services/library_loader.js";
|
|||||||
import froca from "../../services/froca.js";
|
import froca from "../../services/froca.js";
|
||||||
import attributeRenderer from "../../services/attribute_renderer.js";
|
import attributeRenderer from "../../services/attribute_renderer.js";
|
||||||
import noteCreateService from "../../services/note_create.js";
|
import noteCreateService from "../../services/note_create.js";
|
||||||
import treeService from "../../services/tree.js";
|
|
||||||
import attributeService from "../../services/attributes.js";
|
import attributeService from "../../services/attributes.js";
|
||||||
|
|
||||||
const HELP_TEXT = `
|
const HELP_TEXT = `
|
||||||
@ -503,7 +502,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget {
|
|||||||
title: title
|
title: title
|
||||||
});
|
});
|
||||||
|
|
||||||
return treeService.getSomeNotePath(note);
|
return note.getBestNotePathString();
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateAttributeList(attributes) {
|
async updateAttributeList(attributes) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import linkService from '../../services/link.js';
|
import linkService from '../../services/link.js';
|
||||||
import utils from '../../services/utils.js';
|
import utils from '../../services/utils.js';
|
||||||
import server from '../../services/server.js';
|
import server from '../../services/server.js';
|
||||||
import treeService from "../../services/tree.js";
|
|
||||||
import froca from "../../services/froca.js";
|
import froca from "../../services/froca.js";
|
||||||
import appContext from "../../components/app_context.js";
|
import appContext from "../../components/app_context.js";
|
||||||
import hoistedNoteService from "../../services/hoisted_note.js";
|
import hoistedNoteService from "../../services/hoisted_note.js";
|
||||||
@ -108,7 +107,7 @@ export default class RecentChangesDialog extends BasicWidget {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const note = await froca.getNote(change.noteId);
|
const note = await froca.getNote(change.noteId);
|
||||||
const notePath = treeService.getSomeNotePath(note);
|
const notePath = note.getBestNotePathString();
|
||||||
|
|
||||||
if (notePath) {
|
if (notePath) {
|
||||||
$noteLink = await linkService.createNoteLink(notePath, {
|
$noteLink = await linkService.createNoteLink(notePath, {
|
||||||
|
@ -72,7 +72,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedNotePaths = this.note.getSortedNotePaths(this.hoistedNoteId)
|
const sortedNotePaths = this.note.getSortedNotePathRecords(this.hoistedNoteId)
|
||||||
.filter(notePath => !notePath.isHidden);
|
.filter(notePath => !notePath.isHidden);
|
||||||
|
|
||||||
if (sortedNotePaths.length > 0) {
|
if (sortedNotePaths.length > 0) {
|
||||||
|
@ -25,7 +25,7 @@ export default class SharedSwitchWidget extends SwitchWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async switchOn() {
|
async switchOn() {
|
||||||
await branchService.cloneNoteToNote(this.noteId, '_share');
|
await branchService.cloneNoteToParentNote(this.noteId, '_share');
|
||||||
|
|
||||||
syncService.syncNow(true);
|
syncService.syncNow(true);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import mimeTypesService from '../../services/mime_types.js';
|
|||||||
import utils from "../../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
import keyboardActionService from "../../services/keyboard_actions.js";
|
import keyboardActionService from "../../services/keyboard_actions.js";
|
||||||
import froca from "../../services/froca.js";
|
import froca from "../../services/froca.js";
|
||||||
import treeService from "../../services/tree.js";
|
|
||||||
import noteCreateService from "../../services/note_create.js";
|
import noteCreateService from "../../services/note_create.js";
|
||||||
import AbstractTextTypeWidget from "./abstract_text_type_widget.js";
|
import AbstractTextTypeWidget from "./abstract_text_type_widget.js";
|
||||||
import link from "../../services/link.js";
|
import link from "../../services/link.js";
|
||||||
@ -378,7 +377,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return treeService.getSomeNotePath(resp.note);
|
return resp.note.getBestNotePathString();
|
||||||
}
|
}
|
||||||
|
|
||||||
async refreshIncludedNoteEvent({noteId}) {
|
async refreshIncludedNoteEvent({noteId}) {
|
||||||
|
@ -9,11 +9,11 @@ function cloneNoteToBranch(req) {
|
|||||||
return cloningService.cloneNoteToBranch(noteId, parentBranchId, prefix);
|
return cloningService.cloneNoteToBranch(noteId, parentBranchId, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cloneNoteToNote(req) {
|
function cloneNoteToParentNote(req) {
|
||||||
const {noteId, parentNoteId} = req.params;
|
const {noteId, parentNoteId} = req.params;
|
||||||
const {prefix} = req.body;
|
const {prefix} = req.body;
|
||||||
|
|
||||||
return cloningService.cloneNoteToNote(noteId, parentNoteId, prefix);
|
return cloningService.cloneNoteToParentNote(noteId, parentNoteId, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cloneNoteAfter(req) {
|
function cloneNoteAfter(req) {
|
||||||
@ -30,7 +30,7 @@ function toggleNoteInParent(req) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
cloneNoteToBranch,
|
cloneNoteToBranch,
|
||||||
cloneNoteToNote,
|
cloneNoteToParentNote,
|
||||||
cloneNoteAfter,
|
cloneNoteAfter,
|
||||||
toggleNoteInParent
|
toggleNoteInParent
|
||||||
};
|
};
|
||||||
|
@ -143,7 +143,7 @@ function register(app) {
|
|||||||
|
|
||||||
apiRoute(PUT, '/api/notes/:noteId/clone-to-branch/:parentBranchId', cloningApiRoute.cloneNoteToBranch);
|
apiRoute(PUT, '/api/notes/:noteId/clone-to-branch/:parentBranchId', cloningApiRoute.cloneNoteToBranch);
|
||||||
apiRoute(PUT, '/api/notes/:noteId/toggle-in-parent/:parentNoteId/:present', cloningApiRoute.toggleNoteInParent);
|
apiRoute(PUT, '/api/notes/:noteId/toggle-in-parent/:parentNoteId/:present', cloningApiRoute.toggleNoteInParent);
|
||||||
apiRoute(PUT, '/api/notes/:noteId/clone-to-note/:parentNoteId', cloningApiRoute.cloneNoteToNote);
|
apiRoute(PUT, '/api/notes/:noteId/clone-to-note/:parentNoteId', cloningApiRoute.cloneNoteToParentNote);
|
||||||
apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);
|
apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);
|
||||||
|
|
||||||
route(GET, '/api/notes/:branchId/export/:type/:format/:version/:taskId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
|
route(GET, '/api/notes/:branchId/export/:type/:format/:version/:taskId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
|
||||||
|
@ -83,7 +83,7 @@ const ACTION_HANDLERS = {
|
|||||||
let res;
|
let res;
|
||||||
|
|
||||||
if (note.getParentBranches().length > 1) {
|
if (note.getParentBranches().length > 1) {
|
||||||
res = cloningService.cloneNoteToNote(note.noteId, action.targetParentNoteId);
|
res = cloningService.cloneNoteToParentNote(note.noteId, action.targetParentNoteId);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res = branchService.moveBranchToNote(note.getParentBranches()[0], action.targetParentNoteId);
|
res = branchService.moveBranchToNote(note.getParentBranches()[0], action.targetParentNoteId);
|
||||||
|
@ -8,7 +8,7 @@ const becca = require("../becca/becca");
|
|||||||
const beccaService = require("../becca/becca_service");
|
const beccaService = require("../becca/becca_service");
|
||||||
const log = require("./log");
|
const log = require("./log");
|
||||||
|
|
||||||
function cloneNoteToNote(noteId, parentNoteId, prefix) {
|
function cloneNoteToParentNote(noteId, parentNoteId, prefix) {
|
||||||
const parentNote = becca.getNote(parentNoteId);
|
const parentNote = becca.getNote(parentNoteId);
|
||||||
|
|
||||||
if (parentNote.type === 'search') {
|
if (parentNote.type === 'search') {
|
||||||
@ -19,7 +19,7 @@ function cloneNoteToNote(noteId, parentNoteId, prefix) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isNoteDeleted(noteId) || isNoteDeleted(parentNoteId)) {
|
if (isNoteDeleted(noteId) || isNoteDeleted(parentNoteId)) {
|
||||||
return { success: false, message: 'Note is deleted.' };
|
return { success: false, message: 'Note cannot be cloned because either the cloned note or the intended parent is deleted.' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const validationResult = treeService.validateParentChild(parentNoteId, noteId);
|
const validationResult = treeService.validateParentChild(parentNoteId, noteId);
|
||||||
@ -35,7 +35,7 @@ function cloneNoteToNote(noteId, parentNoteId, prefix) {
|
|||||||
isExpanded: 0
|
isExpanded: 0
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
log.info(`Cloned note '${noteId}' to new parent note '${parentNoteId}' with prefix '${prefix}'`);
|
log.info(`Cloned note '${noteId}' to a new parent note '${parentNoteId}' with prefix '${prefix}'`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@ -51,7 +51,7 @@ function cloneNoteToBranch(noteId, parentBranchId, prefix) {
|
|||||||
return { success: false, message: `Parent branch ${parentBranchId} does not exist.` };
|
return { success: false, message: `Parent branch ${parentBranchId} does not exist.` };
|
||||||
}
|
}
|
||||||
|
|
||||||
const ret = cloneNoteToNote(noteId, parentBranch.noteId, prefix);
|
const ret = cloneNoteToParentNote(noteId, parentBranch.noteId, prefix);
|
||||||
|
|
||||||
parentBranch.isExpanded = true; // the new target should be expanded, so it immediately shows up to the user
|
parentBranch.isExpanded = true; // the new target should be expanded, so it immediately shows up to the user
|
||||||
parentBranch.save();
|
parentBranch.save();
|
||||||
@ -182,7 +182,7 @@ function isNoteDeleted(noteId) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
cloneNoteToBranch,
|
cloneNoteToBranch,
|
||||||
cloneNoteToNote,
|
cloneNoteToParentNote,
|
||||||
ensureNoteIsPresentInParent,
|
ensureNoteIsPresentInParent,
|
||||||
ensureNoteIsAbsentFromParent,
|
ensureNoteIsAbsentFromParent,
|
||||||
toggleNoteInParent,
|
toggleNoteInParent,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user