From 520a0620493e8e8dbe90a407e862ac86ee068b04 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Sun, 14 Jun 2026 19:55:18 +0200 Subject: [PATCH] fix(quick-save): mid-drag gedrainter Quick-Save wird nach Drag-Ende gerendert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/js/app.js | 19 ++++++++++++++++++- src/js/drag.js | 28 ++++++++++++++++++---------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/js/app.js b/src/js/app.js index 3dc6c5c..2fc90ff 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -241,6 +241,7 @@ function bindGlobalEvents() { // gegenseitig ueberschreiben (Datensicherheit, Phase-4-Review-Blocker 2b). let _drainBusy = false; 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() { if (_drainBusy) { _drainQueued = true; return; } _drainBusy = true; @@ -270,7 +271,14 @@ async function drainQuickSavePending() { const remaining = Array.isArray(still) ? still.filter(e => e && !drainedIds.has(e.id)) : []; await Store.set('quicksave_pending', remaining); // 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) { console.error('Quick-Save-Drain fehlgeschlagen:', e && e.message); @@ -282,6 +290,15 @@ async function 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. function bindStorageSync() { if (typeof chrome === 'undefined' || !chrome.storage || !chrome.storage.onChanged) return; diff --git a/src/js/drag.js b/src/js/drag.js index bc6cb3f..b826406 100644 --- a/src/js/drag.js +++ b/src/js/drag.js @@ -63,21 +63,27 @@ function initBoardDragDrop() { async function onUp() { cleanup(); // Nur bei echtem Verschieben persistieren — sonst board.pos unangetastet lassen. - if (!moved) return; - const id = boardEl.dataset.boardId; - const board = boards.find(b => b.id === id); - if (board) { - board.pos = { - x: parseFloat(boardEl.style.getPropertyValue('--board-x')), - y: parseFloat(boardEl.style.getPropertyValue('--board-y')) - }; - await saveBoards(); + if (moved) { + const id = boardEl.dataset.boardId; + const board = boards.find(b => b.id === id); + if (board) { + board.pos = { + x: parseFloat(boardEl.style.getPropertyValue('--board-x')), + y: parseFloat(boardEl.style.getPropertyValue('--board-y')) + }; + 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 // 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('pointerup', onUp); @@ -101,6 +107,8 @@ function initBookmarkDragDrop(listEl, board) { listEl.addEventListener('dragend', e => { const item = e.target.closest('.bm-item'); 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 => {