fix(market): re-link stallName when stall arrives after product

Subscription delivers stall (kind 30017) and product (kind 30018)
events without ordering guarantees. handleProductEvent and
loadProducts looked up stall name once at product-ingest time and
froze "Unknown Stall" on the product object when the stall hadn't
arrived yet — even when the stall landed milliseconds later.

Two-sided fix in the Pinia store:

- addStall: after upserting a stall, sweep products and re-stamp
  stallName for any matching stall_id (handles product-arrives-first
  race + downstream stall name updates).
- addProduct: do the lookup itself instead of trusting the caller's
  stallName field (handles stall-arrives-first race + paranoia).

Both paths converge on the live stalls collection, so eventual
consistency is guaranteed regardless of event order.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-05-03 09:16:21 +02:00
commit eb3393f1b8

View file

@ -239,11 +239,20 @@ export const useMarketStore = defineStore('market', () => {
} }
const addProduct = (product: Product) => { const addProduct = (product: Product) => {
const existingIndex = products.value.findIndex(p => p.id === product.id) // Lookup stallName from the current stall set — the value passed in by
// the caller can be stale ("Unknown Stall") if the stall event hadn't
// arrived yet. The reverse race (stall arrives first) is handled in
// addStall below.
const matchedStall = stalls.value.find(s => s.id === product.stall_id)
const enriched: Product = matchedStall
? { ...product, stallName: matchedStall.name }
: product
const existingIndex = products.value.findIndex(p => p.id === enriched.id)
if (existingIndex >= 0) { if (existingIndex >= 0) {
products.value[existingIndex] = product products.value[existingIndex] = enriched
} else { } else {
products.value.push(product) products.value.push(enriched)
} }
} }
@ -254,6 +263,14 @@ export const useMarketStore = defineStore('market', () => {
} else { } else {
stalls.value.push(stall) stalls.value.push(stall)
} }
// Re-stamp stallName on any products that arrived before this stall did
// (or whose stall name has changed). Direct property mutation on items
// in a reactive array triggers Vue's deep reactivity.
products.value.forEach(p => {
if (p.stall_id === stall.id && p.stallName !== stall.name) {
p.stallName = stall.name
}
})
} }
const addMarket = (market: Market) => { const addMarket = (market: Market) => {