diff --git a/config.php b/config.php index 1ac86c0..0cf3ce2 100644 --- a/config.php +++ b/config.php @@ -1,7 +1,7 @@ String(c.id)); // Push all active cards first. for (const card of activeCards) { try { await pushOne(card); } catch {} } + // Tell the relay to drop any rows that aren't in our active set. + // This cleans up stale entries from previous activations that + // were never explicitly deleted (old single-list era, failed + // DELETEs, migration leftovers). + try { + await hyprcrd.api('api/shopping', { method: 'DELETE', body: { keep: activeUids } }); + } catch {} // Pull: fetch all lists from relay, match by card_uid. try { const res = await hyprcrd.api('api/shopping', { method: 'GET' }); diff --git a/relay/inc/routes/shopping.php b/relay/inc/routes/shopping.php index 0974a7c..6904194 100644 --- a/relay/inc/routes/shopping.php +++ b/relay/inc/routes/shopping.php @@ -63,11 +63,30 @@ if ($route === '/api/shopping' && $method === 'POST') { } // ── DELETE /api/shopping ───────────────────────────────────── +// Two modes: +// { card_uid: "123" } → delete one specific list +// { keep: ["123", "456"] } → delete everything EXCEPT these if ($route === '/api/shopping' && $method === 'DELETE') { if (!$clientTenant) jsonResponse(['error' => 'unauthorized'], 401); $input = json_decode(file_get_contents('php://input'), true) ?: []; + $keep = is_array($input['keep'] ?? null) ? $input['keep'] : null; $uid = trim((string)($input['card_uid'] ?? '')); - if ($uid === '') jsonResponse(['error' => 'card_uid required'], 400); + + if ($keep !== null) { + // Bulk cleanup: keep only the listed card_uids, drop the rest. + if (count($keep)) { + $placeholders = implode(',', array_fill(0, count($keep), '?')); + $params = array_merge([$clientTenant['id']], array_map('strval', $keep)); + $db->prepare("DELETE FROM shopping_state WHERE tenant_id = ? AND card_uid NOT IN ($placeholders)") + ->execute($params); + } else { + $db->prepare('DELETE FROM shopping_state WHERE tenant_id = :t') + ->execute(['t' => $clientTenant['id']]); + } + jsonResponse(['ok' => true]); + } + + if ($uid === '') jsonResponse(['error' => 'card_uid or keep[] required'], 400); $db->prepare('DELETE FROM shopping_state WHERE tenant_id = :t AND card_uid = :u') ->execute(['t' => $clientTenant['id'], 'u' => $uid]); jsonResponse(['ok' => true]); diff --git a/relay/ui/app.js b/relay/ui/app.js index 4be75a5..1c1e3fd 100644 --- a/relay/ui/app.js +++ b/relay/ui/app.js @@ -112,6 +112,11 @@ function showView(name) { } else { stopShoppingPoll(); } + // Remember the active view so a page reload lands back here + // instead of defaulting to compose every time. + if (['compose', 'account', 'admin', 'chat', 'shopping'].includes(name)) { + try { localStorage.setItem('relay-last-view', name); } catch {} + } if (name === 'admin') { loadUsers(); } else if (name === 'account') { @@ -279,7 +284,8 @@ function routePostLogin(status) { showView('account'); toast('set your client secret to start using the relay'); } else { - showView('compose'); + const last = localStorage.getItem('relay-last-view') || 'compose'; + showView(['compose', 'account', 'admin', 'chat', 'shopping'].includes(last) ? last : 'compose'); } } @@ -786,7 +792,7 @@ async function loadShopping(quiet = false) { list.innerHTML = '