From 3610926804aadf026c23c683069eb4d07272fcfe Mon Sep 17 00:00:00 2001 From: azivner Date: Sun, 19 Nov 2017 16:35:35 -0500 Subject: [PATCH] adding clone from recent notes dialog --- .../0042__remove_unique_index_on_note_id.sql | 24 ++++++++++ public/javascripts/dialogs/recent_notes.js | 45 ++++++++++++++++--- public/javascripts/note_tree.js | 4 +- routes/api/tree.js | 35 ++++++++++++++- services/migration.js | 2 +- services/notes.js | 7 ++- views/index.ejs | 4 ++ 7 files changed, 109 insertions(+), 12 deletions(-) create mode 100644 migrations/0042__remove_unique_index_on_note_id.sql diff --git a/migrations/0042__remove_unique_index_on_note_id.sql b/migrations/0042__remove_unique_index_on_note_id.sql new file mode 100644 index 000000000..f014a16e6 --- /dev/null +++ b/migrations/0042__remove_unique_index_on_note_id.sql @@ -0,0 +1,24 @@ +CREATE TABLE "notes_tree_mig" ( + [note_tree_id] VARCHAR(30) PRIMARY KEY NOT NULL, + [note_id] VARCHAR(30) 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 note_tree_id, note_id, note_pid, note_pos, is_expanded, date_modified, is_deleted FROM notes_tree; + +DROP TABLE notes_tree; +ALTER TABLE notes_tree_mig RENAME TO notes_tree; + +CREATE INDEX `IDX_notes_tree_note_tree_id` ON `notes_tree` ( + `note_tree_id` +); + +CREATE INDEX `IDX_notes_tree_note_id_note_pid` ON `notes_tree` ( + `note_id`, + `note_pid` +); \ No newline at end of file diff --git a/public/javascripts/dialogs/recent_notes.js b/public/javascripts/dialogs/recent_notes.js index 278a8af90..55587a594 100644 --- a/public/javascripts/dialogs/recent_notes.js +++ b/public/javascripts/dialogs/recent_notes.js @@ -5,6 +5,8 @@ const recentNotes = (function() { const selectBoxEl = $('#recent-notes-select-box'); const jumpToButtonEl = $('#recentNotesJumpTo'); const addLinkButtonEl = $('#recentNotesAddLink'); + const addCurrentAsChildEl = $("#recent-notes-add-current-as-child"); + const addRecentAsChildEl = $("#recent-notes-add-recent-as-child"); const noteDetailEl = $('#note-detail'); let list = []; @@ -31,9 +33,10 @@ const recentNotes = (function() { }, 1500); } + // FIXME: this should be probably just refresh upon deletion, not explicit delete function removeRecentNote(notePathIdToRemove) { $.ajax({ - url: baseApiUrl + 'recent-notes/' + notePathIdToRemove, + url: baseApiUrl + 'recent-notes/' + encodeURIComponent(notePathIdToRemove), type: 'DELETE', error: () => showError("Error removing note from recent notes.") }).then(result => { @@ -72,12 +75,12 @@ const recentNotes = (function() { }); } - function getSelectedNotePathFromRecentNotes() { + function getSelectedNotePath() { return selectBoxEl.find("option:selected").val(); } function setActiveNoteBasedOnRecentNotes() { - const notePath = getSelectedNotePathFromRecentNotes(); + const notePath = getSelectedNotePath(); noteTree.activateNode(notePath); @@ -85,9 +88,9 @@ const recentNotes = (function() { } function addLinkBasedOnRecentNotes() { - const notePath = getSelectedNotePathFromRecentNotes(); + const notePath = getSelectedNotePath(); - const linkTitle = treeUtils.getNoteTitle(notePath); + const linkTitle = noteTree.getNoteTitle(notePath); dialogEl.dialog("close"); @@ -100,9 +103,33 @@ const recentNotes = (function() { }); } + 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.") + }); + + dialogEl.dialog("close"); + + await noteTree.reload(); + } + + async function addCurrentAsChild() { + await addAsChild(getSelectedNotePath(), noteTree.getCurrentNotePath()); + } + + async function addRecentAsChild() { + addAsChild(noteTree.getCurrentNotePath(), getSelectedNotePath()); + } + selectBoxEl.keydown(e => { const key = e.which; + // to get keycodes use http://keycode.info/ if (key === 13)// the enter key code { setActiveNoteBasedOnRecentNotes(); @@ -110,6 +137,12 @@ const recentNotes = (function() { else if (key === 76 /* l */) { addLinkBasedOnRecentNotes(); } + else if (key === 67 /* c */) { + addCurrentAsChild(); + } + else if (key === 82 /* r */) { + addRecentAsChild() + } else { return; // avoid prevent default } @@ -125,6 +158,8 @@ const recentNotes = (function() { jumpToButtonEl.click(setActiveNoteBasedOnRecentNotes); addLinkButtonEl.click(addLinkBasedOnRecentNotes); + addCurrentAsChildEl.click(addCurrentAsChild); + addRecentAsChildEl.click(addRecentAsChild); return { showDialog, diff --git a/public/javascripts/note_tree.js b/public/javascripts/note_tree.js index d5047fa7c..d50eada68 100644 --- a/public/javascripts/note_tree.js +++ b/public/javascripts/note_tree.js @@ -256,10 +256,12 @@ const noteTree = (function() { setExpandedToServer(getNoteTreeIdFromKey(data.node.key), false); }, init: (event, data) => { - showAppIfHidden(); if (startNoteTreeId) { activateNode(startNoteTreeId); } + else { + showAppIfHidden(); + } }, hotkeys: { keydown: keybindings diff --git a/routes/api/tree.js b/routes/api/tree.js index 3e59b7dfd..f69109e54 100644 --- a/routes/api/tree.js +++ b/routes/api/tree.js @@ -6,10 +6,10 @@ const sql = require('../../services/sql'); const options = require('../../services/options'); const utils = require('../../services/utils'); const auth = require('../../services/auth'); -const log = require('../../services/log'); const protected_session = require('../../services/protected_session'); const data_encryption = require('../../services/data_encryption'); const notes = require('../../services/notes'); +const sync_table = require('../../services/sync_table'); router.get('/', auth.checkApiAuth, async (req, res, next) => { const notes = await sql.getResults("select " @@ -48,4 +48,37 @@ router.put('/:noteId/protectSubTree/:isProtected', auth.checkApiAuth, async (req 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; diff --git a/services/migration.js b/services/migration.js index dc949053f..ab846a126 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 = 41; +const APP_DB_VERSION = 42; const MIGRATIONS_DIR = "migrations"; async function migrate() { diff --git a/services/notes.js b/services/notes.js index eea4a5ca6..6bb372c5e 100644 --- a/services/notes.js +++ b/services/notes.js @@ -13,8 +13,7 @@ async function createNewNote(parentNoteId, note, browserId) { let newNotePos = 0; if (note.target === 'into') { - const res = await sql.getSingleResult('select max(note_pos) as max_note_pos from notes_tree where note_pid = ? and is_deleted = 0', [parentNoteId]); - const maxNotePos = res['max_note_pos']; + const maxNotePos = await sql.getSingleValue('select max(note_pos) from notes_tree where note_pid = ? and is_deleted = 0', [parentNoteId]); if (maxNotePos === null) // no children yet newNotePos = 0; @@ -36,7 +35,7 @@ async function createNewNote(parentNoteId, note, browserId) { await sql.doInTransaction(async () => { await sql.addAudit(audit_category.CREATE_NOTE, browserId, noteId); - await sync_table.addNoteTreeSync(noteId); + await sync_table.addNoteTreeSync(noteTreeId); await sync_table.addNoteSync(noteId); const now = utils.nowTimestamp(); @@ -56,7 +55,7 @@ async function createNewNote(parentNoteId, note, browserId) { 'note_pid': parentNoteId, 'note_pos': newNotePos, 'is_expanded': 0, - 'date_modified': utils.nowTimestamp(), + 'date_modified': now, 'is_deleted': 0 }); }); diff --git a/views/index.ejs b/views/index.ejs index c6f984f15..df64f280e 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -107,6 +107,10 @@   + + + +