Fix: batch toast spam on trash drag, restore icon, stale ref, reorder safety

Trash drop handler now uses noToast + single message. Restore button icon changed to ♻️. Removed stale .view-toggle reference in click-outside handler. Added safety check in reorder drop handler to prevent entries being lost from order array.
This commit is contained in:
2026-05-09 17:55:52 +01:00
parent 0b3f886337
commit 7e623067b0
+14 -10
View File
@@ -692,7 +692,7 @@ function render() {
} else {
h += '<td><span class="trash-badge">🗑️ ' + timeAgo(e.deleted_at) + '</span></td>' +
'<td class="actions-cell">' +
'<button class="restore-btn" onclick="restoreEntry(' + e.id + ')"></button> ' +
'<button class="restore-btn" onclick="restoreEntry(' + e.id + ')"></button> ' +
'<button class="delete-btn" data-id="' + e.id + '" style="position:static;display:inline-flex;vertical-align:middle;">✕</button>' +
'</td>';
}
@@ -720,7 +720,7 @@ function gridC(e) {
let html = '<div class="entry-card' + (e.favorite ? ' favorite' : '') + (sel ? ' selected' : '') + '" draggable="true" data-id="' + e.id + '" onclick="toggleSelectEntry(' + e.id + ', event)">';
html += '<div class="action-btns">';
if (showTrash) {
html += '<button class="restore-btn" onclick="restoreEntry(' + e.id + ')"></button>';
html += '<button class="restore-btn" onclick="restoreEntry(' + e.id + ')"></button>';
html += '<button class="delete-btn" data-id="' + e.id + '">✕</button>';
} else {
html += '<button class="star-btn" data-id="' + e.id + '">' + (e.favorite ? '⭐' : '☆') + '</button>';
@@ -745,7 +745,7 @@ function listC(e) {
let html = '<div class="entry-row' + (e.favorite ? ' favorite' : '') + (sel ? ' selected' : '') + '" draggable="true" data-id="' + e.id + '" onclick="toggleSelectEntry(' + e.id + ', event)">';
html += '<div class="action-btns">';
if (showTrash) {
html += '<button class="restore-btn" onclick="restoreEntry(' + e.id + ')"></button>';
html += '<button class="restore-btn" onclick="restoreEntry(' + e.id + ')"></button>';
html += '<button class="delete-btn" data-id="' + e.id + '">✕</button>';
} else {
html += '<button class="star-btn" data-id="' + e.id + '">' + (e.favorite ? '⭐' : '☆') + '</button>';
@@ -770,7 +770,7 @@ function compC(e) {
let html = '<div class="entry-compact' + (e.favorite ? ' favorite' : '') + (sel ? ' selected' : '') + '" draggable="true" data-id="' + e.id + '" onclick="toggleSelectEntry(' + e.id + ', event)">';
html += '<div class="action-btns">';
if (showTrash) {
html += '<button class="restore-btn" onclick="restoreEntry(' + e.id + ')"></button>';
html += '<button class="restore-btn" onclick="restoreEntry(' + e.id + ')"></button>';
html += '<button class="delete-btn" data-id="' + e.id + '">✕</button>';
} else {
html += '<button class="star-btn" data-id="' + e.id + '">' + (e.favorite ? '⭐' : '☆') + '</button>';
@@ -806,7 +806,7 @@ function groupedC(list) {
html += '<div class="entry-row' + (e.favorite ? ' favorite' : '') + (sel ? ' selected' : '') + '" draggable="true" data-id="' + e.id + '" onclick="toggleSelectEntry(' + e.id + ', event)">';
html += '<div class="action-btns">';
if (showTrash) {
html += '<button class="restore-btn" onclick="restoreEntry(' + e.id + ')"></button>';
html += '<button class="restore-btn" onclick="restoreEntry(' + e.id + ')"></button>';
html += '<button class="delete-btn" data-id="' + e.id + '">✕</button>';
} else {
html += '<button class="star-btn" data-id="' + e.id + '">' + (e.favorite ? '⭐' : '☆') + '</button>';
@@ -854,7 +854,7 @@ function detailC(list) {
} else {
html += '<div class="detail-field"><span class="detail-label">Deleted</span><span class="trash-badge">🗑️ ' + timeAgo(e.deleted_at) + '</span></div>';
html += '<div class="detail-actions">' +
'<button class="restore-btn" onclick="restoreEntry(' + e.id + ')"> Restore</button>' +
'<button class="restore-btn" onclick="restoreEntry(' + e.id + ')"> Restore</button>' +
'<button class="delete-btn" data-id="' + e.id + '">✕ Delete</button>' +
'</div>';
}
@@ -962,7 +962,8 @@ function setupDrag() {
el.ondragend = function(e) { c.querySelectorAll('.drag-dim').forEach(card => card.classList.remove('drag-dim')); draggedId = null; c.querySelectorAll('.drag-over').forEach(x => x.classList.remove('drag-over')); document.getElementById('trashBtn')?.classList.remove('drag-over'); };
el.ondragover = function(e) { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; if (this.dataset.id !== draggedId) this.classList.add('drag-over'); };
el.ondragleave = function(e) { this.classList.remove('drag-over'); };
el.ondrop = function(e) { e.preventDefault(); e.stopPropagation(); this.classList.remove('drag-over'); const fromId = parseInt(e.dataTransfer.getData('text/plain')); const toId = parseInt(this.dataset.id); if (!fromId || !toId) return; const ids = selectedIds.has(fromId) && selectedIds.size > 1 ? [...selectedIds] : [fromId]; if (ids.length === 1 && ids[0] === toId) return; const base = order.length > 0 ? order : entries.filter(e => e).map(e => e.id); const filtered = base.filter(id => !ids.includes(id)); const idx = filtered.indexOf(toId); idx > -1 ? filtered.splice(idx, 0, ...ids) : filtered.push(...ids); order = filtered; localStorage.setItem('entryOrder', JSON.stringify(order)); const map = new Map(entries.filter(e => e).map(e => [e.id, e])); entries = order.map(id => map.get(id)).filter(e => e); entries.sort((a, b) => (b.favorite || 0) - (a.favorite || 0)); render(); };
el.ondrop = function(e) { e.preventDefault(); e.stopPropagation(); this.classList.remove('drag-over'); const fromId = parseInt(e.dataTransfer.getData('text/plain')); const toId = parseInt(this.dataset.id); if (!fromId || !toId) return; const ids = selectedIds.has(fromId) && selectedIds.size > 1 ? [...selectedIds] : [fromId]; if (ids.length === 1 && ids[0] === toId) return; const base = order.length > 0 ? order : entries.filter(e => e).map(e => e.id); const filtered = base.filter(id => !ids.includes(id)); const idx = filtered.indexOf(toId); idx > -1 ? filtered.splice(idx, 0, ...ids) : filtered.push(...ids); order = filtered; // Ensure no entries are lost from order
entries.forEach(e => { if (!order.includes(e.id)) order.push(e.id); }); localStorage.setItem('entryOrder', JSON.stringify(order)); const map = new Map(entries.filter(e => e).map(e => [e.id, e])); entries = order.map(id => map.get(id)).filter(e => e); entries.sort((a, b) => (b.favorite || 0) - (a.favorite || 0)); render(); };
});
}
@@ -1043,7 +1044,7 @@ function updateBatchBar() {
bar.innerHTML = `<span class="selected-count">${selectedIds.size} selected</span>`;
if (showTrash) {
bar.innerHTML += `
<button class="btn btn-sm restore-btn" onclick="batchRestore()"> Restore All</button>
<button class="btn btn-sm restore-btn" onclick="batchRestore()"> Restore All</button>
<button class="btn btn-sm btn-danger" onclick="batchPermanentDelete()">🗑️ Delete</button>
`;
} else {
@@ -1238,8 +1239,11 @@ if (token && curUser) {
const id = parseInt(e.dataTransfer.getData('text/plain'));
if (!id) return;
const ids = selectedIds.has(id) && selectedIds.size > 1 ? [...selectedIds] : [id];
for (const sid of ids) await delEntry(sid);
for (const sid of ids) await delEntry(sid, true);
toast('📦 Moved ' + ids.length + ' entries to trash');
playSound('delete');
clearSelection();
await loadEntries();
});
}
})();
@@ -1253,7 +1257,7 @@ document.getElementById('entriesContainer').addEventListener('dragstart', functi
document.addEventListener('mousedown', function(e) {
if (e.button !== 0 || rectState.active) return;
if (e.target?.closest?.('.entry-card,.entry-row,.entry-compact,.table-row-drag,.detail-card,.detail-nav,#batchBar,.custom-modal-overlay.show,.edit-modal.show,.modal-overlay.show,#genModal,#settingsMenu')) return;
if (e.target?.closest?.('button,input,select,.folders-bar,.toolbar,#trashActions,.settings-dropdown,.fab,.view-toggle,.auth-section')) {
if (e.target?.closest?.('button,input,select,.folders-bar,.toolbar,#trashActions,.settings-dropdown,.fab,.auth-section')) {
if (selectedIds.size > 0) clearSelection();
return;
}