fix(documents): refresh library counters after removal (#1924)

This commit is contained in:
Marius Popa
2026-06-04 06:42:23 +03:00
committed by GitHub
parent 1c43daa564
commit 666babfd58
2 changed files with 67 additions and 7 deletions

View File

@@ -391,6 +391,27 @@ let _libraryArchivedView = false; // Documents tab showing archived docs?
} }
} }
function libraryRemoveDocumentFromState(docId) {
const removed = _libraryDocs.find(d => String(d.id) === String(docId));
_libraryDocs = _libraryDocs.filter(d => String(d.id) !== String(docId));
_librarySelectedIds.delete(docId);
_libraryTotal = Math.max(0, _libraryTotal - 1);
const lang = removed && (removed.language || 'text');
if (lang && Object.prototype.hasOwnProperty.call(_libraryLanguages, lang)) {
const next = Math.max(0, Number(_libraryLanguages[lang] || 0) - 1);
if (next > 0) {
_libraryLanguages[lang] = next;
} else {
delete _libraryLanguages[lang];
}
}
libraryRenderStats();
libraryRenderLangChips();
libraryUpdateBulkCount();
}
function libraryRenderGrid() { function libraryRenderGrid() {
const grid = document.getElementById('doclib-grid'); const grid = document.getElementById('doclib-grid');
if (!grid) return; if (!grid) return;
@@ -709,8 +730,7 @@ let _libraryArchivedView = false; // Documents tab showing archived docs?
const res = await fetch(`${API_BASE}/api/document/${doc.id}/archive?archived=${toArchived}`, { method: 'POST', credentials: 'same-origin' }); const res = await fetch(`${API_BASE}/api/document/${doc.id}/archive?archived=${toArchived}`, { method: 'POST', credentials: 'same-origin' });
if (!res.ok) throw new Error('failed'); if (!res.ok) throw new Error('failed');
// Drop it from the current view (it no longer belongs here) and refresh. // Drop it from the current view (it no longer belongs here) and refresh.
_libraryDocs = _libraryDocs.filter(d => d.id !== doc.id); libraryRemoveDocumentFromState(doc.id);
_libraryTotal = Math.max(0, _libraryTotal - 1);
libraryRenderGrid(); libraryRenderGrid();
if (uiModule) uiModule.showToast(toArchived ? 'Archived' : 'Restored'); if (uiModule) uiModule.showToast(toArchived ? 'Archived' : 'Restored');
} catch { if (uiModule) uiModule.showError('Failed to ' + (toArchived ? 'archive' : 'restore')); } } catch { if (uiModule) uiModule.showError('Failed to ' + (toArchived ? 'archive' : 'restore')); }
@@ -802,8 +822,7 @@ let _libraryArchivedView = false; // Documents tab showing archived docs?
try { try {
const res = await fetch(`${API_BASE}/api/document/${doc.id}/archive?archived=${toArchived}`, { method: 'POST', credentials: 'same-origin' }); const res = await fetch(`${API_BASE}/api/document/${doc.id}/archive?archived=${toArchived}`, { method: 'POST', credentials: 'same-origin' });
if (!res.ok) throw new Error('failed'); if (!res.ok) throw new Error('failed');
_libraryDocs = _libraryDocs.filter(d => d.id !== doc.id); libraryRemoveDocumentFromState(doc.id);
_libraryTotal = Math.max(0, _libraryTotal - 1);
libraryRenderGrid(); libraryRenderGrid();
if (uiModule) uiModule.showToast(toArchived ? 'Archived' : 'Restored'); if (uiModule) uiModule.showToast(toArchived ? 'Archived' : 'Restored');
} catch { if (uiModule) uiModule.showError('Failed to ' + (toArchived ? 'archive' : 'restore')); } } catch { if (uiModule) uiModule.showError('Failed to ' + (toArchived ? 'archive' : 'restore')); }
@@ -1170,9 +1189,7 @@ let _libraryArchivedView = false; // Documents tab showing archived docs?
card.addEventListener('transitionend', () => card.remove(), { once: true }); card.addEventListener('transitionend', () => card.remove(), { once: true });
setTimeout(() => { if (card.parentElement) card.remove(); }, 400); setTimeout(() => { if (card.parentElement) card.remove(); }, 400);
} }
_libraryDocs = _libraryDocs.filter(d => d.id !== docId); libraryRemoveDocumentFromState(docId);
_libraryTotal = Math.max(0, _libraryTotal - 1);
libraryRenderStats();
if (uiModule) uiModule.showToast('Document deleted'); if (uiModule) uiModule.showToast('Document deleted');
} catch (e) { } catch (e) {
if (uiModule) uiModule.showError(`Failed to delete document: ${e.message || e}`); if (uiModule) uiModule.showError(`Failed to delete document: ${e.message || e}`);

View File

@@ -0,0 +1,43 @@
"""Regression for #1809: document library counters must update after delete.
documentLibrary.js is a browser module with several DOM-only imports, so this
guards the relevant wiring at the source level. A single-card delete used to
remove the card and decrement `_libraryTotal`, but the header/chips render from
`_libraryLanguages`, which stayed stale until a full library refetch.
"""
from pathlib import Path
SRC = Path(__file__).resolve().parent.parent / "static/js/documentLibrary.js"
def _src() -> str:
return SRC.read_text(encoding="utf-8")
def _between(text: str, start: str, end: str) -> str:
begin = text.index(start)
finish = text.index(end, begin)
return text[begin:finish]
def test_single_delete_updates_language_counters_and_chips():
text = _src()
helper = _between(
text,
"function libraryRemoveDocumentFromState(docId)",
"function libraryRenderGrid()",
)
assert "_libraryLanguages[lang]" in helper
assert "delete _libraryLanguages[lang]" in helper
assert "libraryRenderStats();" in helper
assert "libraryRenderLangChips();" in helper
delete_body = _between(
text,
"async function libraryDeleteSingle(docId, card)",
"async function libraryBulkDelete()",
)
assert "libraryRemoveDocumentFromState(docId);" in delete_body