fix(quick-save): Pending-Queue-Redesign (Blocker 2b) — Worker schreibt eigenen 'quicksave_pending'-Key statt boards, Seite drained in die Inbox; getrennte Schreib-Domaenen, kein boards-Clobber
This commit is contained in:
+37
-11
@@ -27,6 +27,7 @@ async function init() {
|
|||||||
bindGlobalEvents();
|
bindGlobalEvents();
|
||||||
bindSettingsEvents();
|
bindSettingsEvents();
|
||||||
bindStorageSync();
|
bindStorageSync();
|
||||||
|
await drainQuickSavePending(); // beim Start angesammelte Quick-Saves (kein Tab war offen) einlesen
|
||||||
initSearch();
|
initSearch();
|
||||||
initPalette();
|
initPalette();
|
||||||
await migrateSticky();
|
await migrateSticky();
|
||||||
@@ -235,20 +236,45 @@ function bindGlobalEvents() {
|
|||||||
// ---- LIVE-SYNC (Quick-Save aus dem Background) ----
|
// ---- LIVE-SYNC (Quick-Save aus dem Background) ----
|
||||||
// Ein Quick-Save schreibt boards im Background. Ein offener Tab muss das sehen,
|
// Ein Quick-Save schreibt boards im Background. Ein offener Tab muss das sehen,
|
||||||
// sonst ueberschreibt er den Eintrag beim naechsten eigenen Save (QS-03).
|
// sonst ueberschreibt er den Eintrag beim naechsten eigenen Save (QS-03).
|
||||||
|
// Drained die Quick-Save-Queue in die Inbox. Die SEITE ist die einzige Schreiberin von 'boards';
|
||||||
|
// der Background-Worker haengt nur an 'quicksave_pending' an. Dadurch koennen sich Worker und Seite
|
||||||
|
// nicht im boards-Array gegenseitig ueberschreiben (Datensicherheit, Phase-4-Review-Blocker 2b).
|
||||||
|
let _drainBusy = false;
|
||||||
|
async function drainQuickSavePending() {
|
||||||
|
if (_drainBusy) return; // Re-Entry-Schutz (init + onChanged koennten ueberlappen)
|
||||||
|
_drainBusy = true;
|
||||||
|
try {
|
||||||
|
const pending = await Store.get('quicksave_pending');
|
||||||
|
if (!Array.isArray(pending) || pending.length === 0) return;
|
||||||
|
const drained = pending.slice();
|
||||||
|
const drainedIds = new Set(drained.map(e => e && e.id).filter(Boolean));
|
||||||
|
const inbox = await ensureInboxBoard(); // legt die Inbox an, falls noetig; gibt das Board zurueck
|
||||||
|
for (const e of drained) {
|
||||||
|
if (e && typeof e.url === 'string' && e.url) {
|
||||||
|
inbox.bookmarks.push(normalizeBookmark({ title: e.title, url: e.url }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await saveBoards();
|
||||||
|
// NUR die verarbeiteten Eintraege entfernen — ein gleichzeitiger Worker-Append bleibt erhalten.
|
||||||
|
const still = await Store.get('quicksave_pending');
|
||||||
|
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();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Quick-Save-Drain fehlgeschlagen:', e && e.message);
|
||||||
|
} finally {
|
||||||
|
_drainBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
chrome.storage.onChanged.addListener((changes, area) => {
|
chrome.storage.onChanged.addListener((changes, area) => {
|
||||||
if (area !== 'local' || !changes.boards) return;
|
// Nur auf die Quick-Save-Queue reagieren — 'boards' schreibt ausschliesslich diese Seite.
|
||||||
const next = changes.boards.newValue;
|
if (area !== 'local' || !changes.quicksave_pending) return;
|
||||||
if (!Array.isArray(next)) return;
|
drainQuickSavePending();
|
||||||
// Guard (W-c, nach Phase-4-Review auf REALE Klassen korrigiert): nicht mitten in einer offenen
|
|
||||||
// Interaktion das boards-Array ersetzen und neu rendern, sonst verwaist eine per-Closure gehaltene
|
|
||||||
// board-Referenz oder ein laufender Drag/Render wird abgerissen. Abgedeckt: Settings (.panel-overlay),
|
|
||||||
// Modals Add-Board/Add-Bookmark/Rename (.modal-overlay), HellionDialog/Onboarding (.dialog-overlay),
|
|
||||||
// Board-Drag (.board.dragging), Bookmark-Drag (.bm-item.dragging-source).
|
|
||||||
if (document.querySelector('.panel-overlay.active, .modal-overlay.active, .dialog-overlay.active, .board.dragging, .bm-item.dragging-source')) return;
|
|
||||||
boards = next;
|
|
||||||
renderBoards();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,10 @@ function flashBadge(text, color) {
|
|||||||
// Interne/nicht speicherbare Seiten (Browser-UI, Extension-Seiten) — kein sinnvolles Bookmark.
|
// Interne/nicht speicherbare Seiten (Browser-UI, Extension-Seiten) — kein sinnvolles Bookmark.
|
||||||
const UNSAVEABLE_URL = /^(chrome|chrome-extension|about|edge|opera|moz-extension|brave|vivaldi|view-source|devtools):/i;
|
const UNSAVEABLE_URL = /^(chrome|chrome-extension|about|edge|opera|moz-extension|brave|vivaldi|view-source|devtools):/i;
|
||||||
|
|
||||||
// Quick-Save: aktiven Tab lesen, in die Inbox haengen (read-modify-write).
|
// Quick-Save: aktiven Tab in die Pending-Queue haengen — NICHT boards schreiben.
|
||||||
|
// Datensicherheit (Phase-4-Review 2b): boards schreibt ausschliesslich die NewTab-Seite. Der Worker
|
||||||
|
// haengt nur an 'quicksave_pending' an; die Seite drained die Queue in die Inbox. So koennen Worker
|
||||||
|
// und Seite sich nicht im boards-Array gegenseitig ueberschreiben (kein Lost-Update bestehender Daten).
|
||||||
async function quickSaveActiveTab() {
|
async function quickSaveActiveTab() {
|
||||||
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||||
const tab = tabs && tabs[0];
|
const tab = tabs && tabs[0];
|
||||||
@@ -56,13 +59,11 @@ async function quickSaveActiveTab() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read-modify-write: aktuellen Stand frisch aus Storage holen, NICHT blind setzen.
|
|
||||||
const boards = (await bgGet('boards')) ?? [];
|
|
||||||
const inbox = ensureInbox(boards);
|
|
||||||
inbox.bookmarks.push(normalizeBookmark({ title: tab.title || tab.url, url: tab.url }));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await bgSet('boards', boards);
|
// read-modify-write nur auf der EIGENEN Queue (bgGet/bgSet sind via quickSaveChain serialisiert).
|
||||||
|
const pending = (await bgGet('quicksave_pending')) ?? [];
|
||||||
|
pending.push({ id: uid(), title: tab.title || tab.url, url: tab.url });
|
||||||
|
await bgSet('quicksave_pending', pending);
|
||||||
flashBadge(chrome.i18n.getMessage('quickSaveBadge'));
|
flashBadge(chrome.i18n.getMessage('quickSaveBadge'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Quota o.ae.: Badge zeigt nichts Gruenes, Fehler in die Worker-Konsole.
|
// Quota o.ae.: Badge zeigt nichts Gruenes, Fehler in die Worker-Konsole.
|
||||||
|
|||||||
@@ -60,12 +60,12 @@ function quickSaveActiveTab() {
|
|||||||
qsBusy = false;
|
qsBusy = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// read-modify-write: aktuellen boards-Stand frisch holen, anhaengen, zurueckschreiben.
|
// Datensicher: NICHT boards schreiben — nur an die Pending-Queue anhaengen (die Seite
|
||||||
chrome.storage.local.get(['boards'], (r) => {
|
// drained sie in die Inbox). So kann der Worker das boards-Array nicht clobbern (Review 2b).
|
||||||
const boards = r.boards ?? [];
|
chrome.storage.local.get(['quicksave_pending'], (r) => {
|
||||||
const inbox = ensureInbox(boards);
|
const pending = Array.isArray(r.quicksave_pending) ? r.quicksave_pending : [];
|
||||||
inbox.bookmarks.push(normalizeBookmark({ title: tab.title || tab.url, url: tab.url }));
|
pending.push({ id: uid(), title: tab.title || tab.url, url: tab.url });
|
||||||
chrome.storage.local.set({ boards }, () => {
|
chrome.storage.local.set({ quicksave_pending: pending }, () => {
|
||||||
if (chrome.runtime.lastError) {
|
if (chrome.runtime.lastError) {
|
||||||
console.error('Quick-Save fehlgeschlagen:', chrome.runtime.lastError.message);
|
console.error('Quick-Save fehlgeschlagen:', chrome.runtime.lastError.message);
|
||||||
qsBusy = false;
|
qsBusy = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user