mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
cloning in context menu (copy & paste) and a lot of related refactoring and fixes
This commit is contained in:
parent
c1fca4764b
commit
acba72ec4c
@ -3,24 +3,51 @@
|
|||||||
const contextMenu = (function() {
|
const contextMenu = (function() {
|
||||||
const treeEl = $("#tree");
|
const treeEl = $("#tree");
|
||||||
|
|
||||||
|
let clipboardId = null;
|
||||||
|
let clipboardMode = null;
|
||||||
|
|
||||||
function pasteAfter(node) {
|
function pasteAfter(node) {
|
||||||
const subjectNode = treeUtils.getNodeByNoteTreeId(noteTree.getClipboardNoteTreeId());
|
if (clipboardMode === 'cut') {
|
||||||
|
const subjectNode = treeUtils.getNodeByNoteTreeId(clipboardId);
|
||||||
|
|
||||||
treeChanges.moveAfterNode(subjectNode, node);
|
treeChanges.moveAfterNode(subjectNode, node);
|
||||||
|
}
|
||||||
|
else if (clipboardMode === 'copy') {
|
||||||
|
treeChanges.cloneNoteAfter(clipboardId, node.data.note_tree_id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("Unrecognized clipboard mode=" + mode);
|
||||||
|
}
|
||||||
|
|
||||||
noteTree.setClipboardNoteTreeId(null);
|
clipboardId = null;
|
||||||
|
clipboardMode = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pasteInto(node) {
|
function pasteInto(node) {
|
||||||
const subjectNode = treeUtils.getNodeByNoteTreeId(noteTree.getClipboardNoteTreeId());
|
if (clipboardMode === 'cut') {
|
||||||
|
const subjectNode = treeUtils.getNodeByNoteTreeId(clipboardId);
|
||||||
|
|
||||||
treeChanges.moveToNode(subjectNode, node);
|
treeChanges.moveToNode(subjectNode, node);
|
||||||
|
}
|
||||||
|
else if (clipboardMode === 'copy') {
|
||||||
|
treeChanges.cloneNoteTo(clipboardId, node.data.note_id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("Unrecognized clipboard mode=" + mode);
|
||||||
|
}
|
||||||
|
|
||||||
noteTree.setClipboardNoteTreeId(null);
|
clipboardId = null;
|
||||||
|
clipboardMode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function copy(node) {
|
||||||
|
clipboardId = node.data.note_id;
|
||||||
|
clipboardMode = 'copy';
|
||||||
}
|
}
|
||||||
|
|
||||||
function cut(node) {
|
function cut(node) {
|
||||||
noteTree.setClipboardNoteTreeId(node.note_tree_id);
|
clipboardId = node.data.note_tree_id;
|
||||||
|
clipboardMode = 'cut';
|
||||||
}
|
}
|
||||||
|
|
||||||
const contextMenuSettings = {
|
const contextMenuSettings = {
|
||||||
@ -42,8 +69,8 @@ const contextMenu = (function() {
|
|||||||
beforeOpen: (event, ui) => {
|
beforeOpen: (event, ui) => {
|
||||||
const node = $.ui.fancytree.getNode(ui.target);
|
const node = $.ui.fancytree.getNode(ui.target);
|
||||||
// Modify menu entries depending on node status
|
// Modify menu entries depending on node status
|
||||||
treeEl.contextmenu("enableEntry", "pasteAfter", noteTree.getClipboardNoteTreeId() !== null);
|
treeEl.contextmenu("enableEntry", "pasteAfter", clipboardId !== null);
|
||||||
treeEl.contextmenu("enableEntry", "pasteInto", noteTree.getClipboardNoteTreeId() !== null);
|
treeEl.contextmenu("enableEntry", "pasteInto", clipboardId !== null);
|
||||||
|
|
||||||
// Activate node on right-click
|
// Activate node on right-click
|
||||||
node.setActive();
|
node.setActive();
|
||||||
@ -56,13 +83,13 @@ const contextMenu = (function() {
|
|||||||
const node = $.ui.fancytree.getNode(ui.target);
|
const node = $.ui.fancytree.getNode(ui.target);
|
||||||
|
|
||||||
if (ui.cmd === "insertNoteHere") {
|
if (ui.cmd === "insertNoteHere") {
|
||||||
const parentNoteTreeId = treeUtils.getParentNoteTreeId(node);
|
const parentNoteId = node.data.note_pid;
|
||||||
const isProtected = treeUtils.getParentProtectedStatus(node);
|
const isProtected = treeUtils.getParentProtectedStatus(node);
|
||||||
|
|
||||||
noteEditor.createNote(node, parentNoteTreeId, 'after', isProtected);
|
noteTree.createNote(node, parentNoteId, 'after', isProtected);
|
||||||
}
|
}
|
||||||
else if (ui.cmd === "insertChildNote") {
|
else if (ui.cmd === "insertChildNote") {
|
||||||
noteEditor.createNote(node, node.data.note_id, 'into');
|
noteTree.createNote(node, node.data.note_id, 'into');
|
||||||
}
|
}
|
||||||
else if (ui.cmd === "protectSubTree") {
|
else if (ui.cmd === "protectSubTree") {
|
||||||
protected_session.protectSubTree(node.data.note_id, true);
|
protected_session.protectSubTree(node.data.note_id, true);
|
||||||
@ -70,6 +97,9 @@ const contextMenu = (function() {
|
|||||||
else if (ui.cmd === "unprotectSubTree") {
|
else if (ui.cmd === "unprotectSubTree") {
|
||||||
protected_session.protectSubTree(node.data.note_id, false);
|
protected_session.protectSubTree(node.data.note_id, false);
|
||||||
}
|
}
|
||||||
|
else if (ui.cmd === "copy") {
|
||||||
|
copy(node);
|
||||||
|
}
|
||||||
else if (ui.cmd === "cut") {
|
else if (ui.cmd === "cut") {
|
||||||
cut(node);
|
cut(node);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,11 @@ const jumpToNote = (function() {
|
|||||||
return link.getNodePathFromLabel(val);
|
return link.getNodePathFromLabel(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSelectedNoteId() {
|
||||||
|
const notePath = getSelectedNotePath();
|
||||||
|
return treeUtils.getNoteIdFromNotePath(notePath);
|
||||||
|
}
|
||||||
|
|
||||||
function goToNote() {
|
function goToNote() {
|
||||||
const notePath = getSelectedNotePath();
|
const notePath = getSelectedNotePath();
|
||||||
|
|
||||||
@ -63,12 +68,12 @@ const jumpToNote = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (action === 'add-current-as-child') {
|
else if (action === 'add-current-as-child') {
|
||||||
treeUtils.addAsChild(getSelectedNotePath(), noteTree.getCurrentNotePath());
|
treeChanges.cloneNoteTo(noteTree.getCurrentNoteId(), getSelectedNoteId());
|
||||||
|
|
||||||
dialogEl.dialog("close");
|
dialogEl.dialog("close");
|
||||||
}
|
}
|
||||||
else if (action === 'add-selected-as-child') {
|
else if (action === 'add-selected-as-child') {
|
||||||
treeUtils.addAsChild(noteTree.getCurrentNotePath(), getSelectedNotePath());
|
treeChanges.cloneNoteTo(getSelectedNoteId(), noteTree.getCurrentNoteId());
|
||||||
|
|
||||||
dialogEl.dialog("close");
|
dialogEl.dialog("close");
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,11 @@ const recentNotes = (function() {
|
|||||||
return selectBoxEl.find("option:selected").val();
|
return selectBoxEl.find("option:selected").val();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSelectedNoteId() {
|
||||||
|
const notePath = getSelectedNotePath();
|
||||||
|
return treeUtils.getNoteIdFromNotePath(notePath);
|
||||||
|
}
|
||||||
|
|
||||||
function setActiveNoteBasedOnRecentNotes() {
|
function setActiveNoteBasedOnRecentNotes() {
|
||||||
const notePath = getSelectedNotePath();
|
const notePath = getSelectedNotePath();
|
||||||
|
|
||||||
@ -104,13 +109,13 @@ const recentNotes = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function addCurrentAsChild() {
|
async function addCurrentAsChild() {
|
||||||
await treeUtils.addAsChild(getSelectedNotePath(), noteTree.getCurrentNotePath());
|
await treeChanges.cloneNoteTo(noteTree.getCurrentNoteId(), getSelectedNoteId());
|
||||||
|
|
||||||
dialogEl.dialog("close");
|
dialogEl.dialog("close");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addRecentAsChild() {
|
async function addRecentAsChild() {
|
||||||
await treeUtils.addAsChild(noteTree.getCurrentNotePath(), getSelectedNotePath());
|
await treeChanges.cloneNoteTo(getSelectedNoteId(), noteTree.getCurrentNoteId());
|
||||||
|
|
||||||
dialogEl.dialog("close");
|
dialogEl.dialog("close");
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const noteEditor = (function() {
|
const noteEditor = (function() {
|
||||||
const treeEl = $("#tree");
|
|
||||||
const noteTitleEl = $("#note-title");
|
const noteTitleEl = $("#note-title");
|
||||||
const noteDetailEl = $('#note-detail');
|
const noteDetailEl = $('#note-detail');
|
||||||
const protectButton = $("#protect-button");
|
const protectButton = $("#protect-button");
|
||||||
@ -113,59 +112,6 @@ const noteEditor = (function() {
|
|||||||
showMessage("Saved!");
|
showMessage("Saved!");
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNewTopLevelNote() {
|
|
||||||
let rootNode = treeEl.fancytree("getRootNode");
|
|
||||||
|
|
||||||
createNote(rootNode, "root", "into");
|
|
||||||
}
|
|
||||||
|
|
||||||
let newNoteCreated = false;
|
|
||||||
|
|
||||||
async function createNote(node, parentTreeId, target, isProtected) {
|
|
||||||
// if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted
|
|
||||||
// but this is quite weird since user doesn't see WHERE the note is being created so it shouldn't occur often
|
|
||||||
if (!isProtected || !protected_session.isProtectedSessionAvailable()) {
|
|
||||||
isProtected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newNoteName = "new note";
|
|
||||||
|
|
||||||
const result = await $.ajax({
|
|
||||||
url: baseApiUrl + 'notes/' + parentTreeId + '/children' ,
|
|
||||||
type: 'POST',
|
|
||||||
data: JSON.stringify({
|
|
||||||
note_title: newNoteName,
|
|
||||||
target: target,
|
|
||||||
target_note_id: node.note_tree_id,
|
|
||||||
is_protected: isProtected
|
|
||||||
}),
|
|
||||||
contentType: "application/json"
|
|
||||||
});
|
|
||||||
|
|
||||||
const newNode = {
|
|
||||||
title: newNoteName,
|
|
||||||
key: counter++,
|
|
||||||
note_id: result.note_id,
|
|
||||||
note_tree_id: result.note_tree_id,
|
|
||||||
is_protected: isProtected,
|
|
||||||
extraClasses: isProtected ? "protected" : ""
|
|
||||||
};
|
|
||||||
|
|
||||||
newNoteCreated = true;
|
|
||||||
|
|
||||||
if (target === 'after') {
|
|
||||||
node.appendSibling(newNode).setActive(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
node.addChildren(newNode).setActive(true);
|
|
||||||
|
|
||||||
node.folder = true;
|
|
||||||
node.renderTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
showMessage("Created!");
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNoteBackgroundIfProtected(note) {
|
function setNoteBackgroundIfProtected(note) {
|
||||||
if (note.detail.is_protected) {
|
if (note.detail.is_protected) {
|
||||||
$(".note-editable").addClass("protected");
|
$(".note-editable").addClass("protected");
|
||||||
@ -184,8 +130,8 @@ const noteEditor = (function() {
|
|||||||
async function loadNoteToEditor(noteId) {
|
async function loadNoteToEditor(noteId) {
|
||||||
currentNote = await $.get(baseApiUrl + 'notes/' + noteId);
|
currentNote = await $.get(baseApiUrl + 'notes/' + noteId);
|
||||||
|
|
||||||
if (newNoteCreated) {
|
if (noteTree.isNewNoteCreated()) {
|
||||||
newNoteCreated = false;
|
noteTree.switchOffNewNoteCreated();
|
||||||
|
|
||||||
noteTitleEl.focus().select();
|
noteTitleEl.focus().select();
|
||||||
}
|
}
|
||||||
@ -249,8 +195,6 @@ const noteEditor = (function() {
|
|||||||
saveNoteIfChanged,
|
saveNoteIfChanged,
|
||||||
updateNoteFromInputs,
|
updateNoteFromInputs,
|
||||||
saveNoteToServer,
|
saveNoteToServer,
|
||||||
createNewTopLevelNote,
|
|
||||||
createNote,
|
|
||||||
setNoteBackgroundIfProtected,
|
setNoteBackgroundIfProtected,
|
||||||
loadNote,
|
loadNote,
|
||||||
getCurrentNote,
|
getCurrentNote,
|
||||||
|
@ -7,7 +7,6 @@ const noteTree = (function() {
|
|||||||
|
|
||||||
let startNoteTreeId = null;
|
let startNoteTreeId = null;
|
||||||
let treeLoadTime = null;
|
let treeLoadTime = null;
|
||||||
let clipboardNoteTreeId = null;
|
|
||||||
let notesMap = {};
|
let notesMap = {};
|
||||||
let parentToChildren = {};
|
let parentToChildren = {};
|
||||||
let childToParents = {};
|
let childToParents = {};
|
||||||
@ -30,14 +29,6 @@ const noteTree = (function() {
|
|||||||
return treeLoadTime;
|
return treeLoadTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getClipboardNoteTreeId() {
|
|
||||||
return clipboardNoteTreeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setClipboardNoteTreeId(cbNoteId) {
|
|
||||||
clipboardNoteTreeId = cbNoteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNoteTreeId(parentNoteId, childNoteId) {
|
function getNoteTreeId(parentNoteId, childNoteId) {
|
||||||
const key = parentNoteId + "-" + childNoteId;
|
const key = parentNoteId + "-" + childNoteId;
|
||||||
|
|
||||||
@ -107,41 +98,46 @@ const noteTree = (function() {
|
|||||||
for (const childNoteId of childNoteIds) {
|
for (const childNoteId of childNoteIds) {
|
||||||
const noteTreeId = getNoteTreeId(parentNoteId, childNoteId);
|
const noteTreeId = getNoteTreeId(parentNoteId, childNoteId);
|
||||||
const note = notesMap[noteTreeId];
|
const note = notesMap[noteTreeId];
|
||||||
|
const node = {};
|
||||||
|
|
||||||
note.title = noteIdToTitle[note.note_id];
|
node.note_id = note.note_id;
|
||||||
|
node.note_pid = note.note_pid;
|
||||||
|
node.note_tree_id = note.note_tree_id;
|
||||||
|
node.is_protected = note.is_protected;
|
||||||
|
node.title = noteIdToTitle[note.note_id];
|
||||||
|
|
||||||
note.extraClasses = "";
|
node.extraClasses = "";
|
||||||
|
|
||||||
if (note.is_protected) {
|
if (node.is_protected) {
|
||||||
note.extraClasses += ",protected";
|
node.extraClasses += ",protected";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (childToParents[childNoteId].length > 1) {
|
if (childToParents[childNoteId].length > 1) {
|
||||||
note.extraClasses += ",multiple-parents";
|
node.extraClasses += ",multiple-parents";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.extraClasses.startsWith(",")) {
|
if (node.extraClasses.startsWith(",")) {
|
||||||
note.extraClasses = note.extraClasses.substr(1);
|
node.extraClasses = node.extraClasses.substr(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
note.key = counter++ + ""; // key needs to be string
|
node.key = counter++ + ""; // key needs to be string
|
||||||
note.refKey = note.note_id;
|
node.refKey = note.note_id;
|
||||||
note.expanded = note.is_expanded;
|
node.expanded = note.is_expanded;
|
||||||
|
|
||||||
noteTreeIdToKey[noteTreeId] = note.key;
|
noteTreeIdToKey[noteTreeId] = node.key;
|
||||||
|
|
||||||
if (parentToChildren[note.note_id] && parentToChildren[note.note_id].length > 0) {
|
if (parentToChildren[note.note_id] && parentToChildren[note.note_id].length > 0) {
|
||||||
note.folder = true;
|
node.folder = true;
|
||||||
|
|
||||||
if (note.expanded) {
|
if (node.expanded) {
|
||||||
note.children = prepareNoteTreeInner(note.note_id);
|
node.children = prepareNoteTreeInner(note.note_id);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
note.lazy = true;
|
node.lazy = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
noteList.push(note);
|
noteList.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
return noteList;
|
return noteList;
|
||||||
@ -211,6 +207,10 @@ const noteTree = (function() {
|
|||||||
function showParentList(noteId, node) {
|
function showParentList(noteId, node) {
|
||||||
const parents = childToParents[noteId];
|
const parents = childToParents[noteId];
|
||||||
|
|
||||||
|
if (!parents) {
|
||||||
|
throw new Error("Can't find parents for noteId=" + noteId);
|
||||||
|
}
|
||||||
|
|
||||||
if (parents.length <= 1) {
|
if (parents.length <= 1) {
|
||||||
parentListEl.hide();
|
parentListEl.hide();
|
||||||
}
|
}
|
||||||
@ -284,15 +284,17 @@ const noteTree = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function initFancyTree(noteTree) {
|
function initFancyTree(noteTree) {
|
||||||
|
console.log(noteTree);
|
||||||
|
|
||||||
const keybindings = {
|
const keybindings = {
|
||||||
"insert": node => {
|
"insert": node => {
|
||||||
const parentNoteTreeId = treeUtils.getParentNoteTreeId(node);
|
const parentNoteId = node.data.note_pid;
|
||||||
const isProtected = treeUtils.getParentProtectedStatus(node);
|
const isProtected = treeUtils.getParentProtectedStatus(node);
|
||||||
|
|
||||||
noteEditor.createNote(node, parentNoteTreeId, 'after', isProtected);
|
createNote(node, parentNoteId, 'after', isProtected);
|
||||||
},
|
},
|
||||||
"ctrl+insert": node => {
|
"ctrl+insert": node => {
|
||||||
noteEditor.createNote(node, node.note_id, 'into', node.data.is_protected);
|
createNote(node, node.data.note_id, 'into', node.data.is_protected);
|
||||||
},
|
},
|
||||||
"del": node => {
|
"del": node => {
|
||||||
treeChanges.deleteNode(node);
|
treeChanges.deleteNode(node);
|
||||||
@ -498,10 +500,6 @@ const noteTree = (function() {
|
|||||||
tree.clearFilter();
|
tree.clearFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getByNoteTreeId(noteTreeId) {
|
|
||||||
return notesMap[noteTreeId];
|
|
||||||
}
|
|
||||||
|
|
||||||
// note that if you want to access data like note_id or is_protected, you need to go into "data" property
|
// note that if you want to access data like note_id or is_protected, you need to go into "data" property
|
||||||
function getCurrentNode() {
|
function getCurrentNode() {
|
||||||
return treeEl.fancytree("getActiveNode");
|
return treeEl.fancytree("getActiveNode");
|
||||||
@ -587,6 +585,74 @@ const noteTree = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createNewTopLevelNote() {
|
||||||
|
let rootNode = treeEl.fancytree("getRootNode");
|
||||||
|
|
||||||
|
createNote(rootNode, "root", "into");
|
||||||
|
}
|
||||||
|
|
||||||
|
let newNoteCreated = false;
|
||||||
|
|
||||||
|
function isNewNoteCreated() {
|
||||||
|
return newNoteCreated;
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchOffNewNoteCreated() {
|
||||||
|
newNoteCreated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createNote(node, parentNoteId, target, isProtected) {
|
||||||
|
// if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted
|
||||||
|
// but this is quite weird since user doesn't see WHERE the note is being created so it shouldn't occur often
|
||||||
|
if (!isProtected || !protected_session.isProtectedSessionAvailable()) {
|
||||||
|
isProtected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newNoteName = "new note";
|
||||||
|
|
||||||
|
const result = await $.ajax({
|
||||||
|
url: baseApiUrl + 'notes/' + parentNoteId + '/children' ,
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify({
|
||||||
|
note_title: newNoteName,
|
||||||
|
target: target,
|
||||||
|
target_note_tree_id: node.data.note_tree_id,
|
||||||
|
is_protected: isProtected
|
||||||
|
}),
|
||||||
|
contentType: "application/json"
|
||||||
|
});
|
||||||
|
|
||||||
|
const newNode = {
|
||||||
|
title: newNoteName,
|
||||||
|
note_id: result.note_id,
|
||||||
|
note_pid: parentNoteId,
|
||||||
|
refKey: result.note_id,
|
||||||
|
note_tree_id: result.note_tree_id,
|
||||||
|
is_protected: isProtected,
|
||||||
|
extraClasses: isProtected ? "protected" : ""
|
||||||
|
};
|
||||||
|
|
||||||
|
parentToChildren[parentNoteId].push(result.note_id);
|
||||||
|
parentToChildren[result.note_id] = [];
|
||||||
|
childToParents[result.note_id] = [ parentNoteId ];
|
||||||
|
|
||||||
|
noteIdToTitle[result.note_id] = newNoteName;
|
||||||
|
|
||||||
|
newNoteCreated = true;
|
||||||
|
|
||||||
|
if (target === 'after') {
|
||||||
|
node.appendSibling(newNode).setActive(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
node.addChildren(newNode).setActive(true);
|
||||||
|
|
||||||
|
node.folder = true;
|
||||||
|
node.renderTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
showMessage("Created!");
|
||||||
|
}
|
||||||
|
|
||||||
$("button#reset-search-button").click(resetSearch);
|
$("button#reset-search-button").click(resetSearch);
|
||||||
|
|
||||||
$("input[name=search]").keyup(e => {
|
$("input[name=search]").keyup(e => {
|
||||||
@ -613,13 +679,10 @@ const noteTree = (function() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
getTreeLoadTime,
|
getTreeLoadTime,
|
||||||
getClipboardNoteTreeId,
|
|
||||||
setClipboardNoteTreeId,
|
|
||||||
reload,
|
reload,
|
||||||
collapseTree,
|
collapseTree,
|
||||||
scrollToCurrentNote,
|
scrollToCurrentNote,
|
||||||
toggleSearch,
|
toggleSearch,
|
||||||
getByNoteTreeId,
|
|
||||||
getKeyFromNoteTreeId,
|
getKeyFromNoteTreeId,
|
||||||
getNoteTreeIdFromKey,
|
getNoteTreeIdFromKey,
|
||||||
setCurrentNoteTreeBasedOnProtectedStatus,
|
setCurrentNoteTreeBasedOnProtectedStatus,
|
||||||
@ -630,6 +693,10 @@ const noteTree = (function() {
|
|||||||
getNoteTitle,
|
getNoteTitle,
|
||||||
setCurrentNotePathToHash,
|
setCurrentNotePathToHash,
|
||||||
getAutocompleteItems,
|
getAutocompleteItems,
|
||||||
setCurrentNoteTitle
|
setCurrentNoteTitle,
|
||||||
|
createNewTopLevelNote,
|
||||||
|
createNote,
|
||||||
|
isNewNoteCreated,
|
||||||
|
switchOffNewNoteCreated
|
||||||
};
|
};
|
||||||
})();
|
})();
|
@ -25,6 +25,17 @@ const treeChanges = (function() {
|
|||||||
noteTree.setCurrentNotePathToHash(node);
|
noteTree.setCurrentNotePathToHash(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// beware that first arg is noteId and second is noteTreeId!
|
||||||
|
async function cloneNoteAfter(noteId, afterNoteTreeId) {
|
||||||
|
await $.ajax({
|
||||||
|
url: baseApiUrl + 'notes/' + noteId + '/cloneAfter/' + afterNoteTreeId,
|
||||||
|
type: 'PUT',
|
||||||
|
error: () => showError("Error cloning note.")
|
||||||
|
});
|
||||||
|
|
||||||
|
await noteTree.reload();
|
||||||
|
}
|
||||||
|
|
||||||
async function moveToNode(node, toNode) {
|
async function moveToNode(node, toNode) {
|
||||||
await $.ajax({
|
await $.ajax({
|
||||||
url: baseApiUrl + 'notes/' + node.data.note_tree_id + '/moveTo/' + toNode.data.note_id,
|
url: baseApiUrl + 'notes/' + node.data.note_tree_id + '/moveTo/' + toNode.data.note_id,
|
||||||
@ -42,6 +53,16 @@ const treeChanges = (function() {
|
|||||||
noteTree.setCurrentNotePathToHash(node);
|
noteTree.setCurrentNotePathToHash(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function cloneNoteTo(childNoteId, parentNoteId) {
|
||||||
|
await $.ajax({
|
||||||
|
url: baseApiUrl + 'notes/' + childNoteId + '/cloneTo/' + parentNoteId,
|
||||||
|
type: 'PUT',
|
||||||
|
error: () => showError("Error cloning note.")
|
||||||
|
});
|
||||||
|
|
||||||
|
await noteTree.reload();
|
||||||
|
}
|
||||||
|
|
||||||
async function deleteNode(node) {
|
async function deleteNode(node) {
|
||||||
if (!confirm('Are you sure you want to delete note "' + node.title + '"?')) {
|
if (!confirm('Are you sure you want to delete note "' + node.title + '"?')) {
|
||||||
return;
|
return;
|
||||||
@ -97,6 +118,8 @@ const treeChanges = (function() {
|
|||||||
moveAfterNode,
|
moveAfterNode,
|
||||||
moveToNode,
|
moveToNode,
|
||||||
deleteNode,
|
deleteNode,
|
||||||
moveNodeUp
|
moveNodeUp,
|
||||||
|
cloneNoteAfter,
|
||||||
|
cloneNoteTo
|
||||||
};
|
};
|
||||||
})();
|
})();
|
@ -3,10 +3,6 @@
|
|||||||
const treeUtils = (function() {
|
const treeUtils = (function() {
|
||||||
const treeEl = $("#tree");
|
const treeEl = $("#tree");
|
||||||
|
|
||||||
function getParentNoteTreeId(node) {
|
|
||||||
return node.note_pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getParentProtectedStatus(node) {
|
function getParentProtectedStatus(node) {
|
||||||
return node.getParent() === null ? 0 : node.getParent().data.is_protected;
|
return node.getParent() === null ? 0 : node.getParent().data.is_protected;
|
||||||
}
|
}
|
||||||
@ -34,24 +30,6 @@ const treeUtils = (function() {
|
|||||||
return titlePath.join(" > ");
|
return titlePath.join(" > ");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFullName(noteTreeId) {
|
|
||||||
let note = noteTree.getByNoteTreeId(noteTreeId);
|
|
||||||
|
|
||||||
if (note === null) {
|
|
||||||
return "[unknown]";
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = [];
|
|
||||||
|
|
||||||
while (note) {
|
|
||||||
path.push(note.note_title);
|
|
||||||
|
|
||||||
note = noteTree.getByNoteTreeId(note.note_pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.reverse().join(" > ");
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNotePath(node) {
|
function getNotePath(node) {
|
||||||
const path = [];
|
const path = [];
|
||||||
|
|
||||||
@ -66,28 +44,12 @@ const treeUtils = (function() {
|
|||||||
return path.reverse().join("/");
|
return path.reverse().join("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addAsChild(parentNotePath, childNotePath) {
|
|
||||||
const parentNoteId = treeUtils.getNoteIdFromNotePath(parentNotePath);
|
|
||||||
const childNoteId = treeUtils.getNoteIdFromNotePath(childNotePath);
|
|
||||||
|
|
||||||
await $.ajax({
|
|
||||||
url: baseApiUrl + 'tree/' + parentNoteId + '/addChild/' + childNoteId,
|
|
||||||
type: 'PUT',
|
|
||||||
error: () => showError("Error adding child.")
|
|
||||||
});
|
|
||||||
|
|
||||||
await noteTree.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getParentNoteTreeId,
|
|
||||||
getParentProtectedStatus,
|
getParentProtectedStatus,
|
||||||
getNodeByKey,
|
getNodeByKey,
|
||||||
getNodeByNoteTreeId,
|
getNodeByNoteTreeId,
|
||||||
getFullName,
|
|
||||||
getFullNameForPath,
|
getFullNameForPath,
|
||||||
getNotePath,
|
getNotePath,
|
||||||
getNoteIdFromNotePath,
|
getNoteIdFromNotePath
|
||||||
addAsChild
|
|
||||||
};
|
};
|
||||||
})();
|
})();
|
@ -29,12 +29,12 @@ router.get('/:noteId', auth.checkApiAuth, async (req, res, next) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/:parentNoteTreeId/children', async (req, res, next) => {
|
router.post('/:parentNoteId/children', async (req, res, next) => {
|
||||||
const parentNoteTreeId = req.params.parentNoteTreeId;
|
const parentNoteId = req.params.parentNoteId;
|
||||||
const browserId = utils.browserId(req);
|
const browserId = utils.browserId(req);
|
||||||
const note = req.body;
|
const note = req.body;
|
||||||
|
|
||||||
const { noteId, noteTreeId } = await notes.createNewNote(parentNoteTreeId, note, browserId);
|
const { noteId, noteTreeId } = await notes.createNewNote(parentNoteId, note, browserId);
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
'note_id': noteId,
|
'note_id': noteId,
|
||||||
|
@ -86,6 +86,75 @@ router.put('/:noteTreeId/moveAfter/:afterNoteTreeId', async (req, res, next) =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.put('/:childNoteId/cloneTo/:parentNoteId', auth.checkApiAuth, async (req, res, next) => {
|
||||||
|
const parentNoteId = req.params.parentNoteId;
|
||||||
|
const childNoteId = req.params.childNoteId;
|
||||||
|
|
||||||
|
const existing = await sql.getSingleValue('SELECT * FROM notes_tree WHERE note_id = ? AND note_pid = ?', [childNoteId, parentNoteId]);
|
||||||
|
|
||||||
|
if (!existing) {
|
||||||
|
const maxNotePos = await sql.getSingleValue('SELECT MAX(note_pos) FROM notes_tree WHERE note_pid = ? AND is_deleted = 0', [parentNoteId]);
|
||||||
|
const newNotePos = maxNotePos === null ? 0 : maxNotePos + 1;
|
||||||
|
|
||||||
|
const noteTreeId = utils.newNoteTreeId();
|
||||||
|
|
||||||
|
await sql.doInTransaction(async () => {
|
||||||
|
await sync_table.addNoteTreeSync(noteTreeId);
|
||||||
|
|
||||||
|
await sql.insert("notes_tree", {
|
||||||
|
'note_tree_id': noteTreeId,
|
||||||
|
'note_id': childNoteId,
|
||||||
|
'note_pid': parentNoteId,
|
||||||
|
'note_pos': newNotePos,
|
||||||
|
'is_expanded': 0,
|
||||||
|
'date_modified': utils.nowTimestamp(),
|
||||||
|
'is_deleted': 0
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (existing && existing.is_deleted) {
|
||||||
|
await sql.execute("UPDATE notes_tree SET is_deleted = 0 WHERE note_tree_id = ?", [existing.note_tree_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send({});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.put('/:noteId/cloneAfter/:afterNoteTreeId', async (req, res, next) => {
|
||||||
|
const noteId = req.params.noteId;
|
||||||
|
const afterNoteTreeId = req.params.afterNoteTreeId;
|
||||||
|
|
||||||
|
const afterNote = await sql.getSingleResult("SELECT * FROM notes_tree WHERE note_tree_id = ?", [afterNoteTreeId]);
|
||||||
|
|
||||||
|
if (afterNote) {
|
||||||
|
await sql.doInTransaction(async () => {
|
||||||
|
// we don't change date_modified so other changes are prioritized in case of conflict
|
||||||
|
await sql.execute("UPDATE notes_tree SET note_pos = note_pos + 1 WHERE note_pid = ? AND note_pos > ? AND is_deleted = 0",
|
||||||
|
[afterNote.note_pid, afterNote.note_pos]);
|
||||||
|
|
||||||
|
const noteTreeId = utils.newNoteTreeId();
|
||||||
|
|
||||||
|
await sql.insert("notes_tree", {
|
||||||
|
'note_tree_id': noteTreeId,
|
||||||
|
'note_id': noteId,
|
||||||
|
'note_pid': afterNote.note_pid,
|
||||||
|
'note_pos': afterNote.note_pos + 1,
|
||||||
|
'is_expanded': 0,
|
||||||
|
'date_modified': utils.nowTimestamp(),
|
||||||
|
'is_deleted': 0
|
||||||
|
});
|
||||||
|
|
||||||
|
await sync_table.addNoteTreeSync(noteTreeId);
|
||||||
|
await sync_table.addNoteReorderingSync(afterNote.note_pid);
|
||||||
|
await sql.addAudit(audit_category.CHANGE_POSITION, utils.browserId(req), afterNote.note_pid);
|
||||||
|
});
|
||||||
|
|
||||||
|
res.send({});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.status(500).send("After note " + afterNoteTreeId + " doesn't exist.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
router.put('/:noteTreeId/expanded/:expanded', async (req, res, next) => {
|
router.put('/:noteTreeId/expanded/:expanded', async (req, res, next) => {
|
||||||
const noteTreeId = req.params.noteTreeId;
|
const noteTreeId = req.params.noteTreeId;
|
||||||
const expanded = req.params.expanded;
|
const expanded = req.params.expanded;
|
||||||
|
@ -48,37 +48,4 @@ router.put('/:noteId/protectSubTree/:isProtected', auth.checkApiAuth, async (req
|
|||||||
res.send({});
|
res.send({});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/:parentNoteId/addChild/:childNoteId', auth.checkApiAuth, async (req, res, next) => {
|
|
||||||
const parentNoteId = req.params.parentNoteId;
|
|
||||||
const childNoteId = req.params.childNoteId;
|
|
||||||
|
|
||||||
const existing = await sql.getSingleValue('SELECT * FROM notes_tree WHERE note_id = ? AND note_pid = ?', [childNoteId, parentNoteId]);
|
|
||||||
|
|
||||||
if (!existing) {
|
|
||||||
const maxNotePos = await sql.getSingleValue('SELECT MAX(note_pos) FROM notes_tree WHERE note_pid = ? AND is_deleted = 0', [parentNoteId]);
|
|
||||||
const newNotePos = maxNotePos === null ? 0 : maxNotePos + 1;
|
|
||||||
|
|
||||||
const noteTreeId = utils.newNoteTreeId();
|
|
||||||
|
|
||||||
await sql.doInTransaction(async () => {
|
|
||||||
await sync_table.addNoteTreeSync(noteTreeId);
|
|
||||||
|
|
||||||
await sql.insert("notes_tree", {
|
|
||||||
'note_tree_id': noteTreeId,
|
|
||||||
'note_id': childNoteId,
|
|
||||||
'note_pid': parentNoteId,
|
|
||||||
'note_pos': newNotePos,
|
|
||||||
'is_expanded': 0,
|
|
||||||
'date_modified': utils.nowTimestamp(),
|
|
||||||
'is_deleted': 0
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (existing && existing.is_deleted) {
|
|
||||||
await sql.execute("UPDATE notes_tree SET is_deleted = 0 WHERE note_tree_id = ?", [existing.note_tree_id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send({});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
@ -18,7 +18,7 @@ async function createNewNote(parentNoteId, note, browserId) {
|
|||||||
newNotePos = maxNotePos === null ? 0 : maxNotePos + 1;
|
newNotePos = maxNotePos === null ? 0 : maxNotePos + 1;
|
||||||
}
|
}
|
||||||
else if (note.target === 'after') {
|
else if (note.target === 'after') {
|
||||||
const afterNote = await sql.getSingleResult('SELECT note_pos FROM notes_tree WHERE note_id = ?', [note.target_note_id]);
|
const afterNote = await sql.getSingleResult('SELECT note_pos FROM notes_tree WHERE note_tree_id = ?', [note.target_note_tree_id]);
|
||||||
|
|
||||||
newNotePos = afterNote.note_pos + 1;
|
newNotePos = afterNote.note_pos + 1;
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hide-toggle" style="grid-area: tree-actions">
|
<div class="hide-toggle" style="grid-area: tree-actions">
|
||||||
<a onclick="noteEditor.createNewTopLevelNote()" title="Create new top level note" class="icon-action">
|
<a onclick="noteTree.createNewTopLevelNote()" title="Create new top level note" class="icon-action">
|
||||||
<img src="images/icons/file-plus.png" alt="Create new top level note"/>
|
<img src="images/icons/file-plus.png" alt="Create new top level note"/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user