mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 09:58:32 +02: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 $dialog = $("#delete-notes-dialog");
|
||||||
const $confirmContent = $("#delete-notes-dialog-content");
|
const $confirmContent = $("#delete-notes-dialog-content");
|
||||||
const $okButton = $("#delete-notes-dialog-ok-button");
|
const $okButton = $("#delete-notes-dialog-ok-button");
|
||||||
const $cancelButton = $("#delete-notes-dialog-cancel-button");
|
const $cancelButton = $("#delete-notes-dialog-cancel-button");
|
||||||
const $custom = $("#delete-notes-dialog-custom");
|
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";
|
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
|
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');
|
$originallyFocused = $(':focus');
|
||||||
|
|
||||||
$custom.hide();
|
$custom.hide();
|
||||||
|
|
||||||
glob.activeDialog = $dialog;
|
glob.activeDialog = $dialog;
|
||||||
|
|
||||||
if (typeof message === 'string') {
|
const response = await server.post('delete-notes-preview', {branchIdsToDelete});
|
||||||
message = $("<div>").text(message);
|
|
||||||
|
$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();
|
$dialog.modal();
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ async function deleteNotes(branchIdsToDelete) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const deleteNotesDialog = await import("../dialogs/delete_notes.js");
|
const deleteNotesDialog = await import("../dialogs/delete_notes.js");
|
||||||
deleteNotesDialog.showDialog();
|
deleteNotesDialog.showDialog(branchIdsToDelete);
|
||||||
|
|
||||||
const $deleteClonesCheckbox = $('<div class="form-check">')
|
const $deleteClonesCheckbox = $('<div class="form-check">')
|
||||||
.append($('<input type="checkbox" class="form-check-input" id="delete-clones-checkbox">'))
|
.append($('<input type="checkbox" class="form-check-input" id="delete-clones-checkbox">'))
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
const noteService = require('../../services/notes');
|
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 sql = require('../../services/sql');
|
||||||
const utils = require('../../services/utils');
|
const utils = require('../../services/utils');
|
||||||
const log = require('../../services/log');
|
const log = require('../../services/log');
|
||||||
const TaskContext = require('../../services/task_context');
|
const TaskContext = require('../../services/task_context');
|
||||||
@ -220,6 +221,58 @@ function eraseDeletedNotesNow() {
|
|||||||
noteService.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 = {
|
module.exports = {
|
||||||
getNote,
|
getNote,
|
||||||
updateNote,
|
updateNote,
|
||||||
@ -232,5 +285,6 @@ module.exports = {
|
|||||||
getRelationMap,
|
getRelationMap,
|
||||||
changeTitle,
|
changeTitle,
|
||||||
duplicateSubtree,
|
duplicateSubtree,
|
||||||
eraseDeletedNotesNow
|
eraseDeletedNotesNow,
|
||||||
|
getDeleteNotesPreview
|
||||||
};
|
};
|
||||||
|
@ -31,7 +31,7 @@ function getSubtreeSize(req) {
|
|||||||
|
|
||||||
const subTreeNoteIds = note.subtreeNotes.map(note => note.noteId);
|
const subTreeNoteIds = note.subtreeNotes.map(note => note.noteId);
|
||||||
|
|
||||||
sql.fillNoteIdList(subTreeNoteIds);
|
sql.fillParamList(subTreeNoteIds);
|
||||||
|
|
||||||
const subTreeSize = sql.getValue(`
|
const subTreeSize = sql.getValue(`
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -290,6 +290,8 @@ function register(app) {
|
|||||||
apiRoute(GET, '/api/stats/note-size/:noteId', statsRoute.getNoteSize);
|
apiRoute(GET, '/api/stats/note-size/:noteId', statsRoute.getNoteSize);
|
||||||
apiRoute(GET, '/api/stats/subtree-size/:noteId', statsRoute.getSubtreeSize);
|
apiRoute(GET, '/api/stats/subtree-size/:noteId', statsRoute.getSubtreeSize);
|
||||||
|
|
||||||
|
apiRoute(POST, '/api/delete-notes-preview', notesApiRoute.getDeleteNotesPreview);
|
||||||
|
|
||||||
app.use('', router);
|
app.use('', router);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,8 +246,8 @@ function transactional(func) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillNoteIdList(noteIds, truncate = true) {
|
function fillParamList(paramIds, truncate = true) {
|
||||||
if (noteIds.length === 0) {
|
if (paramIds.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,18 +255,18 @@ function fillNoteIdList(noteIds, truncate = true) {
|
|||||||
execute("DELETE FROM param_list");
|
execute("DELETE FROM param_list");
|
||||||
}
|
}
|
||||||
|
|
||||||
noteIds = Array.from(new Set(noteIds));
|
paramIds = Array.from(new Set(paramIds));
|
||||||
|
|
||||||
if (noteIds.length > 30000) {
|
if (paramIds.length > 30000) {
|
||||||
fillNoteIdList(noteIds.slice(30000), false);
|
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
|
// 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 = {
|
module.exports = {
|
||||||
@ -287,5 +287,5 @@ module.exports = {
|
|||||||
executeScript,
|
executeScript,
|
||||||
transactional,
|
transactional,
|
||||||
upsert,
|
upsert,
|
||||||
fillNoteIdList
|
fillParamList
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div id="delete-notes-dialog" class="modal mx-auto" tabindex="-1" role="dialog">
|
<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-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title mr-auto">Delete notes</h5>
|
<h5 class="modal-title mr-auto">Delete notes</h5>
|
||||||
@ -9,7 +9,23 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<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>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-sm" id="delete-notes-dialog-cancel-button">Cancel</button>
|
<button class="btn btn-sm" id="delete-notes-dialog-cancel-button">Cancel</button>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user