fix(shopping): stale-row cleanup, cache-busting, view persistence
Root cause of the doubled lists: the relay DB had stale rows from
previous activations that were never deleted. The composite PK
prevented same-uid duplicates but not leftover rows from cards that
were deactivated without a successful DELETE.
Fixes:
- Client tick sends DELETE { keep: [active uids] } after every
push cycle. The relay drops all rows for this tenant that aren't
in the keep list — cleans up migration leftovers, failed deletes,
old single-list sentinels, everything in one shot.
- Relay DELETE endpoint accepts { keep: [...] } for bulk cleanup
alongside the existing { card_uid } single-delete.
- Relay layout.php adds cache-busting query strings on style.css
and app.js via filemtime() so phone browsers pick up new code
on deploy instead of serving stale cached versions.
- showView persists the active view name to localStorage; after
login the relay restores it instead of always landing on compose.
- Shopping-list-box border/background removed — items have their
own chrome, the box is just a flex grouper now.
- View title is always "Shopping"; each list box keeps its own
header with the card title. No more doubled name.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5c9e9254d1
commit
6fa42eb267
7 files changed
+46
-20
No files matched your search
+12
-8
@@ -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 = '<li class="empty">no active shopping lists</li>';
|
||||
return;
|
||||
}
|
||||
if (title) title.textContent = lists.length === 1 ? (lists[0].title || 'Shopping') : 'Shopping';
|
||||
if (title) title.textContent = 'Shopping';
|
||||
list.innerHTML = '';
|
||||
for (const entry of lists) {
|
||||
const nodes = shoppingParseLines(entry.body);
|
||||
@@ -794,12 +800,10 @@ async function loadShopping(quiet = false) {
|
||||
// List box wrapper
|
||||
const box = document.createElement('li');
|
||||
box.className = 'shopping-list-box';
|
||||
if (lists.length > 1 || entry.title) {
|
||||
const hdr = document.createElement('div');
|
||||
hdr.className = 'shopping-list-header';
|
||||
hdr.textContent = entry.title || '(untitled)';
|
||||
box.appendChild(hdr);
|
||||
}
|
||||
const hdr = document.createElement('div');
|
||||
hdr.className = 'shopping-list-header';
|
||||
hdr.textContent = entry.title || '(untitled)';
|
||||
box.appendChild(hdr);
|
||||
if (!hasItems) {
|
||||
const done = document.createElement('div');
|
||||
done.className = 'shopping-list-done';
|
||||
|
||||
Reference in new issue
Block a user