smaller refactorings in note_tree

This commit is contained in:
zadam 2020-08-24 23:33:27 +02:00
parent b7947a40ea
commit 6a3e27eb62
6 changed files with 85 additions and 133 deletions

View File

@ -30,16 +30,16 @@ function isRootNode(node) {
async function checkNoteAccess(notePath) { async function checkNoteAccess(notePath) {
// notePath argument can contain only noteId which is not good when hoisted since // notePath argument can contain only noteId which is not good when hoisted since
// then we need to check the whole note path // then we need to check the whole note path
const runNotePath = await treeService.getRunPath(notePath); const resolvedNotePath = await treeService.resolveNotePath(notePath);
if (!runNotePath) { if (!resolvedNotePath) {
console.log("Cannot activate " + notePath); console.log("Cannot activate " + notePath);
return false; return false;
} }
const hoistedNoteId = getHoistedNoteId(); const hoistedNoteId = getHoistedNoteId();
if (hoistedNoteId !== 'root' && !runNotePath.includes(hoistedNoteId)) { if (hoistedNoteId !== 'root' && !resolvedNotePath.includes(hoistedNoteId)) {
const confirmDialog = await import('../dialogs/confirm.js'); const confirmDialog = await import('../dialogs/confirm.js');
if (!await confirmDialog.confirm("Requested note is outside of hoisted note subtree and you must unhoist to access the note. Do you want to proceed with unhoisting?")) { if (!await confirmDialog.confirm("Requested note is outside of hoisted note subtree and you must unhoist to access the note. Do you want to proceed with unhoisting?")) {
@ -60,4 +60,4 @@ export default {
isTopLevelNode, isTopLevelNode,
isRootNode, isRootNode,
checkNoteAccess checkNoteAccess
} }

View File

@ -38,13 +38,12 @@ async function createNoteLink(notePath, options = {}) {
const $container = $("<span>").append($noteLink); const $container = $("<span>").append($noteLink);
if (showNotePath) { if (showNotePath) {
notePath = await treeService.resolveNotePath(notePath); const resolvedNotePathSegments = await treeService.resolveNotePathToSegments(notePath);
if (notePath) { if (notePath) {
const noteIds = notePath.split("/"); resolvedNotePathSegments.pop(); // remove last element
noteIds.pop(); // remove last element
const parentNotePath = noteIds.join("/").trim(); const parentNotePath = resolvedNotePathSegments.join("/").trim();
if (parentNotePath) { if (parentNotePath) {
$container.append($("<small>").text(" (" + await treeService.getNotePathTitle(parentNotePath) + ")")); $container.append($("<small>").text(" (" + await treeService.getNotePathTitle(parentNotePath) + ")"));

View File

@ -26,25 +26,25 @@ class TabContext extends Component {
async setNote(inputNotePath, triggerSwitchEvent = true) { async setNote(inputNotePath, triggerSwitchEvent = true) {
const noteId = treeService.getNoteIdFromNotePath(inputNotePath); const noteId = treeService.getNoteIdFromNotePath(inputNotePath);
let notePath; let resolvedNotePath;
if ((await treeCache.getNote(noteId)).isDeleted) { if ((await treeCache.getNote(noteId)).isDeleted) {
// no point in trying to resolve canonical notePath // no point in trying to resolve canonical notePath
notePath = inputNotePath; resolvedNotePath = inputNotePath;
} }
else { else {
notePath = await treeService.resolveNotePath(inputNotePath); resolvedNotePath = await treeService.resolveNotePath(inputNotePath);
if (!notePath) { if (!resolvedNotePath) {
console.error(`Cannot resolve note path ${inputNotePath}`); console.error(`Cannot resolve note path ${inputNotePath}`);
return; return;
} }
if (notePath === this.notePath) { if (resolvedNotePath === this.notePath) {
return; return;
} }
if (await hoistedNoteService.checkNoteAccess(notePath) === false) { if (await hoistedNoteService.checkNoteAccess(resolvedNotePath) === false) {
return; // note is outside of hoisted subtree and user chose not to unhoist return; // note is outside of hoisted subtree and user chose not to unhoist
} }
} }
@ -53,7 +53,7 @@ class TabContext extends Component {
utils.closeActiveDialog(); utils.closeActiveDialog();
this.notePath = notePath; this.notePath = resolvedNotePath;
this.noteId = noteId; this.noteId = noteId;
this.autoBookDisabled = false; this.autoBookDisabled = false;
@ -62,7 +62,7 @@ class TabContext extends Component {
setTimeout(async () => { setTimeout(async () => {
// we include the note into recent list only if the user stayed on the note at least 5 seconds // we include the note into recent list only if the user stayed on the note at least 5 seconds
if (notePath && notePath === this.notePath) { if (resolvedNotePath && resolvedNotePath === this.notePath) {
await server.post('recent-notes', { await server.post('recent-notes', {
noteId: this.note.noteId, noteId: this.note.noteId,
notePath: this.notePath notePath: this.notePath

View File

@ -6,25 +6,25 @@ import hoistedNoteService from '../services/hoisted_note.js';
import appContext from "./app_context.js"; import appContext from "./app_context.js";
/** /**
* Accepts notePath which might or might not be valid and returns an existing path as close to the original
* notePath as possible.
* @return {string|null} * @return {string|null}
*/ */
async function resolveNotePath(notePath) { async function resolveNotePath(notePath) {
const runPath = await getRunPath(notePath); const runPath = await resolveNotePathToSegments(notePath);
return runPath ? runPath.join("/") : null; return runPath ? runPath.join("/") : null;
} }
/** /**
* Accepts notePath and tries to resolve it. Part of the path might not be valid because of note moving (which causes * 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. * path change) or other corruption, in that case this will try to get some other valid path to the correct note.
* *
* @return {string[]} * @return {string[]}
*/ */
async function getRunPath(notePath, logErrors = true) { async function resolveNotePathToSegments(notePath, logErrors = true) {
utils.assertArguments(notePath); utils.assertArguments(notePath);
// we might get notePath with the tabId suffix, remove it if present
notePath = notePath.split("-")[0].trim(); notePath = notePath.split("-")[0].trim();
if (notePath.length === 0) { if (notePath.length === 0) {
@ -54,48 +54,38 @@ async function getRunPath(notePath, logErrors = true) {
const child = await treeCache.getNote(childNoteId); const child = await treeCache.getNote(childNoteId);
if (!child) { if (!child) {
console.log("Can't find note " + childNoteId); console.log(`Can't find note ${childNoteId}`);
return; return;
} }
const parents = child.getParentNotes(); const parents = child.getParentNotes();
if (!parents) { if (!parents.length) {
ws.logError("No parents found for " + childNoteId); if (logErrors) {
ws.logError(`No parents found for ${childNoteId}`);
}
return; return;
} }
if (!parents.some(p => p.noteId === parentNoteId)) { if (!parents.some(p => p.noteId === parentNoteId)) {
if (logErrors) { if (logErrors) {
console.log(utils.now(), "Did not find parent " + parentNoteId + " for child " + childNoteId); console.log(utils.now(), `Did not find parent ${parentNoteId} for child ${childNoteId}, available parents: ${parents}`);
} }
if (parents.length > 0) { const someNotePath = getSomeNotePath(parents[0]);
if (logErrors) {
console.log(utils.now(), "Available parents:", parents); if (someNotePath) { // in case it's root the path may be empty
const pathToRoot = someNotePath.split("/").reverse();
for (const noteId of pathToRoot) {
effectivePath.push(noteId);
} }
const someNotePath = getSomeNotePath(parents[0]); effectivePath.push('root');
if (someNotePath) { // in case it's root the path may be empty
const pathToRoot = someNotePath.split("/").reverse();
for (const noteId of pathToRoot) {
effectivePath.push(noteId);
}
effectivePath.push('root');
}
break;
} }
else {
if (logErrors) {
console.log("No parents so no run path.");
}
return; break;
}
} }
} }
@ -136,7 +126,7 @@ function getSomeNotePath(note) {
} }
async function sortAlphabetically(noteId) { async function sortAlphabetically(noteId) {
await server.put('notes/' + noteId + '/sort'); await server.put(`notes/${noteId}/sort`);
} }
ws.subscribeToMessages(message => { ws.subscribeToMessages(message => {
@ -238,7 +228,7 @@ async function getNoteTitle(noteId, parentNoteId = null) {
const branch = treeCache.getBranch(branchId); const branch = treeCache.getBranch(branchId);
if (branch && branch.prefix) { if (branch && branch.prefix) {
title = branch.prefix + ' - ' + title; title = `${branch.prefix} - ${title}`;
} }
} }
} }
@ -290,8 +280,8 @@ function parseNotePath(notePath) {
export default { export default {
sortAlphabetically, sortAlphabetically,
resolveNotePath, resolveNotePath,
resolveNotePathToSegments,
getSomeNotePath, getSomeNotePath,
getRunPath,
getParentProtectedStatus, getParentProtectedStatus,
getNotePath, getNotePath,
getNoteIdFromNotePath, getNoteIdFromNotePath,

View File

@ -303,12 +303,12 @@ export default class NoteTreeWidget extends TabAwareWidget {
this.$tree.fancytree({ this.$tree.fancytree({
titlesTabbable: true, titlesTabbable: true,
autoScroll: true, autoScroll: true,
keyboard: false, // we takover keyboard handling in the hotkeys plugin keyboard: true,
extensions: utils.isMobile() ? ["dnd5", "clones"] : ["hotkeys", "dnd5", "clones"], extensions: utils.isMobile() ? ["dnd5", "clones"] : ["hotkeys", "dnd5", "clones"],
source: treeData, source: treeData,
scrollOfs: { scrollOfs: {
top: 200, top: 100,
bottom: 200 bottom: 100
}, },
scrollParent: this.$tree, scrollParent: this.$tree,
minExpandLevel: 2, // root can't be collapsed minExpandLevel: 2, // root can't be collapsed
@ -372,10 +372,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
})); }));
data.dataTransfer.setData("text", JSON.stringify(notes)); data.dataTransfer.setData("text", JSON.stringify(notes));
return true; // allow dragging to start
// This function MUST be defined to enable dragging for the tree.
// Return false to cancel dragging of node.
return true;
}, },
dragEnter: (node, data) => true, // allow drop on any node dragEnter: (node, data) => true, // allow drop on any node
dragOver: (node, data) => true, dragOver: (node, data) => true,
@ -528,6 +525,38 @@ export default class NoteTreeWidget extends TabAwareWidget {
} }
} }
async prepareSearchNoteChildren(note) {
await treeCache.reloadNotes([note.noteId]);
const newNote = await treeCache.getNote(note.noteId);
return await this.prepareNormalNoteChildren(newNote);
}
async prepareNormalNoteChildren(parentNote) {
utils.assertArguments(parentNote);
const noteList = [];
const hideArchivedNotes = this.hideArchivedNotes;
for (const branch of this.getChildBranches(parentNote)) {
if (hideArchivedNotes) {
const note = await branch.getNote();
if (note.hasLabel('archived')) {
continue;
}
}
const node = await this.prepareNode(branch);
noteList.push(node);
}
return noteList;
}
getIconClass(note) { getIconClass(note) {
const labels = note.getLabels('iconClass'); const labels = note.getLabels('iconClass');
@ -631,30 +660,6 @@ export default class NoteTreeWidget extends TabAwareWidget {
} }
} }
async prepareNormalNoteChildren(parentNote) {
utils.assertArguments(parentNote);
const noteList = [];
const hideArchivedNotes = this.hideArchivedNotes;
for (const branch of this.getChildBranches(parentNote)) {
if (hideArchivedNotes) {
const note = await branch.getNote();
if (note.hasLabel('archived')) {
continue;
}
}
const node = await this.prepareNode(branch);
noteList.push(node);
}
return noteList;
}
getChildBranches(parentNote) { getChildBranches(parentNote) {
let childBranches = parentNote.getChildBranches(); let childBranches = parentNote.getChildBranches();
@ -678,14 +683,6 @@ export default class NoteTreeWidget extends TabAwareWidget {
return childBranches; return childBranches;
} }
async prepareSearchNoteChildren(note) {
await treeCache.reloadNotes([note.noteId]);
const newNote = await treeCache.getNote(note.noteId);
return await this.prepareNormalNoteChildren(newNote);
}
getExtraClasses(note) { getExtraClasses(note) {
utils.assertArguments(note); utils.assertArguments(note);
@ -810,9 +807,9 @@ export default class NoteTreeWidget extends TabAwareWidget {
/** @const {FancytreeNode} */ /** @const {FancytreeNode} */
let parentNode = null; let parentNode = null;
const runPath = await treeService.getRunPath(notePath, logErrors); const resolvedNotePathSegments = await treeService.resolveNotePathToSegments(notePath, logErrors);
if (!runPath) { if (!resolvedNotePathSegments) {
if (logErrors) { if (logErrors) {
console.error("Could not find run path for notePath:", notePath); console.error("Could not find run path for notePath:", notePath);
} }
@ -820,7 +817,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
return; return;
} }
for (const childNoteId of runPath) { for (const childNoteId of resolvedNotePathSegments) {
if (childNoteId === hoistedNoteId) { if (childNoteId === hoistedNoteId) {
// there must be exactly one node with given hoistedNoteId // there must be exactly one node with given hoistedNoteId
parentNode = this.getNodesByNoteId(childNoteId)[0]; parentNode = this.getNodesByNoteId(childNoteId)[0];
@ -870,16 +867,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
/** @return {FancytreeNode} */ /** @return {FancytreeNode} */
findChildNode(parentNode, childNoteId) { findChildNode(parentNode, childNoteId) {
let foundChildNode = null; return parentNode.getChildren().find(childNode => childNode.data.noteId === childNoteId);
for (const childNode of parentNode.getChildren()) {
if (childNode.data.noteId === childNoteId) {
foundChildNode = childNode;
break;
}
}
return foundChildNode;
} }
/** @return {FancytreeNode} */ /** @return {FancytreeNode} */
@ -1154,6 +1142,9 @@ export default class NoteTreeWidget extends TabAwareWidget {
} }
async setExpanded(branchId, isExpanded) { async setExpanded(branchId, isExpanded) {
console.log("expand", isExpanded);
utils.assertArguments(branchId); utils.assertArguments(branchId);
const branch = treeCache.getBranch(branchId); const branch = treeCache.getBranch(branchId);
@ -1190,35 +1181,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
async getHotKeys() { async getHotKeys() {
const actions = await keyboardActionsService.getActionsForScope('note-tree'); const actions = await keyboardActionsService.getActionsForScope('note-tree');
const hotKeyMap = { const hotKeyMap = {};
// code below shouldn't be necessary normally, however there's some problem with interaction with context menu plugin
// after opening context menu, standard shortcuts don't work, but they are detected here
// so we essentially takeover the standard handling with our implementation.
"left": node => {
node.navigate($.ui.keyCode.LEFT, true);
this.clearSelectedNodes();
return false;
},
"right": node => {
node.navigate($.ui.keyCode.RIGHT, true);
this.clearSelectedNodes();
return false;
},
"up": node => {
node.navigate($.ui.keyCode.UP, true);
this.clearSelectedNodes();
return false;
},
"down": node => {
node.navigate($.ui.keyCode.DOWN, true);
this.clearSelectedNodes();
return false;
}
};
for (const action of actions) { for (const action of actions) {
for (const shortcut of action.effectiveShortcuts) { for (const shortcut of action.effectiveShortcuts) {

View File

@ -368,12 +368,12 @@ const DEFAULT_KEYBOARD_ACTIONS = [
}, },
{ {
actionName: "zoomOut", actionName: "zoomOut",
defaultShortcuts: ["CommandOrControl+-"], defaultShortcuts: isElectron ? ["CommandOrControl+-"] : [],
scope: "window" scope: "window"
}, },
{ {
actionName: "zoomIn", actionName: "zoomIn",
defaultShortcuts: ["CommandOrControl+="], defaultShortcuts: isElectron ? ["CommandOrControl+="] : [],
scope: "window" scope: "window"
}, },
{ {