mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 13:01:31 +08:00 
			
		
		
		
	delete notes preview dialog WIP
This commit is contained in:
		
							parent
							
								
									ec2f8ec796
								
							
						
					
					
						commit
						5b72b577b8
					
				| @ -1,25 +1,61 @@ | ||||
| import server from "../services/server.js"; | ||||
| import treeCache from "../services/tree_cache.js"; | ||||
| import linkService from "../services/link.js"; | ||||
| 
 | ||||
| const $dialog = $("#delete-notes-dialog"); | ||||
| const $confirmContent = $("#delete-notes-dialog-content"); | ||||
| const $okButton = $("#delete-notes-dialog-ok-button"); | ||||
| const $cancelButton = $("#delete-notes-dialog-cancel-button"); | ||||
| const $custom = $("#delete-notes-dialog-custom"); | ||||
| const $deleteNotesList = $("#delete-notes-list"); | ||||
| const $brokenRelationsList = $("#broken-relations-list"); | ||||
| const $deletedNotesCount = $("#deleted-notes-count"); | ||||
| const $noNoteToDeleteWrapper = $("#no-note-to-delete-wrapper"); | ||||
| const $deleteNotesListWrapper = $("#delete-notes-list-wrapper"); | ||||
| const $brokenRelationsListWrapper = $("#broken-relations-wrapper"); | ||||
| 
 | ||||
| const DELETE_NOTE_BUTTON_ID = "delete-notes-dialog-delete-note"; | ||||
| 
 | ||||
| let $originallyFocused; // element focused before the dialog was opened so we can return to it afterwards
 | ||||
| 
 | ||||
| export function showDialog(message) { | ||||
| export async function showDialog(branchIdsToDelete) { | ||||
|     $originallyFocused = $(':focus'); | ||||
| 
 | ||||
|     $custom.hide(); | ||||
| 
 | ||||
|     glob.activeDialog = $dialog; | ||||
| 
 | ||||
|     if (typeof message === 'string') { | ||||
|         message = $("<div>").text(message); | ||||
|     const response = await server.post('delete-notes-preview', {branchIdsToDelete}); | ||||
| 
 | ||||
|     $deleteNotesList.empty(); | ||||
|     $brokenRelationsList.empty(); | ||||
| 
 | ||||
|     $deleteNotesListWrapper.toggle(response.noteIdsToBeDeleted.length > 0); | ||||
|     $noNoteToDeleteWrapper.toggle(response.noteIdsToBeDeleted.length === 0); | ||||
| 
 | ||||
|     for (const note of await treeCache.getNotes(response.noteIdsToBeDeleted)) { | ||||
|         $deleteNotesList.append( | ||||
|             $("<li>").append( | ||||
|                 await linkService.createNoteLink(note.noteId, {showNotePath: true}) | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     $confirmContent.empty().append(message); | ||||
|     $deletedNotesCount.text(response.noteIdsToBeDeleted.length); | ||||
| 
 | ||||
|     $brokenRelationsListWrapper.toggle(response.brokenRelations.length > 0); | ||||
| 
 | ||||
|     await treeCache.getNotes(response.brokenRelations.map(br => br.noteId)); | ||||
| 
 | ||||
|     for (const attr of response.brokenRelations) { | ||||
|         $brokenRelationsList.append( | ||||
|             $("<li>") | ||||
|                 .append(`Note `) | ||||
|                 .append(await linkService.createNoteLink(attr.value)) | ||||
|                 .append(` (to be deleted) is referenced by relation <code>${attr.name}</code> originating from `) | ||||
|                 .append(await linkService.createNoteLink(attr.noteId)) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     $dialog.modal(); | ||||
| 
 | ||||
|  | ||||
| @ -75,7 +75,7 @@ async function deleteNotes(branchIdsToDelete) { | ||||
|     } | ||||
| 
 | ||||
|     const deleteNotesDialog = await import("../dialogs/delete_notes.js"); | ||||
|     deleteNotesDialog.showDialog(); | ||||
|     deleteNotesDialog.showDialog(branchIdsToDelete); | ||||
| 
 | ||||
|     const $deleteClonesCheckbox = $('<div class="form-check">') | ||||
|         .append($('<input type="checkbox" class="form-check-input" id="delete-clones-checkbox">')) | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
| const noteService = require('../../services/notes'); | ||||
| const treeService = require('../../services/tree'); | ||||
| const repository = require('../../services/repository'); | ||||
| const sql = require('../../services/sql'); | ||||
| const utils = require('../../services/utils'); | ||||
| const log = require('../../services/log'); | ||||
| const TaskContext = require('../../services/task_context'); | ||||
| @ -220,6 +221,58 @@ function eraseDeletedNotesNow() { | ||||
|     noteService.eraseDeletedNotesNow(); | ||||
| } | ||||
| 
 | ||||
| function getDeleteNotesPreview(req) { | ||||
|     const {branchIdsToDelete} = req.body; | ||||
| 
 | ||||
|     const noteIdsToBeDeleted = new Set(); | ||||
|     const branchCountToDelete = {}; // noteId => count (integer)
 | ||||
| 
 | ||||
|     function branchPreviewDeletion(branch) { | ||||
|         branchCountToDelete[branch.branchId] = branchCountToDelete[branch.branchId] || 0; | ||||
|         branchCountToDelete[branch.branchId]++; | ||||
| 
 | ||||
|         const note = branch.getNote(); | ||||
| 
 | ||||
|         if (note.getBranches().length <= branchCountToDelete[branch.branchId]) { | ||||
|             noteIdsToBeDeleted.add(note.noteId); | ||||
| 
 | ||||
|             for (const childBranch of note.getChildBranches()) { | ||||
|                 branchPreviewDeletion(childBranch); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     for (const branchId of branchIdsToDelete) { | ||||
|         const branch = repository.getBranch(branchId); | ||||
| 
 | ||||
|         if (!branch) { | ||||
|             log.error(`Branch ${branchId} was not found and delete preview can't be calculated for this note.`); | ||||
| 
 | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         branchPreviewDeletion(branch); | ||||
|     } | ||||
| 
 | ||||
|     let brokenRelations = []; | ||||
| 
 | ||||
|     if (noteIdsToBeDeleted.length > 0) { | ||||
|         sql.fillParamList(noteIdsToBeDeleted); | ||||
| 
 | ||||
|         brokenRelations = sql.getRows(` | ||||
|             SELECT attr.noteId, attr.name, attr.value | ||||
|             FROM attributes attr | ||||
|                      JOIN param_list ON param_list.paramId = attr.value | ||||
|             WHERE attr.isDeleted = 0 | ||||
|               AND attr.type = 'relation'`).filter(attr => !noteIdsToBeDeleted.has(attr.noteId));
 | ||||
|     } | ||||
| 
 | ||||
|     return { | ||||
|         noteIdsToBeDeleted: Array.from(noteIdsToBeDeleted), | ||||
|         brokenRelations | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
|     getNote, | ||||
|     updateNote, | ||||
| @ -232,5 +285,6 @@ module.exports = { | ||||
|     getRelationMap, | ||||
|     changeTitle, | ||||
|     duplicateSubtree, | ||||
|     eraseDeletedNotesNow | ||||
|     eraseDeletedNotesNow, | ||||
|     getDeleteNotesPreview | ||||
| }; | ||||
|  | ||||
| @ -31,7 +31,7 @@ function getSubtreeSize(req) { | ||||
| 
 | ||||
|     const subTreeNoteIds = note.subtreeNotes.map(note => note.noteId); | ||||
| 
 | ||||
|     sql.fillNoteIdList(subTreeNoteIds); | ||||
|     sql.fillParamList(subTreeNoteIds); | ||||
| 
 | ||||
|     const subTreeSize = sql.getValue(` | ||||
|         SELECT | ||||
|  | ||||
| @ -290,6 +290,8 @@ function register(app) { | ||||
|     apiRoute(GET, '/api/stats/note-size/:noteId', statsRoute.getNoteSize); | ||||
|     apiRoute(GET, '/api/stats/subtree-size/:noteId', statsRoute.getSubtreeSize); | ||||
| 
 | ||||
|     apiRoute(POST, '/api/delete-notes-preview', notesApiRoute.getDeleteNotesPreview); | ||||
| 
 | ||||
|     app.use('', router); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -246,8 +246,8 @@ function transactional(func) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function fillNoteIdList(noteIds, truncate = true) { | ||||
|     if (noteIds.length === 0) { | ||||
| function fillParamList(paramIds, truncate = true) { | ||||
|     if (paramIds.length === 0) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
| @ -255,18 +255,18 @@ function fillNoteIdList(noteIds, truncate = true) { | ||||
|         execute("DELETE FROM param_list"); | ||||
|     } | ||||
| 
 | ||||
|     noteIds = Array.from(new Set(noteIds)); | ||||
|     paramIds = Array.from(new Set(paramIds)); | ||||
| 
 | ||||
|     if (noteIds.length > 30000) { | ||||
|         fillNoteIdList(noteIds.slice(30000), false); | ||||
|     if (paramIds.length > 30000) { | ||||
|         fillParamList(paramIds.slice(30000), false); | ||||
| 
 | ||||
|         noteIds = noteIds.slice(0, 30000); | ||||
|         paramIds = paramIds.slice(0, 30000); | ||||
|     } | ||||
| 
 | ||||
|     // doing it manually to avoid this showing up on the sloq query list
 | ||||
|     const s = stmt(`INSERT INTO param_list VALUES ` + noteIds.map(noteId => `(?)`).join(','), noteIds); | ||||
|     const s = stmt(`INSERT INTO param_list VALUES ` + paramIds.map(paramId => `(?)`).join(','), paramIds); | ||||
| 
 | ||||
|     s.run(noteIds); | ||||
|     s.run(paramIds); | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
| @ -287,5 +287,5 @@ module.exports = { | ||||
|     executeScript, | ||||
|     transactional, | ||||
|     upsert, | ||||
|     fillNoteIdList | ||||
|     fillParamList | ||||
| }; | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <div id="delete-notes-dialog" class="modal mx-auto" tabindex="-1" role="dialog"> | ||||
|     <div class="modal-dialog modal-dialog-scrollable" role="document"> | ||||
|     <div class="modal-dialog modal-dialog-scrollable modal-xl" role="document"> | ||||
|         <div class="modal-content"> | ||||
|             <div class="modal-header"> | ||||
|                 <h5 class="modal-title mr-auto">Delete notes</h5> | ||||
| @ -9,7 +9,23 @@ | ||||
|                 </button> | ||||
|             </div> | ||||
|             <div class="modal-body"> | ||||
|                 ... delete | ||||
|                 <div id="delete-notes-list-wrapper"> | ||||
|                     <h5>Following notes will be deleted (<span id="deleted-notes-count"></span>)</h5> | ||||
| 
 | ||||
|                     <ul id="delete-notes-list" style="max-height: 400px; overflow: auto;"></ul> | ||||
|                 </div> | ||||
| 
 | ||||
|                 <div id="no-note-to-delete-wrapper"> | ||||
|                     <strong>No note will be deleted (only clones).</strong> | ||||
|                 </div> | ||||
| 
 | ||||
|                 <div id="broken-relations-wrapper"> | ||||
|                     <h5>Broken relations</h5> | ||||
| 
 | ||||
|                     Below can be seen relations which will be broken after notes mentioned above are deleted. | ||||
| 
 | ||||
|                     <ul id="broken-relations-list" style="max-height: 400px; overflow: auto;"></ul> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="modal-footer"> | ||||
|                 <button class="btn btn-sm" id="delete-notes-dialog-cancel-button">Cancel</button> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zadam
						zadam