mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	add move to dialog
This commit is contained in:
		| @@ -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`); | ||||
|   | ||||
							
								
								
									
										2
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "trilium", | ||||
|   "version": "0.36.4", | ||||
|   "version": "0.36.5", | ||||
|   "lockfileVersion": 1, | ||||
|   "requires": true, | ||||
|   "dependencies": { | ||||
|   | ||||
							
								
								
									
										62
									
								
								src/public/javascripts/dialogs/move_to.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/public/javascripts/dialogs/move_to.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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($("<li>").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; | ||||
| }); | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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 <kbd>Ctrl+C</kbd>", cmd: "copy", uiIcon: "copy", | ||||
|                 enabled: isNotRoot }, | ||||
|                 enabled: isNotRoot && !isHoisted }, | ||||
|             { title: "Clone to ... <kbd>Ctrl+Shift+C</kbd>", cmd: "cloneTo", uiIcon: "empty", | ||||
|                 enabled: isNotRoot }, | ||||
|                 enabled: isNotRoot && !isHoisted }, | ||||
|             { title: "Cut <kbd>Ctrl+X</kbd>", cmd: "cut", uiIcon: "cut", | ||||
|                 enabled: isNotRoot && !isHoisted && parentNotSearch }, | ||||
|             { title: "Move to ... <kbd>Ctrl+Shift+X</kbd>", cmd: "moveTo", uiIcon: "empty", | ||||
|                 enabled: isNotRoot && !isHoisted && parentNotSearch }, | ||||
|             { title: "Paste into <kbd>Ctrl+V</kbd>", 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); | ||||
|         } | ||||
|   | ||||
| @@ -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`, | ||||
|   | ||||
| @@ -176,6 +176,7 @@ | ||||
|     <% include dialogs/note_info.ejs %> | ||||
|     <% include dialogs/link_map.ejs %> | ||||
|     <% include dialogs/clone_to.ejs %> | ||||
|     <% include dialogs/move_to.ejs %> | ||||
| </div> | ||||
|  | ||||
| <script type="text/javascript"> | ||||
|   | ||||
							
								
								
									
										31
									
								
								src/views/dialogs/move_to.ejs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/views/dialogs/move_to.ejs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| <div id="move-to-dialog" class="modal mx-auto" tabindex="-1" role="dialog"> | ||||
|     <div class="modal-dialog modal-lg" style="max-width: 1000px" role="document"> | ||||
|         <div class="modal-content"> | ||||
|             <div class="modal-header"> | ||||
|                 <h5 class="modal-title mr-auto">Move notes to ...</h5> | ||||
|                  | ||||
|                 <button type="button" class="close" data-dismiss="modal" aria-label="Close" style="margin-left: 0 !important;"> | ||||
|                     <span aria-hidden="true">×</span> | ||||
|                 </button> | ||||
|             </div> | ||||
|             <form id="move-to-form"> | ||||
|                 <div class="modal-body"> | ||||
|                     <h5>Notes to move</h5> | ||||
|  | ||||
|                     <ul id="move-to-note-list" style="max-height: 200px; overflow: auto;"></ul> | ||||
|  | ||||
|                     <div class="form-group"> | ||||
|                         <label for="move-to-note-autocomplete">Target parent note</label> | ||||
|  | ||||
|                         <div class="input-group"> | ||||
|                             <input id="move-to-note-autocomplete" class="form-control" placeholder="search for note by its name"> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <div class="modal-footer"> | ||||
|                     <button type="submit" class="btn btn-primary">Move to selected note <kbd>enter</kbd></button> | ||||
|                 </div> | ||||
|             </form> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
		Reference in New Issue
	
	Block a user