fix(documents): refresh library counters after removal (#1924)
This commit is contained in:
@@ -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}`);
|
||||||
|
|||||||
43
tests/test_document_library_delete_counters.py
Normal file
43
tests/test_document_library_delete_counters.py
Normal 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
|
||||||
Reference in New Issue
Block a user