Fix cart pull not syncing removals across devices
pullServerCart() only added/updated matching lines and never dropped a local line the server no longer had — a removal on one device never reached another (adding synced, removing didn't). Now replaces the local cart outright with the server's instead of merging into it, including down to empty.
This commit is contained in:
+15
-17
@@ -150,32 +150,30 @@ export async function mergeServerCartIntoLocal(): Promise<void> {
|
||||
// Cross-device sync for an *already* logged-in session — CartSync.tsx
|
||||
// only ever pushes local→server, and mergeServerCartIntoLocal() above only
|
||||
// ever runs at the moment of a fresh login/register, so a customer already
|
||||
// signed in on device B never picked up an addition made on device A. This
|
||||
// signed in on device B never picked up a change made on device A. This
|
||||
// covers that gap: called on mount and on window focus (CartSync.tsx), not
|
||||
// just at login.
|
||||
//
|
||||
// Unlike mergeServerCartIntoLocal()'s additive `addToCart` (only safe
|
||||
// Replaces the local cart outright with the server's — NOT an additive
|
||||
// merge like mergeServerCartIntoLocal() above (that one is only safe
|
||||
// right after login, when the local cart can't yet overlap the server
|
||||
// one — see that function's own comment), this must be idempotent: it can
|
||||
// run repeatedly against a cart that's already in sync. For a line that
|
||||
// exists on both sides, the server's qty wins outright (overwrite, not
|
||||
// add) — otherwise calling this twice in a row would double the quantity
|
||||
// every time. A local-only line (added on this device but not yet pushed
|
||||
// by CartSync's 800ms debounce) is left untouched rather than dropped.
|
||||
// one, see its own comment). An earlier version of this function only
|
||||
// added/updated matching lines and never removed a local line the server
|
||||
// no longer had, which meant a *removal* on device A never reached device
|
||||
// B (confirmed live 2026-08-01 — adding synced, removing didn't). Server
|
||||
// wins outright, including "server cart is now empty."
|
||||
//
|
||||
// Trade-off: a local addition made in the last few hundred ms — after
|
||||
// this fetch went out but before CartSync's own 800ms debounce pushed it
|
||||
// — could theoretically get overwritten by a pull that lands in between.
|
||||
// Narrow enough (and self-healing on the next change/focus) to accept,
|
||||
// consistent with every other "best-effort" sync path in this file.
|
||||
export async function pullServerCart(): Promise<void> {
|
||||
try {
|
||||
const res = await fetch("/api/account/cart");
|
||||
if (!res.ok) return;
|
||||
const data: { cart?: CartItem[] } = await res.json();
|
||||
const serverItems = data.cart ?? [];
|
||||
if (serverItems.length === 0) return;
|
||||
const items = readCart();
|
||||
for (const server of serverItems) {
|
||||
const existing = items.find((i) => sameLine(i, server.id, server.variant));
|
||||
if (existing) existing.qty = server.qty;
|
||||
else items.push(server.variant ? { id: server.id, qty: server.qty, variant: server.variant } : { id: server.id, qty: server.qty });
|
||||
}
|
||||
writeCart(items);
|
||||
writeCart(data.cart ?? []);
|
||||
} catch {
|
||||
// Best-effort — a failed pull just means this device doesn't see
|
||||
// another device's changes yet; nothing local is lost either way.
|
||||
|
||||
Reference in New Issue
Block a user