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