mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
Merge branch 'sort-by'
This commit is contained in:
commit
2c958eaacb
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>
|
Loading…
x
Reference in New Issue
Block a user