don't allow deleting hoisted note + other related fixes, closes #488

This commit is contained in:
zadam 2019-04-16 21:40:04 +02:00
parent 3760835608
commit 064a11d872
12 changed files with 66 additions and 54 deletions

View File

@ -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 });
}

View File

@ -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,

View File

@ -42,7 +42,7 @@ function initContextMenu(event, contextMenuItems, selectContextMenuItem) {
});
if (item.enabled !== undefined && !item.enabled) {
$link.addClass("disabled");
$item.addClass("disabled");
}
if (item.items) {

View File

@ -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;

View File

@ -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
}

View File

@ -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>', {

View File

@ -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);

View File

@ -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', {

View File

@ -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);
}
},

View File

@ -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("/");
}

View File

@ -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,

View File

@ -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");
}