From 5fb94fcbbdcbb49a0ddb1cae29bba16382bb6452 Mon Sep 17 00:00:00 2001 From: azivner Date: Sat, 18 Nov 2017 17:05:50 -0500 Subject: [PATCH] notes_tree now has note_tree_id so we stricly distinguish between working on notes or note trees --- migrations/0037__add_note_tree_id.sql | 21 +++ migrations/0038__rename_start_node.sql | 1 + ...ent_notes_should_point_to_note_tree_id.sql | 7 + public/javascripts/context_menu.js | 18 +-- public/javascripts/dialogs/recent_notes.js | 20 +-- public/javascripts/note_editor.js | 24 ++-- public/javascripts/note_tree.js | 132 ++++++++++++------ public/javascripts/protected_session.js | 2 +- public/javascripts/tree_changes.js | 2 +- public/javascripts/tree_utils.js | 37 ++--- routes/api/notes.js | 18 ++- routes/api/recent_notes.js | 17 ++- routes/api/tree.js | 15 +- services/migration.js | 2 +- services/notes.js | 8 +- services/sync_table.js | 4 +- views/index.ejs | 4 +- 17 files changed, 203 insertions(+), 129 deletions(-) create mode 100644 migrations/0037__add_note_tree_id.sql create mode 100644 migrations/0038__rename_start_node.sql create mode 100644 migrations/0039__recent_notes_should_point_to_note_tree_id.sql diff --git a/migrations/0037__add_note_tree_id.sql b/migrations/0037__add_note_tree_id.sql new file mode 100644 index 000000000..5afc168e5 --- /dev/null +++ b/migrations/0037__add_note_tree_id.sql @@ -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` +); \ No newline at end of file diff --git a/migrations/0038__rename_start_node.sql b/migrations/0038__rename_start_node.sql new file mode 100644 index 000000000..9601974c8 --- /dev/null +++ b/migrations/0038__rename_start_node.sql @@ -0,0 +1 @@ +UPDATE options SET opt_name = 'start_note_tree_id' WHERE opt_name = 'start_node'; \ No newline at end of file diff --git a/migrations/0039__recent_notes_should_point_to_note_tree_id.sql b/migrations/0039__recent_notes_should_point_to_note_tree_id.sql new file mode 100644 index 000000000..00b5efbd3 --- /dev/null +++ b/migrations/0039__recent_notes_should_point_to_note_tree_id.sql @@ -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 +); \ No newline at end of file diff --git a/public/javascripts/context_menu.js b/public/javascripts/context_menu.js index 51c6f4953..4f3fdd950 100644 --- a/public/javascripts/context_menu.js +++ b/public/javascripts/context_menu.js @@ -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'); diff --git a/public/javascripts/dialogs/recent_notes.js b/public/javascripts/dialogs/recent_notes.js index f995dab63..d5bd7d113 100644 --- a/public/javascripts/dialogs/recent_notes.js +++ b/public/javascripts/dialogs/recent_notes.js @@ -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 = $("") - .attr("value", valueNoteId) + .attr("value", valueNoteTreeId) .text(noteTitle); // select the first one (most recent one) by default diff --git a/public/javascripts/note_editor.js b/public/javascripts/note_editor.js index a1767d531..d74180d29 100644 --- a/public/javascripts/note_editor.js +++ b/public/javascripts/note_editor.js @@ -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); diff --git a/public/javascripts/note_tree.js b/public/javascripts/note_tree.js index 5ed133f7d..2e8fd2ec5 100644 --- a/public/javascripts/note_tree.js +++ b/public/javascripts/note_tree.js @@ -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, }; })(); \ No newline at end of file diff --git a/public/javascripts/protected_session.js b/public/javascripts/protected_session.js index 59963c90b..0da7f07cb 100644 --- a/public/javascripts/protected_session.js +++ b/public/javascripts/protected_session.js @@ -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(); } } }); diff --git a/public/javascripts/tree_changes.js b/public/javascripts/tree_changes.js index 59b762555..b5073c56b 100644 --- a/public/javascripts/tree_changes.js +++ b/public/javascripts/tree_changes.js @@ -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) { diff --git a/public/javascripts/tree_utils.js b/public/javascripts/tree_utils.js index 81a028801..0826bef9d 100644 --- a/public/javascripts/tree_utils.js +++ b/public/javascripts/tree_utils.js @@ -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 diff --git a/routes/api/notes.js b/routes/api/notes.js index 4768669e8..7e775c0f2 100644 --- a/routes/api/notes.js +++ b/routes/api/notes.js @@ -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 }); }); diff --git a/routes/api/recent_notes.js b/routes/api/recent_notes.js index 04d3d7407..14baf4d20 100644 --- a/routes/api/recent_notes.js +++ b/routes/api/recent_notes.js @@ -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()); }); diff --git a/routes/api/tree.js b/routes/api/tree.js index 2dcd79df0..3fa72f577 100644 --- a/routes/api/tree.js +++ b/routes/api/tree.js @@ -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() }); }); diff --git a/services/migration.js b/services/migration.js index 8c0f50979..9e07e1eda 100644 --- a/services/migration.js +++ b/services/migration.js @@ -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() { diff --git a/services/notes.js b/services/notes.js index 0d51ef3bb..2cdc57ba7 100644 --- a/services/notes.js +++ b/services/notes.js @@ -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) { diff --git a/services/sync_table.js b/services/sync_table.js index fa7f8645f..b0f8fc71b 100644 --- a/services/sync_table.js +++ b/services/sync_table.js @@ -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) { diff --git a/views/index.ejs b/views/index.ejs index 1f1a2f8ca..c6f984f15 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -137,10 +137,10 @@ -