mirror of
https://github.com/zadam/trilium.git
synced 2025-06-05 17:38:47 +02:00
don't allow deleting hoisted note + other related fixes, closes #488
This commit is contained in:
parent
3760835608
commit
064a11d872
@ -86,10 +86,10 @@ $("#note-menu-button").click(async e => {
|
||||
enabled: isNotRoot && parentNote.type !== 'search' }
|
||||
];
|
||||
|
||||
contextMenuWidget.initContextMenu(e, items, (event, cmd) => {
|
||||
contextMenuWidget.initContextMenu(e, items, async (event, cmd) => {
|
||||
if (cmd === "insertNoteAfter") {
|
||||
const parentNoteId = node.data.parentNoteId;
|
||||
const isProtected = treeUtils.getParentProtectedStatus(node);
|
||||
const isProtected = await treeUtils.getParentProtectedStatus(node);
|
||||
|
||||
treeService.createNote(node, parentNoteId, 'after', { isProtected: isProtected });
|
||||
}
|
||||
|
@ -3,9 +3,10 @@ import utils from './utils.js';
|
||||
import server from './server.js';
|
||||
import infoService from "./info.js";
|
||||
import treeCache from "./tree_cache.js";
|
||||
import hoistedNoteService from "./hoisted_note.js";
|
||||
|
||||
async function moveBeforeNode(nodesToMove, beforeNode) {
|
||||
nodesToMove = filterRootNote(nodesToMove);
|
||||
nodesToMove = await filterRootNote(nodesToMove);
|
||||
|
||||
if (beforeNode.data.noteId === 'root') {
|
||||
alert('Cannot move notes before root note.');
|
||||
@ -29,9 +30,9 @@ async function moveBeforeNode(nodesToMove, beforeNode) {
|
||||
}
|
||||
|
||||
async function moveAfterNode(nodesToMove, afterNode) {
|
||||
nodesToMove = filterRootNote(nodesToMove);
|
||||
nodesToMove = await filterRootNote(nodesToMove);
|
||||
|
||||
if (afterNode.data.noteId === 'root') {
|
||||
if (afterNode.data.noteId === 'root' || afterNode.data.noteId === await hoistedNoteService.getHoistedNoteId()) {
|
||||
alert('Cannot move notes after root note.');
|
||||
return;
|
||||
}
|
||||
@ -55,7 +56,7 @@ async function moveAfterNode(nodesToMove, afterNode) {
|
||||
}
|
||||
|
||||
async function moveToNode(nodesToMove, toNode) {
|
||||
nodesToMove = filterRootNote(nodesToMove);
|
||||
nodesToMove = await filterRootNote(nodesToMove);
|
||||
|
||||
for (const nodeToMove of nodesToMove) {
|
||||
const resp = await server.put('branches/' + nodeToMove.data.branchId + '/move-to/' + toNode.data.noteId);
|
||||
@ -76,13 +77,8 @@ async function moveToNode(nodesToMove, toNode) {
|
||||
}
|
||||
}
|
||||
|
||||
function filterRootNote(nodes) {
|
||||
// some operations are not possible on root notes
|
||||
return nodes.filter(node => node.data.noteId !== 'root');
|
||||
}
|
||||
|
||||
async function deleteNodes(nodes) {
|
||||
nodes = filterRootNote(nodes);
|
||||
nodes = await filterRootNote(nodes);
|
||||
|
||||
if (nodes.length === 0 || !confirm('Are you sure you want to delete select note(s) and all the sub-notes?')) {
|
||||
return;
|
||||
@ -92,8 +88,6 @@ async function deleteNodes(nodes) {
|
||||
await server.remove('branches/' + node.data.branchId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// following code assumes that nodes contain only top-most selected nodes - getSelectedNodes has been
|
||||
// called with stopOnParent=true
|
||||
let next = nodes[nodes.length - 1].getNextSibling();
|
||||
@ -102,7 +96,7 @@ async function deleteNodes(nodes) {
|
||||
next = nodes[0].getPrevSibling();
|
||||
}
|
||||
|
||||
if (!next && !utils.isTopLevelNode(nodes[0])) {
|
||||
if (!next && !hoistedNoteService.isTopLevelNode(nodes[0])) {
|
||||
next = nodes[0].getParent();
|
||||
}
|
||||
|
||||
@ -129,7 +123,7 @@ async function deleteNodes(nodes) {
|
||||
}
|
||||
|
||||
async function moveNodeUpInHierarchy(node) {
|
||||
if (utils.isRootNode(node) || utils.isTopLevelNode(node)) {
|
||||
if (await hoistedNoteService.isRootNode(node) || await hoistedNoteService.isTopLevelNode(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -140,7 +134,7 @@ async function moveNodeUpInHierarchy(node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!utils.isTopLevelNode(node) && node.getParent().getChildren().length <= 1) {
|
||||
if (!hoistedNoteService.isTopLevelNode(node) && node.getParent().getChildren().length <= 1) {
|
||||
node.getParent().folder = false;
|
||||
node.getParent().renderTitle();
|
||||
}
|
||||
@ -202,6 +196,14 @@ async function changeNode(func, node, beforeNoteId = null, afterNoteId = null) {
|
||||
}
|
||||
}
|
||||
|
||||
async function filterRootNote(nodes) {
|
||||
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||
|
||||
return nodes.filter(node =>
|
||||
node.data.noteId !== 'root'
|
||||
&& node.data.noteId !== hoistedNoteId);
|
||||
}
|
||||
|
||||
export default {
|
||||
moveBeforeNode,
|
||||
moveAfterNode,
|
||||
|
@ -42,7 +42,7 @@ function initContextMenu(event, contextMenuItems, selectContextMenuItem) {
|
||||
});
|
||||
|
||||
if (item.enabled !== undefined && !item.enabled) {
|
||||
$link.addClass("disabled");
|
||||
$item.addClass("disabled");
|
||||
}
|
||||
|
||||
if (item.items) {
|
||||
|
@ -236,7 +236,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
|
||||
|
||||
/**
|
||||
* @method
|
||||
* @returns {string} returns note path of active note
|
||||
* @returns {Promise<string>} returns note path of active note
|
||||
*/
|
||||
this.getActiveNotePath = treeService.getActiveNotePath;
|
||||
|
||||
|
@ -26,8 +26,20 @@ async function unhoist() {
|
||||
await setHoistedNoteId('root');
|
||||
}
|
||||
|
||||
async function isTopLevelNode(node) {
|
||||
return await isRootNode(node.getParent());
|
||||
}
|
||||
|
||||
async function isRootNode(node) {
|
||||
// even though check for 'root' should not be necessary, we keep it just in case
|
||||
return node.data.noteId === "root"
|
||||
|| node.data.noteId === await getHoistedNoteId();
|
||||
}
|
||||
|
||||
export default {
|
||||
getHoistedNoteId,
|
||||
setHoistedNoteId,
|
||||
unhoist
|
||||
unhoist,
|
||||
isTopLevelNode,
|
||||
isRootNode
|
||||
}
|
@ -270,7 +270,7 @@ async function showChildrenOverview() {
|
||||
|
||||
$childrenOverview.empty();
|
||||
|
||||
const notePath = treeService.getActiveNotePath();
|
||||
const notePath = await treeService.getActiveNotePath();
|
||||
|
||||
for (const childBranch of await note.getChildBranches()) {
|
||||
const link = $('<a>', {
|
||||
|
@ -39,10 +39,8 @@ function getActiveNode() {
|
||||
return $tree.fancytree("getActiveNode");
|
||||
}
|
||||
|
||||
function getActiveNotePath() {
|
||||
const node = getActiveNode();
|
||||
|
||||
return treeUtils.getNotePath(node);
|
||||
async function getActiveNotePath() {
|
||||
return getHashValueFromAddress();
|
||||
}
|
||||
|
||||
async function getNodesByBranchId(branchId) {
|
||||
@ -325,16 +323,16 @@ async function setExpandedToServer(branchId, isExpanded) {
|
||||
function addRecentNote(branchId, notePath) {
|
||||
setTimeout(async () => {
|
||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||
if (notePath && notePath === getActiveNotePath()) {
|
||||
if (notePath && notePath === await getActiveNotePath()) {
|
||||
await server.post('recent-notes', { branchId, notePath });
|
||||
}
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
function setCurrentNotePathToHash(node) {
|
||||
async function setCurrentNotePathToHash(node) {
|
||||
utils.assertArguments(node);
|
||||
|
||||
const activeNotePath = treeUtils.getNotePath(node);
|
||||
const activeNotePath = await treeUtils.getNotePath(node);
|
||||
const currentBranchId = node.data.branchId;
|
||||
|
||||
document.location.hash = activeNotePath;
|
||||
@ -412,14 +410,14 @@ function initFancyTree(tree) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
activate: (event, data) => {
|
||||
activate: async (event, data) => {
|
||||
const node = data.node;
|
||||
const noteId = node.data.noteId;
|
||||
|
||||
// click event won't propagate so let's close context menu manually
|
||||
contextMenuWidget.hideContextMenu();
|
||||
|
||||
setCurrentNotePathToHash(node);
|
||||
await setCurrentNotePathToHash(node);
|
||||
|
||||
noteDetailService.switchToNote(noteId);
|
||||
|
||||
@ -721,7 +719,7 @@ messagingService.subscribeToSyncMessages(syncData => {
|
||||
utils.bindShortcut('ctrl+o', async () => {
|
||||
const node = getActiveNode();
|
||||
const parentNoteId = node.data.parentNoteId;
|
||||
const isProtected = treeUtils.getParentProtectedStatus(node);
|
||||
const isProtected = await treeUtils.getParentProtectedStatus(node);
|
||||
|
||||
if (node.data.noteId === 'root' || node.data.noteId === await hoistedNoteService.getHoistedNoteId()) {
|
||||
return;
|
||||
@ -774,11 +772,11 @@ utils.bindShortcut('ctrl+p', createNoteInto);
|
||||
|
||||
utils.bindShortcut('ctrl+.', scrollToActiveNote);
|
||||
|
||||
$(window).bind('hashchange', function() {
|
||||
$(window).bind('hashchange', async function() {
|
||||
if (isNotePathInAddress()) {
|
||||
const notePath = getHashValueFromAddress();
|
||||
|
||||
if (notePath !== '-' && getActiveNotePath() !== notePath) {
|
||||
if (notePath !== '-' && await getActiveNotePath() !== notePath) {
|
||||
console.debug("Switching to " + notePath + " because of hash change");
|
||||
|
||||
activateNote(notePath);
|
||||
|
@ -110,7 +110,7 @@ async function getTopLevelItems(event) {
|
||||
items: insertChildNoteEnabled ? getNoteTypeItems("insertChildNote") : null,
|
||||
enabled: insertChildNoteEnabled },
|
||||
{ title: "Delete <kbd>Delete</kbd>", cmd: "delete", uiIcon: "trash",
|
||||
enabled: isNotRoot && parentNote.type !== 'search' },
|
||||
enabled: isNotRoot && !isHoisted && parentNote.type !== 'search' },
|
||||
{ title: "----" },
|
||||
isHoisted ? null : { title: "Hoist note <kbd>Ctrl-H</kbd>", cmd: "hoist", uiIcon: "empty" },
|
||||
!isHoisted || !isNotRoot ? null : { title: "Unhoist note <kbd>Ctrl-H</kbd>", cmd: "unhoist", uiIcon: "arrow-up" },
|
||||
@ -156,13 +156,13 @@ async function getContextMenuItems(event) {
|
||||
return items;
|
||||
}
|
||||
|
||||
function selectContextMenuItem(event, cmd) {
|
||||
async function selectContextMenuItem(event, cmd) {
|
||||
// context menu is always triggered on current node
|
||||
const node = treeService.getActiveNode();
|
||||
|
||||
if (cmd.startsWith("insertNoteAfter")) {
|
||||
const parentNoteId = node.data.parentNoteId;
|
||||
const isProtected = treeUtils.getParentProtectedStatus(node);
|
||||
const isProtected = await treeUtils.getParentProtectedStatus(node);
|
||||
const type = cmd.split("_")[1];
|
||||
|
||||
treeService.createNote(node, parentNoteId, 'after', {
|
||||
|
@ -109,8 +109,8 @@ const keyBindings = {
|
||||
|
||||
return false;
|
||||
},
|
||||
"backspace": node => {
|
||||
if (!utils.isRootNode(node)) {
|
||||
"backspace": async node => {
|
||||
if (!await hoistedNoteService.isRootNode(node)) {
|
||||
node.getParent().setActive().then(treeService.clearSelectedNodes);
|
||||
}
|
||||
},
|
||||
|
@ -1,10 +1,11 @@
|
||||
import utils from './utils.js';
|
||||
import hoistedNoteService from './hoisted_note.js';
|
||||
import treeCache from "./tree_cache.js";
|
||||
|
||||
const $tree = $("#tree");
|
||||
|
||||
function getParentProtectedStatus(node) {
|
||||
return utils.isRootNode(node) ? 0 : node.getParent().data.isProtected;
|
||||
async function getParentProtectedStatus(node) {
|
||||
return await hoistedNoteService.isRootNode(node) ? 0 : node.getParent().data.isProtected;
|
||||
}
|
||||
|
||||
function getNodeByKey(key) {
|
||||
@ -21,10 +22,15 @@ function getNoteIdFromNotePath(notePath) {
|
||||
return path[path.length - 1];
|
||||
}
|
||||
|
||||
function getNotePath(node) {
|
||||
async function getNotePath(node) {
|
||||
if (!node) {
|
||||
console.error("Node is null");
|
||||
return "";
|
||||
}
|
||||
|
||||
const path = [];
|
||||
|
||||
while (node && !utils.isRootNode(node)) {
|
||||
while (node && !await hoistedNoteService.isRootNode(node)) {
|
||||
if (node.data.noteId) {
|
||||
path.push(node.data.noteId);
|
||||
}
|
||||
@ -32,7 +38,7 @@ function getNotePath(node) {
|
||||
node = node.getParent();
|
||||
}
|
||||
|
||||
path.push('root');
|
||||
path.push(node.data.noteId); // root or hoisted noteId
|
||||
|
||||
return path.reverse().join("/");
|
||||
}
|
||||
|
@ -60,14 +60,6 @@ function assertArguments() {
|
||||
}
|
||||
}
|
||||
|
||||
function isTopLevelNode(node) {
|
||||
return isRootNode(node.getParent());
|
||||
}
|
||||
|
||||
function isRootNode(node) {
|
||||
return node.data.noteId === "root";
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return $('<div/>').text(str).html();
|
||||
}
|
||||
@ -211,8 +203,6 @@ export default {
|
||||
isElectron,
|
||||
isMac,
|
||||
assertArguments,
|
||||
isTopLevelNode,
|
||||
isRootNode,
|
||||
escapeHtml,
|
||||
stopWatch,
|
||||
formatValueWithWhitespace,
|
||||
|
@ -12,6 +12,7 @@ const Link = require('../entities/link');
|
||||
const NoteRevision = require('../entities/note_revision');
|
||||
const Branch = require('../entities/branch');
|
||||
const Attribute = require('../entities/attribute');
|
||||
const hoistedNoteService = require('../services/hoisted_note');
|
||||
|
||||
async function getNewNotePosition(parentNoteId, noteData) {
|
||||
let newNotePos = 0;
|
||||
@ -364,7 +365,10 @@ async function deleteNote(branch) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (branch.branchId === 'root' || branch.noteId === 'root') {
|
||||
if (branch.branchId === 'root'
|
||||
|| branch.noteId === 'root'
|
||||
|| branch.noteId === await hoistedNoteService.getHoistedNoteId()) {
|
||||
|
||||
throw new Error("Can't delete root branch/note");
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user