fix(quick-save): mid-drag gedrainter Quick-Save wird nach Drag-Ende gerendert

Der Drain laesst renderBoards() aus, solange ein Board- oder Bookmark-Drag laeuft
(replaceChildren wuerde den Drag abreissen) — holte den Render danach aber nie nach,
sodass der gespeicherte Eintrag bis zu einem unabhaengigen Fremd-Render unsichtbar blieb.
Der ausgelassene Render wird jetzt gemerkt (_renderDeferredByDrag) und drag.js ruft am
Ende jedes Drags (onUp/onCancel/Bookmark-dragend) flushQuickSaveRenderIfDeferred nach.
Idempotent: ohne ausstehenden Render kein Extra-Render bei normalen Drags.
This commit is contained in:
2026-06-14 19:55:18 +02:00
parent 327bcd3385
commit 520a062049
2 changed files with 36 additions and 11 deletions
+18 -1
View File
@@ -241,6 +241,7 @@ function bindGlobalEvents() {
// gegenseitig ueberschreiben (Datensicherheit, Phase-4-Review-Blocker 2b). // gegenseitig ueberschreiben (Datensicherheit, Phase-4-Review-Blocker 2b).
let _drainBusy = false; let _drainBusy = false;
let _drainQueued = false; // ein waehrend eines laufenden Drains angefragter Drain wird nachgeholt let _drainQueued = false; // ein waehrend eines laufenden Drains angefragter Drain wird nachgeholt
let _renderDeferredByDrag = false; // Drain hat den Render wegen eines laufenden Drags ausgelassen -> nach Drag-Ende nachholen
async function drainQuickSavePending() { async function drainQuickSavePending() {
if (_drainBusy) { _drainQueued = true; return; } if (_drainBusy) { _drainQueued = true; return; }
_drainBusy = true; _drainBusy = true;
@@ -270,7 +271,14 @@ async function drainQuickSavePending() {
const remaining = Array.isArray(still) ? still.filter(e => e && !drainedIds.has(e.id)) : []; const remaining = Array.isArray(still) ? still.filter(e => e && !drainedIds.has(e.id)) : [];
await Store.set('quicksave_pending', remaining); await Store.set('quicksave_pending', remaining);
// Render nur, wenn gerade KEIN Drag laeuft (renderBoards->replaceChildren wuerde ihn abreissen). // Render nur, wenn gerade KEIN Drag laeuft (renderBoards->replaceChildren wuerde ihn abreissen).
if (!document.querySelector('.board.dragging, .bm-item.dragging-source')) renderBoards(); // Laeuft einer, den Render-Wunsch merken und nach Drag-Ende nachholen (drag.js ruft
// flushQuickSaveRenderIfDeferred), sonst bliebe der frisch gedrainte Quick-Save bis zu einem
// unabhaengigen Fremd-Render unsichtbar (Phase-6-Review).
if (document.querySelector('.board.dragging, .bm-item.dragging-source')) {
_renderDeferredByDrag = true;
} else {
renderBoards();
}
} }
} catch (e) { } catch (e) {
console.error('Quick-Save-Drain fehlgeschlagen:', e && e.message); console.error('Quick-Save-Drain fehlgeschlagen:', e && e.message);
@@ -282,6 +290,15 @@ async function drainQuickSavePending() {
if (_drainQueued) { _drainQueued = false; drainQuickSavePending(); } if (_drainQueued) { _drainQueued = false; drainQuickSavePending(); }
} }
// Wird von drag.js nach jedem Drag-Ende aufgerufen: einen waehrend des Drags ausgelassenen
// Quick-Save-Render nachholen. Idempotent — tut nichts, wenn kein Render aussteht.
function flushQuickSaveRenderIfDeferred() {
if (_renderDeferredByDrag) {
_renderDeferredByDrag = false;
renderBoards();
}
}
// Live-Sync (QS-03): ein offener NewTab drained die Queue, sobald der Worker etwas anhaengt. // Live-Sync (QS-03): ein offener NewTab drained die Queue, sobald der Worker etwas anhaengt.
function bindStorageSync() { function bindStorageSync() {
if (typeof chrome === 'undefined' || !chrome.storage || !chrome.storage.onChanged) return; if (typeof chrome === 'undefined' || !chrome.storage || !chrome.storage.onChanged) return;
+10 -2
View File
@@ -63,7 +63,7 @@ function initBoardDragDrop() {
async function onUp() { async function onUp() {
cleanup(); cleanup();
// Nur bei echtem Verschieben persistieren — sonst board.pos unangetastet lassen. // Nur bei echtem Verschieben persistieren — sonst board.pos unangetastet lassen.
if (!moved) return; if (moved) {
const id = boardEl.dataset.boardId; const id = boardEl.dataset.boardId;
const board = boards.find(b => b.id === id); const board = boards.find(b => b.id === id);
if (board) { if (board) {
@@ -74,10 +74,16 @@ function initBoardDragDrop() {
await saveBoards(); await saveBoards();
} }
} }
// Einen waehrend des Drags ausgelassenen Quick-Save-Render nachholen (app.js).
if (typeof flushQuickSaveRenderIfDeferred === 'function') flushQuickSaveRenderIfDeferred();
}
// pointercancel feuert STATT pointerup bei Touch-Interrupt, Browser-Geste oder wenn das // pointercancel feuert STATT pointerup bei Touch-Interrupt, Browser-Geste oder wenn das
// captured Element aus dem DOM faellt -> nur aufraeumen (cleanup), pos NICHT ueberschreiben. // captured Element aus dem DOM faellt -> nur aufraeumen (cleanup), pos NICHT ueberschreiben.
function onCancel() { cleanup(); } function onCancel() {
cleanup();
if (typeof flushQuickSaveRenderIfDeferred === 'function') flushQuickSaveRenderIfDeferred();
}
handle.addEventListener('pointermove', onMove); handle.addEventListener('pointermove', onMove);
handle.addEventListener('pointerup', onUp); handle.addEventListener('pointerup', onUp);
@@ -101,6 +107,8 @@ function initBookmarkDragDrop(listEl, board) {
listEl.addEventListener('dragend', e => { listEl.addEventListener('dragend', e => {
const item = e.target.closest('.bm-item'); const item = e.target.closest('.bm-item');
if (item) item.classList.remove('dragging-source'); if (item) item.classList.remove('dragging-source');
// Blieb ein Quick-Save-Render waehrend des Bookmark-Drags aus (Drop ausgeblieben), jetzt nachholen.
if (typeof flushQuickSaveRenderIfDeferred === 'function') flushQuickSaveRenderIfDeferred();
}); });
listEl.addEventListener('dragover', e => { listEl.addEventListener('dragover', e => {