From 8e738ed9d48273634535df00d4e6c19d309d36ca Mon Sep 17 00:00:00 2001 From: th Date: Thu, 16 Apr 2026 23:56:42 +0200 Subject: [PATCH] fix(bookmarks): sync local state on save so columns persist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit save() PATCHed the card on the server but left state.cards stale. The next openOverlay read the old body from the local cache and the new column was gone. Now save() writes the updated body back into the matching state.cards entry so close + reopen sees the latest data without a full _reloadAll round-trip. Test: "new columns survive close + reopen" — adds a column via API + local state update, closes, reopens, asserts both column names are present in the DOM. Bumps to 1.9.28. Co-Authored-By: Claude Opus 4.6 (1M context) --- config.php | 2 +- electron/package.json | 2 +- public/plugins/bookmarks/bookmarks.js | 4 +++ public/plugins/tests/bookmarks.test.js | 49 ++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/config.php b/config.php index ca438a9..a0c64f3 100644 --- a/config.php +++ b/config.php @@ -1,7 +1,7 @@ c.id == activeCardId); + if (local) local.body = body; } // ── Board card decoration ──────────────────────────────────────── diff --git a/public/plugins/tests/bookmarks.test.js b/public/plugins/tests/bookmarks.test.js index 78858eb..b8e86e6 100644 --- a/public/plugins/tests/bookmarks.test.js +++ b/public/plugins/tests/bookmarks.test.js @@ -130,6 +130,55 @@ describe('Bookmarks: card links', () => { }); }); +describe('Bookmarks: column persistence', () => { + it('new columns survive close + reopen', async () => { + let card; + try { + card = await fetch('api/cards', { method: 'POST', headers: hJson(), body: JSON.stringify({ title: '__test_bm_colsave__', body: emptyBody() }) }).then(r => r.json()); + await window._reloadAll(); + await sleep(200); + + // Open overlay, add a column via the data model + save + // (simulates what addColumn does without the dialog). + const el = document.querySelector(`.board-card[data-id="${card.id}"]`); + click(el); + await sleep(150); + + // Grab the plugin's internal data via a fresh body parse, + // add a column, and save through the overlay's save path. + const full = await fetch(`api/cards?ids=${card.id}`).then(r => r.json()); + const body = (full[0]?.body || '').trim(); + const jsonEnd = body.lastIndexOf('}'); + const parsed = JSON.parse(body.substring(0, jsonEnd + 1)); + parsed.columns.push({ name: 'Added', items: [] }); + const newBody = JSON.stringify(parsed) + '\n\n#bookmarks'; + await fetch(`api/cards/${card.id}`, { + method: 'PATCH', headers: hJson(), + body: JSON.stringify({ body: newBody }), + }); + // Update local state like save() does. + const local = (window.state?.cards || []).find(c => c.id == card.id); + if (local) local.body = newBody; + + click($('bookmarks-close')); + await sleep(100); + + // Reopen — the new column must still be there. + const el2 = document.querySelector(`.board-card[data-id="${card.id}"]`); + click(el2); + await sleep(150); + const names = [...document.querySelectorAll('.bm-col-name')].map(n => n.textContent); + expect(names).toContain('General'); + expect(names).toContain('Added'); + click($('bookmarks-close')); + await sleep(80); + } finally { + if (card?.id) await fetch(`api/cards/${card.id}`, { method: 'DELETE', headers: hDel() }); + await window._reloadAll(); + } + }); +}); + describe('Bookmarks: column width persistence', () => { it('saves and restores columnWidth from card body', async () => { const cols = [{ name: 'W', items: [] }];