mirror of
https://github.com/zadam/trilium.git
synced 2024-09-20 15:45:58 +08:00
notes_tree now has note_tree_id so we stricly distinguish between working on notes or note trees
This commit is contained in:
parent
dec9cad106
commit
5fb94fcbbd
21
migrations/0037__add_note_tree_id.sql
Normal file
21
migrations/0037__add_note_tree_id.sql
Normal file
|
@ -0,0 +1,21 @@
|
|||
CREATE TABLE [notes_tree_mig] (
|
||||
[note_tree_id] VARCHAR(30) PRIMARY KEY NOT NULL,
|
||||
[note_id] VARCHAR(30) UNIQUE NOT NULL,
|
||||
[note_pid] VARCHAR(30) NOT NULL,
|
||||
[note_pos] INTEGER NOT NULL,
|
||||
[is_expanded] BOOLEAN NULL ,
|
||||
date_modified INTEGER NOT NULL DEFAULT 0,
|
||||
is_deleted INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
INSERT INTO notes_tree_mig (note_tree_id, note_id, note_pid, note_pos, is_expanded, date_modified, is_deleted)
|
||||
SELECT 'TT' || SUBSTR(note_id, 3), note_id, note_pid, note_pos, is_expanded, date_modified, is_deleted FROM notes_tree;
|
||||
|
||||
UPDATE notes_tree_mig SET note_pid = 'TT' || SUBSTR(note_pid, 3) WHERE note_pid != 'root';
|
||||
|
||||
DROP TABLE notes_tree;
|
||||
ALTER TABLE notes_tree_mig RENAME TO notes_tree;
|
||||
|
||||
CREATE INDEX `IDX_notes_tree_note_id` ON `notes_tree` (
|
||||
`note_tree_id`
|
||||
);
|
1
migrations/0038__rename_start_node.sql
Normal file
1
migrations/0038__rename_start_node.sql
Normal file
|
@ -0,0 +1 @@
|
|||
UPDATE options SET opt_name = 'start_note_tree_id' WHERE opt_name = 'start_node';
|
|
@ -0,0 +1,7 @@
|
|||
DROP TABLE recent_notes;
|
||||
|
||||
CREATE TABLE `recent_notes` (
|
||||
`note_tree_id` TEXT NOT NULL PRIMARY KEY,
|
||||
`date_accessed` INTEGER NOT NULL ,
|
||||
is_deleted INT
|
||||
);
|
|
@ -4,23 +4,23 @@ const contextMenu = (function() {
|
|||
const treeEl = $("#tree");
|
||||
|
||||
function pasteAfter(node) {
|
||||
const subjectNode = treeUtils.getNodeByKey(noteTree.getClipboardNoteId());
|
||||
const subjectNode = treeUtils.getNodeByNoteTreeId(noteTree.getClipboardNoteTreeId());
|
||||
|
||||
treeChanges.moveAfterNode(subjectNode, node);
|
||||
|
||||
noteTree.setClipboardNoteId(null);
|
||||
noteTree.setClipboardNoteTreeId(null);
|
||||
}
|
||||
|
||||
function pasteInto(node) {
|
||||
const subjectNode = treeUtils.getNodeByKey(noteTree.getClipboardNoteId());
|
||||
const subjectNode = treeUtils.getNodeByNoteTreeId(noteTree.getClipboardNoteTreeId());
|
||||
|
||||
treeChanges.moveToNode(subjectNode, node);
|
||||
|
||||
noteTree.setClipboardNoteId(null);
|
||||
noteTree.setClipboardNoteTreeId(null);
|
||||
}
|
||||
|
||||
function cut(node) {
|
||||
noteTree.setClipboardNoteId(node.key);
|
||||
noteTree.setClipboardNoteTreeId(node.note_tree_id);
|
||||
}
|
||||
|
||||
const contextMenuSettings = {
|
||||
|
@ -42,8 +42,8 @@ const contextMenu = (function() {
|
|||
beforeOpen: (event, ui) => {
|
||||
const node = $.ui.fancytree.getNode(ui.target);
|
||||
// Modify menu entries depending on node status
|
||||
treeEl.contextmenu("enableEntry", "pasteAfter", noteTree.getClipboardNoteId() !== null);
|
||||
treeEl.contextmenu("enableEntry", "pasteInto", noteTree.getClipboardNoteId() !== null);
|
||||
treeEl.contextmenu("enableEntry", "pasteAfter", noteTree.getClipboardNoteTreeId() !== null);
|
||||
treeEl.contextmenu("enableEntry", "pasteInto", noteTree.getClipboardNoteTreeId() !== null);
|
||||
|
||||
treeEl.contextmenu("enableEntry", "protectSubTree", protected_session.isProtectedSessionAvailable());
|
||||
treeEl.contextmenu("enableEntry", "unprotectSubTree", protected_session.isProtectedSessionAvailable());
|
||||
|
@ -59,10 +59,10 @@ const contextMenu = (function() {
|
|||
const node = $.ui.fancytree.getNode(ui.target);
|
||||
|
||||
if (ui.cmd === "insertNoteHere") {
|
||||
const parentKey = treeUtils.getParentKey(node);
|
||||
const parentNoteTreeId = treeUtils.getParentNoteTreeId(node);
|
||||
const isProtected = treeUtils.getParentProtectedStatus(node);
|
||||
|
||||
noteEditor.createNote(node, parentKey, 'after', isProtected);
|
||||
noteEditor.createNote(node, parentNoteTreeId, 'after', isProtected);
|
||||
}
|
||||
else if (ui.cmd === "insertChildNote") {
|
||||
noteEditor.createNote(node, node.key, 'into');
|
||||
|
|
|
@ -13,31 +13,31 @@ const recentNotes = (function() {
|
|||
type: 'GET',
|
||||
error: () => showError("Error getting recent notes.")
|
||||
}).then(result => {
|
||||
list = result.map(r => r.note_id);
|
||||
list = result.map(r => r.note_tree_id);
|
||||
});
|
||||
|
||||
function addRecentNote(noteTreeId, noteContentId) {
|
||||
function addRecentNote(noteTreeId) {
|
||||
setTimeout(() => {
|
||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||
if (noteTreeId === noteEditor.getCurrentNoteId() || noteContentId === noteEditor.getCurrentNoteId()) {
|
||||
if (noteTreeId === noteEditor.getCurrentNoteId()) {
|
||||
$.ajax({
|
||||
url: baseApiUrl + 'recent-notes/' + noteTreeId,
|
||||
type: 'PUT',
|
||||
error: () => showError("Error setting recent notes.")
|
||||
}).then(result => {
|
||||
list = result.map(r => r.note_id);
|
||||
list = result.map(r => r.note_tree_id);
|
||||
});
|
||||
}
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
function removeRecentNote(noteIdToRemove) {
|
||||
function removeRecentNote(noteTreeIdToRemove) {
|
||||
$.ajax({
|
||||
url: baseApiUrl + 'recent-notes/' + noteIdToRemove,
|
||||
url: baseApiUrl + 'recent-notes/' + noteTreeIdToRemove,
|
||||
type: 'DELETE',
|
||||
error: () => showError("Error removing note from recent notes.")
|
||||
}).then(result => {
|
||||
list = result.map(r => r.note_id);
|
||||
list = result.map(r => r.note_tree_id);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -56,15 +56,15 @@ const recentNotes = (function() {
|
|||
// remove the current note
|
||||
const recNotes = list.filter(note => note !== noteEditor.getCurrentNoteId());
|
||||
|
||||
$.each(recNotes, (key, valueNoteId) => {
|
||||
const noteTitle = treeUtils.getFullName(valueNoteId);
|
||||
$.each(recNotes, (key, valueNoteTreeId) => {
|
||||
const noteTitle = treeUtils.getFullName(valueNoteTreeId);
|
||||
|
||||
if (!noteTitle) {
|
||||
return;
|
||||
}
|
||||
|
||||
const option = $("<option></option>")
|
||||
.attr("value", valueNoteId)
|
||||
.attr("value", valueNoteTreeId)
|
||||
.text(noteTitle);
|
||||
|
||||
// select the first one (most recent one) by default
|
||||
|
|
|
@ -92,7 +92,7 @@ const noteEditor = (function() {
|
|||
|
||||
const title = noteTitleEl.val();
|
||||
|
||||
treeUtils.getNodeByKey(note.detail.note_id).setTitle(title);
|
||||
noteTree.getCurrentNode().setTitle(title);
|
||||
|
||||
note.detail.note_title = title;
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ const noteEditor = (function() {
|
|||
|
||||
let newNoteCreated = false;
|
||||
|
||||
async function createNote(node, parentKey, target, isProtected) {
|
||||
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()) {
|
||||
|
@ -131,12 +131,12 @@ const noteEditor = (function() {
|
|||
const newNoteName = "new note";
|
||||
|
||||
const result = await $.ajax({
|
||||
url: baseApiUrl + 'notes/' + parentKey + '/children' ,
|
||||
url: baseApiUrl + 'notes/' + parentTreeId + '/children' ,
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
note_title: newNoteName,
|
||||
target: target,
|
||||
target_note_id: node.key,
|
||||
target_note_id: node.note_tree_id,
|
||||
is_protected: isProtected
|
||||
}),
|
||||
contentType: "application/json"
|
||||
|
@ -144,13 +144,14 @@ const noteEditor = (function() {
|
|||
|
||||
const newNode = {
|
||||
title: newNoteName,
|
||||
key: result.note_id,
|
||||
key: counter++,
|
||||
note_id: result.note_id,
|
||||
note_tree_id: result.note_tree_id,
|
||||
is_protected: isProtected,
|
||||
extraClasses: isProtected ? "protected" : ""
|
||||
};
|
||||
|
||||
glob.allNoteIds.push(result.note_id);
|
||||
glob.allNoteIds.push(result.note_tree_id);
|
||||
|
||||
newNoteCreated = true;
|
||||
|
||||
|
@ -167,11 +168,6 @@ const noteEditor = (function() {
|
|||
showMessage("Created!");
|
||||
}
|
||||
|
||||
function setTreeBasedOnProtectedStatus(note) {
|
||||
const node = treeUtils.getNodeByKey(note.detail.note_id);
|
||||
node.toggleClass("protected", !!note.detail.is_protected);
|
||||
}
|
||||
|
||||
function setNoteBackgroundIfProtected(note) {
|
||||
if (note.detail.is_protected) {
|
||||
$(".note-editable").addClass("protected");
|
||||
|
@ -184,7 +180,7 @@ const noteEditor = (function() {
|
|||
unprotectButton.hide();
|
||||
}
|
||||
|
||||
setTreeBasedOnProtectedStatus(note);
|
||||
noteTree.setCurrentNoteTreeBasedOnProtectedStatus();
|
||||
}
|
||||
|
||||
async function loadNoteToEditor(noteId) {
|
||||
|
@ -217,10 +213,6 @@ const noteEditor = (function() {
|
|||
|
||||
noteDetailEl.summernote('code', currentNote.detail.note_text);
|
||||
|
||||
document.location.hash = noteId;
|
||||
|
||||
recentNotes.addRecentNote(noteId, currentNote.detail.note_id);
|
||||
|
||||
noteChangeDisabled = false;
|
||||
|
||||
setNoteBackgroundIfProtected(currentNote);
|
||||
|
|
|
@ -3,35 +3,59 @@
|
|||
const noteTree = (function() {
|
||||
const noteDetailEl = $('#note-detail');
|
||||
const treeEl = $("#tree");
|
||||
let startNoteId = null;
|
||||
let startNoteTreeId = null;
|
||||
let treeLoadTime = null;
|
||||
let clipboardNoteId = null;
|
||||
let clipboardNoteTreeId = null;
|
||||
let notesMap = {};
|
||||
let parentToNotes = {};
|
||||
let counter = 1;
|
||||
let noteTreeIdToKey = {};
|
||||
|
||||
function getNoteTreeIdFromKey(key) {
|
||||
const node = treeUtils.getNodeByKey(key);
|
||||
|
||||
return node.note_tree_id;
|
||||
}
|
||||
|
||||
function getKeyFromNoteTreeId(noteTreeId) {
|
||||
return noteTreeIdToKey[noteTreeId];
|
||||
}
|
||||
|
||||
function getTreeLoadTime() {
|
||||
return treeLoadTime;
|
||||
}
|
||||
|
||||
function getClipboardNoteId() {
|
||||
return clipboardNoteId;
|
||||
function getClipboardNoteTreeId() {
|
||||
return clipboardNoteTreeId;
|
||||
}
|
||||
|
||||
function setClipboardNoteId(cbNoteId) {
|
||||
clipboardNoteId = cbNoteId;
|
||||
function setClipboardNoteTreeId(cbNoteId) {
|
||||
clipboardNoteTreeId = cbNoteId;
|
||||
}
|
||||
|
||||
function prepareNoteTree() {
|
||||
function prepareNoteTree(notes) { showAppIfHidden();
|
||||
parentToNotes = {};
|
||||
notesMap = {};
|
||||
|
||||
for (const note of notes) {
|
||||
if (!parentToNotes[note.note_pid]) {
|
||||
parentToNotes[note.note_pid] = [];
|
||||
}
|
||||
|
||||
notesMap[note.note_tree_id] = note;
|
||||
parentToNotes[note.note_pid].push(note.note_tree_id);
|
||||
}
|
||||
|
||||
glob.allNoteIds = Object.keys(notesMap);
|
||||
|
||||
return prepareNoteTreeInner(parentToNotes['root']);
|
||||
}
|
||||
|
||||
function prepareNoteTreeInner(noteIds) {
|
||||
function prepareNoteTreeInner(noteTreeIds) {
|
||||
const noteList = [];
|
||||
|
||||
for (const noteId of noteIds) {
|
||||
const note = notesMap[noteId];
|
||||
for (const noteTreeId of noteTreeIds) {
|
||||
const note = notesMap[noteTreeId];
|
||||
|
||||
note.title = note.note_title;
|
||||
|
||||
|
@ -39,14 +63,16 @@ const noteTree = (function() {
|
|||
note.extraClasses = "protected";
|
||||
}
|
||||
|
||||
note.key = note.note_id;
|
||||
note.key = counter++ + ""; // key needs to be string
|
||||
note.expanded = note.is_expanded;
|
||||
|
||||
if (parentToNotes[noteId] && parentToNotes[noteId].length > 0) {
|
||||
noteTreeIdToKey[noteTreeId] = note.key;
|
||||
|
||||
if (parentToNotes[noteTreeId] && parentToNotes[noteTreeId].length > 0) {
|
||||
note.folder = true;
|
||||
|
||||
if (note.expanded) {
|
||||
note.children = prepareNoteTreeInner(parentToNotes[noteId], notesMap, parentToNotes);
|
||||
note.children = prepareNoteTreeInner(parentToNotes[noteTreeId], notesMap, parentToNotes);
|
||||
}
|
||||
else {
|
||||
note.lazy = true;
|
||||
|
@ -59,27 +85,27 @@ const noteTree = (function() {
|
|||
return noteList;
|
||||
}
|
||||
|
||||
function setExpandedToServer(note_id, is_expanded) {
|
||||
const expandedNum = is_expanded ? 1 : 0;
|
||||
function setExpandedToServer(noteTreeId, isExpanded) {
|
||||
const expandedNum = isExpanded ? 1 : 0;
|
||||
|
||||
$.ajax({
|
||||
url: baseApiUrl + 'notes/' + note_id + '/expanded/' + expandedNum,
|
||||
url: baseApiUrl + 'notes/' + noteTreeId + '/expanded/' + expandedNum,
|
||||
type: 'PUT',
|
||||
contentType: "application/json",
|
||||
success: result => {}
|
||||
});
|
||||
}
|
||||
|
||||
function initFancyTree(notes) {
|
||||
function initFancyTree(noteTree) {
|
||||
const keybindings = {
|
||||
"insert": node => {
|
||||
const parentKey = treeUtils.getParentKey(node);
|
||||
const parentNoteTreeId = treeUtils.getParentNoteTreeId(node);
|
||||
const isProtected = treeUtils.getParentProtectedStatus(node);
|
||||
|
||||
noteEditor.createNote(node, parentKey, 'after', isProtected);
|
||||
noteEditor.createNote(node, parentNoteTreeId, 'after', isProtected);
|
||||
},
|
||||
"ctrl+insert": node => {
|
||||
noteEditor.createNote(node, node.key, 'into', node.data.is_protected);
|
||||
noteEditor.createNote(node, node.note_id, 'into', node.data.is_protected);
|
||||
},
|
||||
"del": node => {
|
||||
treeChanges.deleteNode(node);
|
||||
|
@ -116,22 +142,26 @@ const noteTree = (function() {
|
|||
treeEl.fancytree({
|
||||
autoScroll: true,
|
||||
extensions: ["hotkeys", "filter", "dnd"],
|
||||
source: notes,
|
||||
source: noteTree,
|
||||
scrollParent: $("#tree"),
|
||||
activate: (event, data) => {
|
||||
const node = data.node.data;
|
||||
|
||||
document.location.hash = node.note_tree_id;
|
||||
|
||||
recentNotes.addRecentNote(node.note_tree_id);
|
||||
|
||||
noteEditor.switchToNote(node.note_id);
|
||||
},
|
||||
expand: (event, data) => {
|
||||
setExpandedToServer(data.node.key, true);
|
||||
setExpandedToServer(getNoteTreeIdFromKey(data.node.key), true);
|
||||
},
|
||||
collapse: (event, data) => {
|
||||
setExpandedToServer(data.node.key, false);
|
||||
setExpandedToServer(getNoteTreeIdFromKey(data.node.key), false);
|
||||
},
|
||||
init: (event, data) => {
|
||||
if (startNoteId) {
|
||||
data.tree.activateKey(startNoteId);
|
||||
if (startNoteTreeId) {
|
||||
treeUtils.activateNode(startNoteTreeId);
|
||||
}
|
||||
},
|
||||
hotkeys: {
|
||||
|
@ -197,13 +227,14 @@ const noteTree = (function() {
|
|||
}
|
||||
},
|
||||
lazyLoad: function(event, data){
|
||||
const node = data.node;
|
||||
const node = data.node.data;
|
||||
const noteTreeId = node.note_tree_id;
|
||||
|
||||
if (parentToNotes[node.key]) {
|
||||
data.result = prepareNoteTreeInner(parentToNotes[node.key]);
|
||||
if (parentToNotes[noteTreeId]) {
|
||||
data.result = prepareNoteTreeInner(parentToNotes[noteTreeId]);
|
||||
}
|
||||
else {
|
||||
console.log("No children. This shouldn't happen.");
|
||||
console.log("No children for " + noteTreeId + ". This shouldn't happen.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -212,28 +243,26 @@ const noteTree = (function() {
|
|||
}
|
||||
|
||||
async function reload() {
|
||||
const notesMap = await loadTree();
|
||||
const notes = await loadTree();
|
||||
|
||||
// this will also reload the note content
|
||||
await treeEl.fancytree('getTree').reload(notesMap);
|
||||
await treeEl.fancytree('getTree').reload(notes);
|
||||
}
|
||||
|
||||
function loadTree() {
|
||||
return $.get(baseApiUrl + 'tree').then(resp => {
|
||||
notesMap = resp.notes_map;
|
||||
parentToNotes = resp.parent_to_notes;
|
||||
startNoteId = resp.start_note_id;
|
||||
startNoteTreeId = resp.start_note_tree_id;
|
||||
treeLoadTime = resp.tree_load_time;
|
||||
|
||||
if (document.location.hash) {
|
||||
startNoteId = document.location.hash.substr(1); // strip initial #
|
||||
startNoteTreeId = document.location.hash.substr(1); // strip initial #
|
||||
}
|
||||
|
||||
return prepareNoteTree();
|
||||
return prepareNoteTree(resp.notes);
|
||||
});
|
||||
}
|
||||
|
||||
$(() => loadTree().then(notesMap => initFancyTree(notesMap)));
|
||||
$(() => loadTree().then(noteTree => initFancyTree(noteTree)));
|
||||
|
||||
function collapseTree() {
|
||||
treeEl.fancytree("getRootNode").visit(node => {
|
||||
|
@ -244,7 +273,7 @@ const noteTree = (function() {
|
|||
$(document).bind('keydown', 'alt+c', collapseTree);
|
||||
|
||||
function scrollToCurrentNote() {
|
||||
const node = treeUtils.getNodeByKey(noteEditor.getCurrentNoteId());
|
||||
const node = treeUtils.getNodeByNoteTreeId(noteEditor.getCurrentNoteId());
|
||||
|
||||
if (node) {
|
||||
node.makeVisible({scrollIntoView: true});
|
||||
|
@ -281,6 +310,22 @@ const noteTree = (function() {
|
|||
return notesMap[noteId];
|
||||
}
|
||||
|
||||
// note that if you want to access data like note_id or is_protected, you need to go into "data" property
|
||||
function getCurrentNode() {
|
||||
return treeEl.fancytree("getActiveNode");
|
||||
}
|
||||
|
||||
function getCurrentNoteTreeId() {
|
||||
const node = getCurrentNode();
|
||||
return node.note_tree_id;
|
||||
}
|
||||
|
||||
function setCurrentNoteTreeBasedOnProtectedStatus() {
|
||||
const node = getCurrentNode();
|
||||
|
||||
node.toggleClass("protected", !!node.data.is_protected);
|
||||
}
|
||||
|
||||
$("button#reset-search-button").click(resetSearch);
|
||||
|
||||
$("input[name=search]").keyup(e => {
|
||||
|
@ -308,12 +353,17 @@ const noteTree = (function() {
|
|||
|
||||
return {
|
||||
getTreeLoadTime,
|
||||
getClipboardNoteId,
|
||||
setClipboardNoteId,
|
||||
getClipboardNoteTreeId,
|
||||
setClipboardNoteTreeId,
|
||||
reload,
|
||||
collapseTree,
|
||||
scrollToCurrentNote,
|
||||
toggleSearch,
|
||||
getByNoteId
|
||||
getByNoteId,
|
||||
getKeyFromNoteTreeId,
|
||||
getNoteTreeIdFromKey,
|
||||
setCurrentNoteTreeBasedOnProtectedStatus,
|
||||
getCurrentNode,
|
||||
getCurrentNoteTreeId,
|
||||
};
|
||||
})();
|
|
@ -37,7 +37,7 @@ const protected_session = (function() {
|
|||
open: () => {
|
||||
if (!modal) {
|
||||
// dialog steals focus for itself, which is not what we want for non-modal (viewing)
|
||||
treeUtils.getNodeByKey(noteEditor.getCurrentNoteId()).setFocus();
|
||||
noteTree.getCurrentNode().setFocus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -52,7 +52,7 @@ const treeChanges = (function() {
|
|||
|
||||
glob.allNoteIds = glob.allNoteIds.filter(e => e !== node.key);
|
||||
|
||||
recentNotes.removeRecentNote(node.key);
|
||||
recentNotes.removeRecentNote(node.note_tree_id);
|
||||
|
||||
let next = node.getNextSibling();
|
||||
if (!next) {
|
||||
|
|
|
@ -3,37 +3,41 @@
|
|||
const treeUtils = (function() {
|
||||
const treeEl = $("#tree");
|
||||
|
||||
function getParentKey(node) {
|
||||
return (node.getParent() === null || node.getParent().key === "root_1") ? "root" : node.getParent().key;
|
||||
function getParentNoteTreeId(node) {
|
||||
return node.note_pid;
|
||||
}
|
||||
|
||||
function getParentProtectedStatus(node) {
|
||||
return node.getParent() === null ? 0 : node.getParent().data.is_protected;
|
||||
}
|
||||
|
||||
function getNodeByKey(noteId) {
|
||||
return treeEl.fancytree('getNodeByKey', noteId);
|
||||
function getNodeByKey(key) {
|
||||
return treeEl.fancytree('getNodeByKey', key);
|
||||
}
|
||||
|
||||
async function activateNode(noteIdToActivate) {
|
||||
const noteIdPath = [ noteIdToActivate ];
|
||||
function getNodeByNoteTreeId(noteTreeId) {
|
||||
const key = noteTree.getKeyFromNoteTreeId(noteTreeId);
|
||||
|
||||
let note = noteTree.getByNoteId(noteIdToActivate);
|
||||
return getNodeByKey(key);
|
||||
}
|
||||
|
||||
async function activateNode(noteTreeIdToActivate) {
|
||||
const noteTreeIdPath = [ noteTreeIdToActivate ];
|
||||
|
||||
let note = noteTree.getByNoteId(noteTreeIdToActivate);
|
||||
|
||||
while (note) {
|
||||
if (note.note_pid !== 'root') {
|
||||
noteIdPath.push(note.note_pid);
|
||||
noteTreeIdPath.push(note.note_pid);
|
||||
}
|
||||
|
||||
note = noteTree.getByNoteId(note.note_pid);
|
||||
}
|
||||
|
||||
for (const noteId of noteIdPath.reverse()) {
|
||||
console.log("Activating/expanding " + noteId);
|
||||
for (const noteTreeId of noteTreeIdPath.reverse()) {
|
||||
const node = treeUtils.getNodeByNoteTreeId(noteTreeId);
|
||||
|
||||
const node = treeUtils.getNodeByKey(noteId);
|
||||
|
||||
if (noteId !== noteIdToActivate) {
|
||||
if (noteTreeId !== noteTreeIdToActivate) {
|
||||
await node.setExpanded();
|
||||
}
|
||||
else {
|
||||
|
@ -57,8 +61,8 @@ const treeUtils = (function() {
|
|||
return noteTitle;
|
||||
}
|
||||
|
||||
function getFullName(noteId) {
|
||||
let note = noteTree.getByNoteId(noteId);
|
||||
function getFullName(noteTreeId) {
|
||||
let note = noteTree.getByNoteId(noteTreeId);
|
||||
|
||||
if (note === null) {
|
||||
return "[unknown]";
|
||||
|
@ -76,9 +80,10 @@ const treeUtils = (function() {
|
|||
}
|
||||
|
||||
return {
|
||||
getParentKey,
|
||||
getParentNoteTreeId,
|
||||
getParentProtectedStatus,
|
||||
getNodeByKey,
|
||||
getNodeByNoteTreeId,
|
||||
activateNode,
|
||||
getNoteTitle,
|
||||
getFullName
|
||||
|
|
|
@ -4,7 +4,6 @@ const express = require('express');
|
|||
const router = express.Router();
|
||||
const auth = require('../../services/auth');
|
||||
const sql = require('../../services/sql');
|
||||
const options = require('../../services/options');
|
||||
const utils = require('../../services/utils');
|
||||
const notes = require('../../services/notes');
|
||||
const protected_session = require('../../services/protected_session');
|
||||
|
@ -14,33 +13,32 @@ const RequestContext = require('../../services/request_context');
|
|||
router.get('/:noteId', auth.checkApiAuth, async (req, res, next) => {
|
||||
const noteId = req.params.noteId;
|
||||
|
||||
await options.setOption('start_node', noteId);
|
||||
|
||||
const detail = await sql.getSingleResult("select * from notes where note_id = ?", [noteId]);
|
||||
|
||||
if (detail.is_protected) {
|
||||
const dataKey = protected_session.getDataKey(req);
|
||||
|
||||
detail.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(noteId), detail.note_title);
|
||||
detail.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(noteId), detail.note_text);
|
||||
detail.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(detail.note_id), detail.note_title);
|
||||
detail.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(detail.note_id), detail.note_text);
|
||||
}
|
||||
|
||||
res.send({
|
||||
detail: detail,
|
||||
images: await sql.getResults("select * from images where note_id = ? order by note_offset", [noteId]),
|
||||
images: await sql.getResults("select * from images where note_id = ? order by note_offset", [detail.note_id]),
|
||||
loadTime: utils.nowTimestamp()
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/:parentNoteId/children', async (req, res, next) => {
|
||||
const parentNoteId = req.params.parentNoteId;
|
||||
router.post('/:parentNoteTreeId/children', async (req, res, next) => {
|
||||
const parentNoteTreeId = req.params.parentNoteTreeId;
|
||||
const browserId = utils.browserId(req);
|
||||
const note = req.body;
|
||||
|
||||
const noteId = await notes.createNewNote(parentNoteId, note, browserId);
|
||||
const { noteId, noteTreeId } = await notes.createNewNote(parentNoteTreeId, note, browserId);
|
||||
|
||||
res.send({
|
||||
'note_id': noteId
|
||||
'note_id': noteId,
|
||||
'note_tree_id': noteTreeId
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -6,27 +6,32 @@ const sql = require('../../services/sql');
|
|||
const auth = require('../../services/auth');
|
||||
const utils = require('../../services/utils');
|
||||
const sync_table = require('../../services/sync_table');
|
||||
const options = require('../../services/options');
|
||||
|
||||
router.get('', auth.checkApiAuth, async (req, res, next) => {
|
||||
res.send(await getRecentNotes());
|
||||
});
|
||||
|
||||
router.put('/:noteId', auth.checkApiAuth, async (req, res, next) => {
|
||||
router.put('/:noteTreeId', auth.checkApiAuth, async (req, res, next) => {
|
||||
const noteTreeId = req.params.noteTreeId;
|
||||
|
||||
await sql.replace('recent_notes', {
|
||||
note_id: req.params.noteId,
|
||||
note_tree_id: noteTreeId,
|
||||
date_accessed: utils.nowTimestamp(),
|
||||
is_deleted: 0
|
||||
});
|
||||
|
||||
await sync_table.addRecentNoteSync(req.params.noteId);
|
||||
await sync_table.addRecentNoteSync(noteTreeId);
|
||||
|
||||
await options.setOption('start_note_tree_id', noteTreeId);
|
||||
|
||||
res.send(await getRecentNotes());
|
||||
});
|
||||
|
||||
router.delete('/:noteId', auth.checkApiAuth, async (req, res, next) => {
|
||||
await sql.execute('UPDATE recent_notes SET is_deleted = 1 WHERE note_id = ?', [req.params.noteId]);
|
||||
router.delete('/:noteTreeId', auth.checkApiAuth, async (req, res, next) => {
|
||||
await sql.execute('UPDATE recent_notes SET is_deleted = 1 WHERE note_tree_id = ?', [req.params.noteTreeId]);
|
||||
|
||||
await sync_table.addRecentNoteSync(req.params.noteId);
|
||||
await sync_table.addRecentNoteSync(req.params.noteTreeId);
|
||||
|
||||
res.send(await getRecentNotes());
|
||||
});
|
||||
|
|
|
@ -21,28 +21,17 @@ router.get('/', auth.checkApiAuth, async (req, res, next) => {
|
|||
+ "where notes.is_deleted = 0 and notes_tree.is_deleted = 0 "
|
||||
+ "order by note_pid, note_pos");
|
||||
|
||||
const parentToNotes = {};
|
||||
const notesMap = {};
|
||||
|
||||
const dataKey = protected_session.getDataKey(req);
|
||||
|
||||
for (const note of notes) {
|
||||
if (note.is_protected) {
|
||||
note.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title);
|
||||
}
|
||||
|
||||
if (!parentToNotes[note.note_pid]) {
|
||||
parentToNotes[note.note_pid] = [];
|
||||
}
|
||||
|
||||
notesMap[note.note_id] = note;
|
||||
parentToNotes[note.note_pid].push(note.note_id);
|
||||
}
|
||||
|
||||
res.send({
|
||||
notes_map: notesMap,
|
||||
parent_to_notes: parentToNotes,
|
||||
start_note_id: await options.getOption('start_node'),
|
||||
notes: notes,
|
||||
start_note_tree_id: await options.getOption('start_note_tree_id'),
|
||||
tree_load_time: utils.nowTimestamp()
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ const options = require('./options');
|
|||
const fs = require('fs-extra');
|
||||
const log = require('./log');
|
||||
|
||||
const APP_DB_VERSION = 36;
|
||||
const APP_DB_VERSION = 39;
|
||||
const MIGRATIONS_DIR = "migrations";
|
||||
|
||||
async function migrate() {
|
||||
|
|
|
@ -8,6 +8,7 @@ const sync_table = require('./sync_table');
|
|||
|
||||
async function createNewNote(parentNoteId, note, browserId) {
|
||||
const noteId = utils.newNoteId();
|
||||
const noteTreeId = utils.newNoteId();
|
||||
|
||||
let newNotePos = 0;
|
||||
|
||||
|
@ -50,6 +51,7 @@ async function createNewNote(parentNoteId, note, browserId) {
|
|||
});
|
||||
|
||||
await sql.insert("notes_tree", {
|
||||
'note_tree_id': noteTreeId,
|
||||
'note_id': noteId,
|
||||
'note_pid': parentNoteId,
|
||||
'note_pos': newNotePos,
|
||||
|
@ -58,7 +60,11 @@ async function createNewNote(parentNoteId, note, browserId) {
|
|||
'is_deleted': 0
|
||||
});
|
||||
});
|
||||
return noteId;
|
||||
|
||||
return {
|
||||
noteId,
|
||||
noteTreeId
|
||||
};
|
||||
}
|
||||
|
||||
async function encryptNote(note, ctx) {
|
||||
|
|
|
@ -22,8 +22,8 @@ async function addOptionsSync(optName, sourceId) {
|
|||
await addEntitySync("options", optName, sourceId);
|
||||
}
|
||||
|
||||
async function addRecentNoteSync(noteId, sourceId) {
|
||||
await addEntitySync("recent_notes", noteId, sourceId);
|
||||
async function addRecentNoteSync(noteTreeId, sourceId) {
|
||||
await addEntitySync("recent_notes", noteTreeId, sourceId);
|
||||
}
|
||||
|
||||
async function addEntitySync(entityName, entityId, sourceId) {
|
||||
|
|
|
@ -137,10 +137,10 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<div id="protected-session-password-dialog" title="Encrypted note" style="display: none;">
|
||||
<div id="protected-session-password-dialog" title="Protected session" style="display: none;">
|
||||
<form id="protected-session-password-form">
|
||||
<div class="form-group">
|
||||
<label for="protected-session-password">This note is encrypted. Enter password to show it:</label>
|
||||
<label for="protected-session-password">To proceed with requested action you need to enter protected session by entering password:</label>
|
||||
<input id="protected-session-password" style="width: 250px;" type="password">
|
||||
<button class="btn btn-sm">Show</button>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue