From 4cda661c1b5d841f6b1413a0695dcb295664438f Mon Sep 17 00:00:00 2001 From: zadam Date: Mon, 11 Nov 2019 22:57:51 +0100 Subject: [PATCH] add move to dialog --- db/schema.sql | 64 ++++++++++--------- package-lock.json | 2 +- src/public/javascripts/dialogs/move_to.js | 62 ++++++++++++++++++ .../javascripts/services/entrypoints.js | 13 +++- .../javascripts/services/tree_context_menu.js | 24 ++++--- src/services/consistency_checks.js | 2 +- src/views/desktop.ejs | 1 + src/views/dialogs/move_to.ejs | 31 +++++++++ 8 files changed, 156 insertions(+), 43 deletions(-) create mode 100644 src/public/javascripts/dialogs/move_to.js create mode 100644 src/views/dialogs/move_to.ejs diff --git a/db/schema.sql b/db/schema.sql index c1d27d76f..414123672 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -83,42 +83,25 @@ CREATE TABLE IF NOT EXISTS "branches" ( CREATE INDEX `IDX_branches_noteId` ON `branches` (`noteId`); CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`); CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId); -CREATE TABLE IF NOT EXISTS "note_revisions" (`noteRevisionId` TEXT NOT NULL PRIMARY KEY, - `noteId` TEXT NOT NULL, - `title` TEXT, - `contentLength` INT NOT NULL, - `isProtected` INT NOT NULL DEFAULT 0, - `utcDateLastEdited` TEXT NOT NULL, - `utcDateCreated` TEXT NOT NULL, - `utcDateModified` TEXT NOT NULL, - `dateLastEdited` TEXT NOT NULL, - `dateCreated` TEXT NOT NULL, - type TEXT DEFAULT '' NOT NULL, - mime TEXT DEFAULT '' NOT NULL, - hash TEXT DEFAULT '' NOT NULL); CREATE TABLE IF NOT EXISTS "note_revision_contents" (`noteRevisionId` TEXT NOT NULL PRIMARY KEY, `content` TEXT, hash TEXT DEFAULT '' NOT NULL, `utcDateModified` TEXT NOT NULL); -CREATE INDEX `IDX_note_revisions_noteId` ON `note_revisions` (`noteId`); -CREATE INDEX `IDX_note_revisions_utcDateCreated` ON `note_revisions` (`utcDateCreated`); -CREATE INDEX `IDX_note_revisions_utcDateLastEdited` ON `note_revisions` (`utcDateLastEdited`); -CREATE INDEX `IDX_note_revisions_dateCreated` ON `note_revisions` (`dateCreated`); -CREATE INDEX `IDX_note_revisions_dateLastEdited` ON `note_revisions` (`dateLastEdited`); CREATE TABLE IF NOT EXISTS "notes" ( - `noteId` TEXT NOT NULL, - `title` TEXT NOT NULL DEFAULT "note", - `isProtected` INT NOT NULL DEFAULT 0, - `type` TEXT NOT NULL DEFAULT 'text', - `mime` TEXT NOT NULL DEFAULT 'text/html', - `hash` TEXT DEFAULT "" NOT NULL, - `isDeleted` INT NOT NULL DEFAULT 0, - `isErased` INT NOT NULL DEFAULT 0, - `dateCreated` TEXT NOT NULL, - `dateModified` TEXT NOT NULL, - `utcDateCreated` TEXT NOT NULL, - `utcDateModified` TEXT NOT NULL, - PRIMARY KEY(`noteId`)); + `noteId` TEXT NOT NULL, + `title` TEXT NOT NULL DEFAULT "note", + `contentLength` INT NOT NULL, + `isProtected` INT NOT NULL DEFAULT 0, + `type` TEXT NOT NULL DEFAULT 'text', + `mime` TEXT NOT NULL DEFAULT 'text/html', + `hash` TEXT DEFAULT "" NOT NULL, + `isDeleted` INT NOT NULL DEFAULT 0, + `isErased` INT NOT NULL DEFAULT 0, + `dateCreated` TEXT NOT NULL, + `dateModified` TEXT NOT NULL, + `utcDateCreated` TEXT NOT NULL, + `utcDateModified` TEXT NOT NULL, + PRIMARY KEY(`noteId`)); CREATE INDEX `IDX_notes_isDeleted` ON `notes` (`isDeleted`); CREATE INDEX `IDX_notes_title` ON `notes` (`title`); CREATE INDEX `IDX_notes_type` ON `notes` (`type`); @@ -126,3 +109,22 @@ CREATE INDEX `IDX_notes_dateCreated` ON `notes` (`dateCreated`); CREATE INDEX `IDX_notes_dateModified` ON `notes` (`dateModified`); CREATE INDEX `IDX_notes_utcDateModified` ON `notes` (`utcDateModified`); CREATE INDEX `IDX_notes_utcDateCreated` ON `notes` (`utcDateCreated`); +CREATE TABLE IF NOT EXISTS "note_revisions" (`noteRevisionId` TEXT NOT NULL PRIMARY KEY, + `noteId` TEXT NOT NULL, + `title` TEXT, + `contentLength` INT NOT NULL, + `isErased` INT NOT NULL DEFAULT 0, + `isProtected` INT NOT NULL DEFAULT 0, + `utcDateLastEdited` TEXT NOT NULL, + `utcDateCreated` TEXT NOT NULL, + `utcDateModified` TEXT NOT NULL, + `dateLastEdited` TEXT NOT NULL, + `dateCreated` TEXT NOT NULL, + type TEXT DEFAULT '' NOT NULL, + mime TEXT DEFAULT '' NOT NULL, + hash TEXT DEFAULT '' NOT NULL); +CREATE INDEX `IDX_note_revisions_noteId` ON `note_revisions` (`noteId`); +CREATE INDEX `IDX_note_revisions_utcDateCreated` ON `note_revisions` (`utcDateCreated`); +CREATE INDEX `IDX_note_revisions_utcDateLastEdited` ON `note_revisions` (`utcDateLastEdited`); +CREATE INDEX `IDX_note_revisions_dateCreated` ON `note_revisions` (`dateCreated`); +CREATE INDEX `IDX_note_revisions_dateLastEdited` ON `note_revisions` (`dateLastEdited`); diff --git a/package-lock.json b/package-lock.json index 6655db41f..63c583348 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "trilium", - "version": "0.36.4", + "version": "0.36.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/public/javascripts/dialogs/move_to.js b/src/public/javascripts/dialogs/move_to.js new file mode 100644 index 000000000..68d570880 --- /dev/null +++ b/src/public/javascripts/dialogs/move_to.js @@ -0,0 +1,62 @@ +import noteAutocompleteService from "../services/note_autocomplete.js"; +import utils from "../services/utils.js"; +import cloningService from "../services/cloning.js"; +import treeUtils from "../services/tree_utils.js"; +import toastService from "../services/toast.js"; +import treeCache from "../services/tree_cache.js"; +import treeChangesService from "../services/branches.js"; +import treeService from "../services/tree.js"; + +const $dialog = $("#move-to-dialog"); +const $form = $("#move-to-form"); +const $noteAutoComplete = $("#move-to-note-autocomplete"); +const $movePrefix = $("#move-prefix"); +const $noteList = $("#move-to-note-list"); + +let movedNodes; + +export async function showDialog(nodes) { + movedNodes = nodes; + + utils.closeActiveDialog(); + + glob.activeDialog = $dialog; + + $dialog.modal(); + + $noteAutoComplete.val('').trigger('focus'); + + $noteList.empty(); + + for (const node of movedNodes) { + const note = await treeCache.getNote(node.data.noteId); + + $noteList.append($("
  • ").text(note.title)); + } + + noteAutocompleteService.initNoteAutocomplete($noteAutoComplete); + noteAutocompleteService.showRecentNotes($noteAutoComplete); +} + +async function moveNotesTo(notePath) { + const targetNode = await treeService.getNodeFromPath(notePath); + + await treeChangesService.moveToNode(movedNodes, targetNode); + + toastService.showMessage(`Selected notes have been moved into ${targetNode.title}`); +} + +$form.on('submit', () => { + const notePath = $noteAutoComplete.getSelectedPath(); + + if (notePath) { + $dialog.modal('hide'); + + moveNotesTo(notePath); + } + else { + console.error("No path to move to."); + } + + return false; +}); \ No newline at end of file diff --git a/src/public/javascripts/services/entrypoints.js b/src/public/javascripts/services/entrypoints.js index c99f5e9d0..8256ae96c 100644 --- a/src/public/javascripts/services/entrypoints.js +++ b/src/public/javascripts/services/entrypoints.js @@ -18,6 +18,7 @@ const NOTE_INFO = "../dialogs/note_info.js"; const ABOUT = "../dialogs/about.js"; const LINK_MAP = "../dialogs/link_map.js"; const CLONE_TO = "../dialogs/clone_to.js"; +const MOVE_TO = "../dialogs/move_to.js"; function registerEntrypoints() { // hot keys are active also inside inputs and content editables @@ -189,14 +190,22 @@ function registerEntrypoints() { utils.bindGlobalShortcut('ctrl+shift+c', () => import(CLONE_TO).then(d => { const activeNode = treeService.getActiveNode(); - console.log("activeNode", activeNode); + const selectedOrActiveNodes = treeService.getSelectedOrActiveNodes(activeNode); - console.log("selectedOrActiveNodes", selectedOrActiveNodes); const noteIds = selectedOrActiveNodes.map(node => node.data.noteId); d.showDialog(noteIds); })); + + utils.bindGlobalShortcut('ctrl+shift+x', () => import(MOVE_TO).then(d => { + const activeNode = treeService.getActiveNode(); + + const selectedOrActiveNodes = treeService.getSelectedOrActiveNodes(activeNode); + + d.showDialog(selectedOrActiveNodes); + })); + } export default { diff --git a/src/public/javascripts/services/tree_context_menu.js b/src/public/javascripts/services/tree_context_menu.js index f03a4eb3d..6cb5fc320 100644 --- a/src/public/javascripts/services/tree_context_menu.js +++ b/src/public/javascripts/services/tree_context_menu.js @@ -17,11 +17,12 @@ class TreeContextMenu { getNoteTypeItems(baseCmd) { return [ - { title: "Text", cmd: baseCmd + "_text", uiIcon: "file" }, - { title: "Code", cmd: baseCmd + "_code", uiIcon: "terminal" }, - { title: "Saved search", cmd: baseCmd + "_search", uiIcon: "search-folder" }, - { title: "Relation Map", cmd: baseCmd + "_relation-map", uiIcon: "map" }, - { title: "Render HTML note", cmd: baseCmd + "_render", uiIcon: "play" } + { title: "Text", cmd: baseCmd + "_text", uiIcon: "note" }, + { title: "Code", cmd: baseCmd + "_code", uiIcon: "code" }, + { title: "Saved search", cmd: baseCmd + "_search", uiIcon: "file-find" }, + { title: "Relation Map", cmd: baseCmd + "_relation-map", uiIcon: "map-alt" }, + { title: "Render HTML note", cmd: baseCmd + "_render", uiIcon: "extension" }, + { title: "Book", cmd: baseCmd + "_book", uiIcon: "book" } ]; } @@ -63,11 +64,13 @@ class TreeContextMenu { { title: "Unprotect subtree", cmd: "unprotectSubtree", uiIcon: "shield", enabled: noSelectedNotes }, { title: "----" }, { title: "Copy / clone Ctrl+C", cmd: "copy", uiIcon: "copy", - enabled: isNotRoot }, + enabled: isNotRoot && !isHoisted }, { title: "Clone to ... Ctrl+Shift+C", cmd: "cloneTo", uiIcon: "empty", - enabled: isNotRoot }, + enabled: isNotRoot && !isHoisted }, { title: "Cut Ctrl+X", cmd: "cut", uiIcon: "cut", enabled: isNotRoot && !isHoisted && parentNotSearch }, + { title: "Move to ... Ctrl+Shift+X", cmd: "moveTo", uiIcon: "empty", + enabled: isNotRoot && !isHoisted && parentNotSearch }, { title: "Paste into Ctrl+V", cmd: "pasteInto", uiIcon: "paste", enabled: !clipboard.isEmpty() && notSearch && noSelectedNotes }, { title: "Paste after", cmd: "pasteAfter", uiIcon: "paste", @@ -127,11 +130,16 @@ class TreeContextMenu { const nodes = treeService.getSelectedOrActiveNodes(this.node); const noteIds = nodes.map(node => node.data.noteId); - import("../dialogs/clone_to.js").then(d => d.showDialog(noteIds)) + import("../dialogs/clone_to.js").then(d => d.showDialog(noteIds)); } else if (cmd === "cut") { clipboard.cut(treeService.getSelectedOrActiveNodes(this.node)); } + else if (cmd === "moveTo") { + const nodes = treeService.getSelectedOrActiveNodes(this.node); + + import("../dialogs/move_to.js").then(d => d.showDialog(nodes)); + } else if (cmd === "pasteAfter") { clipboard.pasteAfter(this.node); } diff --git a/src/services/consistency_checks.js b/src/services/consistency_checks.js index a6bdf0fd9..6d781f7a0 100644 --- a/src/services/consistency_checks.js +++ b/src/services/consistency_checks.js @@ -166,7 +166,7 @@ async function findBrokenReferenceIssues() { } }); - await findAndFixIssues(` + await findIssues(` SELECT noteRevisionId, note_revisions.noteId FROM note_revisions LEFT JOIN notes USING(noteId) WHERE notes.noteId IS NULL`, diff --git a/src/views/desktop.ejs b/src/views/desktop.ejs index a1840bb39..e6a078476 100644 --- a/src/views/desktop.ejs +++ b/src/views/desktop.ejs @@ -176,6 +176,7 @@ <% include dialogs/note_info.ejs %> <% include dialogs/link_map.ejs %> <% include dialogs/clone_to.ejs %> + <% include dialogs/move_to.ejs %>