mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	Merge branch 'sort-by'
This commit is contained in:
		
							
								
								
									
										24
									
								
								src/public/app/dialogs/sort_child_notes.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/public/app/dialogs/sort_child_notes.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | import server from "../services/server.js"; | ||||||
|  | import utils from "../services/utils.js"; | ||||||
|  |  | ||||||
|  | const $dialog = $("#sort-child-notes-dialog"); | ||||||
|  | const $form = $("#sort-child-notes-form"); | ||||||
|  |  | ||||||
|  | let parentNoteId = null; | ||||||
|  |  | ||||||
|  | $form.on('submit', async () => { | ||||||
|  |     const sortBy = $form.find("input[name='sort-by']:checked").val(); | ||||||
|  |     const sortDirection = $form.find("input[name='sort-direction']:checked").val(); | ||||||
|  |  | ||||||
|  |     await server.put(`notes/${parentNoteId}/sort-children`, {sortBy, sortDirection}); | ||||||
|  |  | ||||||
|  |     utils.closeActiveDialog(); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | export async function showDialog(noteId) { | ||||||
|  |     parentNoteId = noteId; | ||||||
|  |  | ||||||
|  |     utils.openDialog($dialog); | ||||||
|  |  | ||||||
|  |     $form.find('input:first').focus(); | ||||||
|  | } | ||||||
| @@ -75,7 +75,7 @@ class TreeContextMenu { | |||||||
|                     { title: 'Expand subtree <kbd data-command="expandSubtree"></kbd>', command: "expandSubtree", uiIcon: "expand", enabled: noSelectedNotes }, |                     { title: 'Expand subtree <kbd data-command="expandSubtree"></kbd>', command: "expandSubtree", uiIcon: "expand", enabled: noSelectedNotes }, | ||||||
|                     { title: 'Collapse subtree <kbd data-command="collapseSubtree"></kbd>', command: "collapseSubtree", uiIcon: "collapse", enabled: noSelectedNotes }, |                     { title: 'Collapse subtree <kbd data-command="collapseSubtree"></kbd>', command: "collapseSubtree", uiIcon: "collapse", enabled: noSelectedNotes }, | ||||||
|                     { title: "Force note sync", command: "forceNoteSync", uiIcon: "refresh", enabled: noSelectedNotes }, |                     { title: "Force note sync", command: "forceNoteSync", uiIcon: "refresh", enabled: noSelectedNotes }, | ||||||
|                     { title: 'Sort alphabetically <kbd data-command="sortChildNotes"></kbd>', command: "sortChildNotes", uiIcon: "empty", enabled: noSelectedNotes && notSearch }, |                     { title: 'Sort by ... <kbd data-command="sortChildNotes"></kbd>', command: "sortChildNotes", uiIcon: "empty", enabled: noSelectedNotes && notSearch }, | ||||||
|                     { title: 'Recent changes in subtree', command: "recentChangesInSubtree", uiIcon: "history", enabled: noSelectedNotes } |                     { title: 'Recent changes in subtree', command: "recentChangesInSubtree", uiIcon: "history", enabled: noSelectedNotes } | ||||||
|                 ] }, |                 ] }, | ||||||
|             { title: "----" }, |             { title: "----" }, | ||||||
|   | |||||||
| @@ -1366,7 +1366,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     sortChildNotesCommand({node}) { |     sortChildNotesCommand({node}) { | ||||||
|         treeService.sortAlphabetically(node.data.noteId); |         import("../dialogs/sort_child_notes.js").then(d => d.showDialog(node.data.noteId)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async recentChangesInSubtreeCommand({node}) { |     async recentChangesInSubtreeCommand({node}) { | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ const noteService = require('../../services/notes'); | |||||||
| const treeService = require('../../services/tree'); | const treeService = require('../../services/tree'); | ||||||
| const repository = require('../../services/repository'); | const repository = require('../../services/repository'); | ||||||
| const utils = require('../../services/utils'); | const utils = require('../../services/utils'); | ||||||
|  | const log = require('../../services/log'); | ||||||
| const TaskContext = require('../../services/task_context'); | const TaskContext = require('../../services/task_context'); | ||||||
|  |  | ||||||
| function getNote(req) { | function getNote(req) { | ||||||
| @@ -85,10 +86,20 @@ function undeleteNote(req) { | |||||||
|     taskContext.taskSucceeded(); |     taskContext.taskSucceeded(); | ||||||
| } | } | ||||||
|  |  | ||||||
| function sortNotes(req) { | function sortChildNotes(req) { | ||||||
|     const noteId = req.params.noteId; |     const noteId = req.params.noteId; | ||||||
|  |     const {sortBy, sortDirection} = req.body; | ||||||
|  |  | ||||||
|     treeService.sortNotesAlphabetically(noteId); |     log.info(`Sorting ${noteId} children with ${sortBy} ${sortDirection}`); | ||||||
|  |  | ||||||
|  |     const reverse = sortDirection === 'desc'; | ||||||
|  |  | ||||||
|  |     if (sortBy === 'title') { | ||||||
|  |         treeService.sortNotesByTitle(noteId, false, reverse); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         treeService.sortNotes(noteId, sortBy, reverse); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| function protectNote(req) { | function protectNote(req) { | ||||||
| @@ -215,7 +226,7 @@ module.exports = { | |||||||
|     deleteNote, |     deleteNote, | ||||||
|     undeleteNote, |     undeleteNote, | ||||||
|     createNote, |     createNote, | ||||||
|     sortNotes, |     sortChildNotes, | ||||||
|     protectNote, |     protectNote, | ||||||
|     setNoteTypeMime, |     setNoteTypeMime, | ||||||
|     getRelationMap, |     getRelationMap, | ||||||
|   | |||||||
| @@ -150,7 +150,7 @@ function register(app) { | |||||||
|     apiRoute(DELETE, '/api/notes/:noteId', notesApiRoute.deleteNote); |     apiRoute(DELETE, '/api/notes/:noteId', notesApiRoute.deleteNote); | ||||||
|     apiRoute(PUT, '/api/notes/:noteId/undelete', notesApiRoute.undeleteNote); |     apiRoute(PUT, '/api/notes/:noteId/undelete', notesApiRoute.undeleteNote); | ||||||
|     apiRoute(POST, '/api/notes/:parentNoteId/children', notesApiRoute.createNote); |     apiRoute(POST, '/api/notes/:parentNoteId/children', notesApiRoute.createNote); | ||||||
|     apiRoute(PUT, '/api/notes/:noteId/sort', notesApiRoute.sortNotes); |     apiRoute(PUT, '/api/notes/:noteId/sort-children', notesApiRoute.sortChildNotes); | ||||||
|     apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectNote); |     apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectNote); | ||||||
|     apiRoute(PUT, /\/api\/notes\/(.*)\/type\/(.*)\/mime\/(.*)/, notesApiRoute.setNoteTypeMime); |     apiRoute(PUT, /\/api\/notes\/(.*)\/type\/(.*)\/mime\/(.*)/, notesApiRoute.setNoteTypeMime); | ||||||
|     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); |     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); | ||||||
|   | |||||||
| @@ -359,7 +359,7 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|      * @method |      * @method | ||||||
|      * @param {string} parentNoteId - this note's child notes will be sorted |      * @param {string} parentNoteId - this note's child notes will be sorted | ||||||
|      */ |      */ | ||||||
|     this.sortNotesAlphabetically = treeService.sortNotesAlphabetically; |     this.sortNotesByTitle = treeService.sortNotesByTitle; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * This method finds note by its noteId and prefix and either sets it to the given parentNoteId |      * This method finds note by its noteId and prefix and either sets it to the given parentNoteId | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ eventService.subscribe(eventService.NOTE_TITLE_CHANGED, note => { | |||||||
|  |  | ||||||
|         for (const parentNote of noteFromCache.parents) { |         for (const parentNote of noteFromCache.parents) { | ||||||
|             if (parentNote.hasLabel("sorted")) { |             if (parentNote.hasLabel("sorted")) { | ||||||
|                 treeService.sortNotesAlphabetically(parentNote.noteId); |                 treeService.sortNotesByTitle(parentNote.noteId); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -84,14 +84,14 @@ eventService.subscribe(eventService.ENTITY_CREATED, ({ entityName, entity }) => | |||||||
|             noteService.duplicateSubtreeWithoutRoot(templateNote.noteId, note.noteId); |             noteService.duplicateSubtreeWithoutRoot(templateNote.noteId, note.noteId); | ||||||
|         } |         } | ||||||
|         else if (entity.type === 'label' && entity.name === 'sorted') { |         else if (entity.type === 'label' && entity.name === 'sorted') { | ||||||
|             treeService.sortNotesAlphabetically(entity.noteId); |             treeService.sortNotesByTitle(entity.noteId); | ||||||
|  |  | ||||||
|             if (entity.isInheritable) { |             if (entity.isInheritable) { | ||||||
|                 const note = noteCache.notes[entity.noteId]; |                 const note = noteCache.notes[entity.noteId]; | ||||||
|  |  | ||||||
|                 if (note) { |                 if (note) { | ||||||
|                     for (const noteId of note.subtreeNoteIds) { |                     for (const noteId of note.subtreeNoteIds) { | ||||||
|                         treeService.sortNotesAlphabetically(noteId); |                         treeService.sortNotesByTitle(noteId); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -463,7 +463,7 @@ async function importZip(taskContext, fileBuffer, importRootNote) { | |||||||
|         if (!metaFile) { |         if (!metaFile) { | ||||||
|             // if there's no meta file then the notes are created based on the order in that tar file but that |             // if there's no meta file then the notes are created based on the order in that tar file but that | ||||||
|             // is usually quite random so we sort the notes in the way they would appear in the file manager |             // is usually quite random so we sort the notes in the way they would appear in the file manager | ||||||
|             treeService.sortNotesAlphabetically(noteId, true); |             treeService.sortNotesByTitle(noteId, true); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         taskContext.increaseProgressCount(); |         taskContext.increaseProgressCount(); | ||||||
|   | |||||||
| @@ -106,7 +106,7 @@ function loadSubtreeNoteIds(parentNoteId, subtreeNoteIds) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| function sortNotesAlphabetically(parentNoteId, directoriesFirst = false) { | function sortNotesByTitle(parentNoteId, foldersFirst = false, reverse = false) { | ||||||
|     sql.transactional(() => { |     sql.transactional(() => { | ||||||
|         const notes = sql.getRows( |         const notes = sql.getRows( | ||||||
|             `SELECT branches.branchId, notes.noteId, title, isProtected,  |             `SELECT branches.branchId, notes.noteId, title, isProtected,  | ||||||
| @@ -120,7 +120,7 @@ function sortNotesAlphabetically(parentNoteId, directoriesFirst = false) { | |||||||
|         protectedSessionService.decryptNotes(notes); |         protectedSessionService.decryptNotes(notes); | ||||||
|  |  | ||||||
|         notes.sort((a, b) => { |         notes.sort((a, b) => { | ||||||
|             if (directoriesFirst && ((a.hasChildren && !b.hasChildren) || (!a.hasChildren && b.hasChildren))) { |             if (foldersFirst && ((a.hasChildren && !b.hasChildren) || (!a.hasChildren && b.hasChildren))) { | ||||||
|                 // exactly one note of the two is a directory so the sorting will be done based on this status |                 // exactly one note of the two is a directory so the sorting will be done based on this status | ||||||
|                 return a.hasChildren ? -1 : 1; |                 return a.hasChildren ? -1 : 1; | ||||||
|             } |             } | ||||||
| @@ -129,6 +129,10 @@ function sortNotesAlphabetically(parentNoteId, directoriesFirst = false) { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |         if (reverse) { | ||||||
|  |             notes.reverse(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         let position = 10; |         let position = 10; | ||||||
|  |  | ||||||
|         for (const note of notes) { |         for (const note of notes) { | ||||||
| @@ -144,6 +148,33 @@ function sortNotesAlphabetically(parentNoteId, directoriesFirst = false) { | |||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function sortNotes(parentNoteId, sortBy, reverse = false) { | ||||||
|  |     sql.transactional(() => { | ||||||
|  |         const notes = repository.getNote(parentNoteId).getChildNotes(); | ||||||
|  |  | ||||||
|  |         notes.sort((a, b) => a[sortBy] < b[sortBy] ? -1 : 1); | ||||||
|  |  | ||||||
|  |         if (reverse) { | ||||||
|  |             notes.reverse(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let position = 10; | ||||||
|  |  | ||||||
|  |         for (const note of notes) { | ||||||
|  |             const branch = note.getBranches().find(b => b.parentNoteId === parentNoteId); | ||||||
|  |  | ||||||
|  |             sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", | ||||||
|  |                 [position, branch.branchId]); | ||||||
|  |  | ||||||
|  |             noteCache.branches[branch.branchId].notePosition = position; | ||||||
|  |  | ||||||
|  |             position += 10; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         entityChangesService.addNoteReorderingEntityChange(parentNoteId); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @deprecated - this will be removed in the future |  * @deprecated - this will be removed in the future | ||||||
|  */ |  */ | ||||||
| @@ -194,6 +225,7 @@ function setNoteToParent(noteId, prefix, parentNoteId) { | |||||||
| module.exports = { | module.exports = { | ||||||
|     getNotes, |     getNotes, | ||||||
|     validateParentChild, |     validateParentChild, | ||||||
|     sortNotesAlphabetically, |     sortNotesByTitle, | ||||||
|  |     sortNotes, | ||||||
|     setNoteToParent |     setNoteToParent | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -39,6 +39,7 @@ | |||||||
| <%- include('dialogs/move_to.ejs') %> | <%- include('dialogs/move_to.ejs') %> | ||||||
| <%- include('dialogs/backend_log.ejs') %> | <%- include('dialogs/backend_log.ejs') %> | ||||||
| <%- include('dialogs/include_note.ejs') %> | <%- include('dialogs/include_note.ejs') %> | ||||||
|  | <%- include('dialogs/sort_child_notes.ejs') %> | ||||||
|  |  | ||||||
| <script type="text/javascript"> | <script type="text/javascript"> | ||||||
|     window.baseApiUrl = 'api/'; |     window.baseApiUrl = 'api/'; | ||||||
|   | |||||||
							
								
								
									
										60
									
								
								src/views/dialogs/sort_child_notes.ejs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/views/dialogs/sort_child_notes.ejs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | <div id="sort-child-notes-dialog" class="modal mx-auto" tabindex="-1" role="dialog"> | ||||||
|  |     <div class="modal-dialog modal-lg" style="max-width: 500px" role="document"> | ||||||
|  |         <div class="modal-content"> | ||||||
|  |             <div class="modal-header"> | ||||||
|  |                 <h5 class="modal-title mr-auto">Sort children by ...</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="sort-child-notes-form"> | ||||||
|  |                 <div class="modal-body"> | ||||||
|  |                     <h5>Sorting criteria</h5> | ||||||
|  |  | ||||||
|  |                     <div class="form-check"> | ||||||
|  |                         <input class="form-check-input" type="radio" name="sort-by" value="title" id="sort-by-title" checked> | ||||||
|  |                         <label class="form-check-label" for="sort-by-title"> | ||||||
|  |                             title | ||||||
|  |                         </label> | ||||||
|  |                     </div> | ||||||
|  |  | ||||||
|  |                     <div class="form-check"> | ||||||
|  |                         <input class="form-check-input" type="radio" name="sort-by" value="dateCreated" id="sort-by-date-created"> | ||||||
|  |                         <label class="form-check-label" for="sort-by-date-created"> | ||||||
|  |                             date created | ||||||
|  |                         </label> | ||||||
|  |                     </div> | ||||||
|  |  | ||||||
|  |                     <div class="form-check"> | ||||||
|  |                         <input class="form-check-input" type="radio" name="sort-by" value="dateModified" id="sort-by-date-modified"> | ||||||
|  |                         <label class="form-check-label" for="sort-by-date-modified"> | ||||||
|  |                             date modified | ||||||
|  |                         </label> | ||||||
|  |                     </div> | ||||||
|  |  | ||||||
|  |                     <br/> | ||||||
|  |  | ||||||
|  |                     <h5>Sorting direction</h5> | ||||||
|  |  | ||||||
|  |                     <div class="form-check"> | ||||||
|  |                         <input class="form-check-input" type="radio" name="sort-direction" value="asc" id="sort-direction-asc" checked> | ||||||
|  |                         <label class="form-check-label" for="sort-direction-asc"> | ||||||
|  |                             ascending | ||||||
|  |                         </label> | ||||||
|  |                     </div> | ||||||
|  |  | ||||||
|  |                     <div class="form-check"> | ||||||
|  |                         <input class="form-check-input" type="radio" name="sort-direction" value="desc" id="sort-direction-desc"> | ||||||
|  |                         <label class="form-check-label" for="sort-direction-desc"> | ||||||
|  |                             descending | ||||||
|  |                         </label> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="modal-footer"> | ||||||
|  |                     <button type="submit" class="btn btn-primary">Sort <kbd>enter</kbd></button> | ||||||
|  |                 </div> | ||||||
|  |             </form> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
		Reference in New Issue
	
	Block a user