diff --git a/src/becca/entities/note.js b/src/becca/entities/note.js index 9f938d412..316f87f5e 100644 --- a/src/becca/entities/note.js +++ b/src/becca/entities/note.js @@ -132,6 +132,10 @@ class Note extends AbstractEntity { return this.children; } + hasChildren() { + return this.children && this.children.length > 0; + } + getChildBranches() { return this.children.map(childNote => this.becca.getBranchFromChildAndParent(childNote.noteId, this.noteId)); } diff --git a/src/routes/api/branches.js b/src/routes/api/branches.js index 47da2b75d..8bf5c623e 100644 --- a/src/routes/api/branches.js +++ b/src/routes/api/branches.js @@ -96,12 +96,10 @@ function moveBranchBeforeNote(req) { branchToMove.markAsDeleted(); } - if (parentNote.hasLabel('sorted')) { - treeService.sortNotesByTitle(parentNote.noteId, false, false); - } - else { - entityChangesService.addNoteReorderingEntityChange(parentNote.noteId); - } + treeService.sortNotesIfNeeded(parentNote.noteId); + + // if sorting is not needed then still the ordering might have changed above manually + entityChangesService.addNoteReorderingEntityChange(parentNote.noteId); return { success: true }; } @@ -147,12 +145,10 @@ function moveBranchAfterNote(req) { branchToMove.markAsDeleted(); } - if (parentNote.hasLabel('sorted')) { - treeService.sortNotesByTitle(parentNote.noteId, false, false); - } - else { - entityChangesService.addNoteReorderingEntityChange(parentNote.noteId); - } + treeService.sortNotesIfNeeded(parentNote.noteId); + + // if sorting is not needed then still the ordering might have changed above manually + entityChangesService.addNoteReorderingEntityChange(parentNote.noteId); return { success: true }; } diff --git a/src/routes/api/notes.js b/src/routes/api/notes.js index 95cb5b434..2014cc409 100644 --- a/src/routes/api/notes.js +++ b/src/routes/api/notes.js @@ -97,12 +97,7 @@ function sortChildNotes(req) { const reverse = sortDirection === 'desc'; - if (sortBy === 'title') { - treeService.sortNotesByTitle(noteId, false, reverse); - } - else { - treeService.sortNotes(noteId, sortBy, reverse); - } + treeService.sortNotes(noteId, sortBy, reverse); } function protectNote(req) { diff --git a/src/services/backend_script_api.js b/src/services/backend_script_api.js index fd8f65a87..10da7c63c 100644 --- a/src/services/backend_script_api.js +++ b/src/services/backend_script_api.js @@ -352,7 +352,7 @@ function BackendScriptApi(currentNote, apiParams) { * @method * @param {string} parentNoteId - this note's child notes will be sorted */ - this.sortNotesByTitle = treeService.sortNotesByTitle; + this.sortNotesByTitle = parentNoteId => treeService.sortNotes(parentNoteId); /** * This method finds note by its noteId and prefix and either sets it to the given parentNoteId diff --git a/src/services/handlers.js b/src/services/handlers.js index 9a2c9260e..d1c03bbce 100644 --- a/src/services/handlers.js +++ b/src/services/handlers.js @@ -30,7 +30,7 @@ eventService.subscribe(eventService.NOTE_TITLE_CHANGED, note => { for (const parentNote of noteFromCache.parents) { if (parentNote.hasLabel("sorted")) { - treeService.sortNotesByTitle(parentNote.noteId); + treeService.sortNotesIfNeeded(parentNote.noteId); } } } @@ -83,14 +83,14 @@ eventService.subscribe(eventService.ENTITY_CREATED, ({ entityName, entity }) => } } else if (entity.type === 'label' && entity.name === 'sorted') { - treeService.sortNotesByTitle(entity.noteId); + treeService.sortNotesIfNeeded(entity.noteId); if (entity.isInheritable) { const note = becca.notes[entity.noteId]; if (note) { for (const noteId of note.getSubtreeNoteIds()) { - treeService.sortNotesByTitle(noteId); + treeService.sortNotesIfNeeded(noteId); } } } diff --git a/src/services/import/zip.js b/src/services/import/zip.js index f8e127c41..a1fc049ac 100644 --- a/src/services/import/zip.js +++ b/src/services/import/zip.js @@ -469,9 +469,9 @@ async function importZip(taskContext, fileBuffer, importRootNote) { noteService.scanForLinks(becca.getNote(noteId)); 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 zip file but that // is usually quite random so we sort the notes in the way they would appear in the file manager - treeService.sortNotesByTitle(noteId, true); + treeService.sortNotes(noteId, 'title', false, true); } taskContext.increaseProgressCount(); diff --git a/src/services/tree.js b/src/services/tree.js index 836d4421c..a5cbb5430 100644 --- a/src/services/tree.js +++ b/src/services/tree.js @@ -108,58 +108,32 @@ function loadSubtreeNoteIds(parentNoteId, subtreeNoteIds) { } } -function sortNotesByTitle(parentNoteId, foldersFirst = false, reverse = false) { - sql.transactional(() => { - const notes = sql.getRows( - `SELECT branches.branchId, notes.noteId, title, isProtected, - CASE WHEN COUNT(childBranches.noteId) > 0 THEN 1 ELSE 0 END AS hasChildren - FROM notes - JOIN branches ON branches.noteId = notes.noteId - LEFT JOIN branches childBranches ON childBranches.parentNoteId = notes.noteId AND childBranches.isDeleted = 0 - WHERE branches.isDeleted = 0 AND branches.parentNoteId = ? - GROUP BY notes.noteId`, [parentNoteId]); +function sortNotes(parentNoteId, sortBy = 'title', reverse = false, foldersFirst = false) { + if (!sortBy) { + sortBy = 'title'; + } - protectedSessionService.decryptNotes(notes); - - notes.sort((a, b) => { - 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 - return a.hasChildren ? -1 : 1; - } - else { - return a.title.toLowerCase() < b.title.toLowerCase() ? -1 : 1; - } - }); - - if (reverse) { - notes.reverse(); - } - - let position = 10; - - for (const note of notes) { - sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", - [position, note.branchId]); - - if (note.branchId in becca.branches) { - becca.branches[note.branchId].notePosition = position; - } - else { - log.info(`Branch "${note.branchId}" was not found in becca.`); - } - - position += 10; - } - - entityChangesService.addNoteReorderingEntityChange(parentNoteId); - }); -} - -function sortNotes(parentNoteId, sortBy, reverse = false) { sql.transactional(() => { const notes = becca.getNote(parentNoteId).getChildNotes(); - notes.sort((a, b) => a[sortBy] < b[sortBy] ? -1 : 1); + const normalize = obj => (obj && typeof obj === 'string') ? obj.toLowerCase() : obj; + + notes.sort((a, b) => { + if (foldersFirst) { + const aHasChildren = a.hasChildren(); + const bHasChildren = b.hasChildren(); + + if ((aHasChildren && !bHasChildren) || (!aHasChildren && bHasChildren)) { + // exactly one note of the two is a directory so the sorting will be done based on this status + return aHasChildren ? -1 : 1; + } + } + + let aEl = normalize(a[sortBy]); + let bEl = normalize(b[sortBy]); + + return aEl < bEl ? -1 : 1; + }); if (reverse) { notes.reverse(); @@ -182,6 +156,22 @@ function sortNotes(parentNoteId, sortBy, reverse = false) { }); } +function sortNotesIfNeeded(parentNoteId) { + const parentNote = becca.getNote(parentNoteId); + + if (!parentNote) { + return; + } + + const sortedLabel = parentNote.getLabel('sorted'); + + if (!sortedLabel || sortedLabel.value === 'off') { + return; + } + + sortNotes(parentNoteId, sortedLabel.value); +} + /** * @deprecated - this will be removed in the future */ @@ -233,7 +223,7 @@ function setNoteToParent(noteId, prefix, parentNoteId) { module.exports = { getNotes, validateParentChild, - sortNotesByTitle, sortNotes, + sortNotesIfNeeded, setNoteToParent };