fix(activities): i18n keys + retry useOwnedTickets after transient failure
i18n: add the missing keys the ticket purchase + owned-tickets
surfaces use across en/es/fr — activities.detail.{buyTicket,
buyAnotherTicket, viewMyTickets, ticketsOwned, unlimitedTickets}
and activities.filters.myTickets. Without these the runtime fell
back to the literal key strings + spammed [intlify] warnings; the
filter chip rendered the bare key text on logged-in sessions.
ticketsOwned uses i18n pluralization so "You have 1 ticket" vs
"You have 5 tickets" both come out correct.
useOwnedTickets: the hasAutoLoaded guard prevented retries after
a transient backend failure (e.g. an LNbits restart mid-fetch).
The composable would stay stuck with tickets = [] forever, so the
buyer landing on a fresh detail page right after a transient error
saw no badges anywhere. Detect the "previous load didn't actually
hydrate" state (lastLoadedUserId still null while authenticated)
and retry on the next useOwnedTickets() call.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
722bc21f4d
commit
794b63e699
5 changed files with 37 additions and 2 deletions
|
|
@ -57,6 +57,7 @@ const messages: LocaleMessages = {
|
||||||
tomorrow: 'Tomorrow',
|
tomorrow: 'Tomorrow',
|
||||||
thisWeek: 'This Week',
|
thisWeek: 'This Week',
|
||||||
thisMonth: 'This Month',
|
thisMonth: 'This Month',
|
||||||
|
myTickets: 'My tickets',
|
||||||
},
|
},
|
||||||
categories: {
|
categories: {
|
||||||
concert: 'Concert',
|
concert: 'Concert',
|
||||||
|
|
@ -96,6 +97,11 @@ const messages: LocaleMessages = {
|
||||||
when: 'When',
|
when: 'When',
|
||||||
tickets: 'Tickets',
|
tickets: 'Tickets',
|
||||||
ticketsAvailable: '{count} tickets available',
|
ticketsAvailable: '{count} tickets available',
|
||||||
|
ticketsOwned: 'You have {count} ticket | You have {count} tickets',
|
||||||
|
unlimitedTickets: 'Unlimited tickets',
|
||||||
|
buyTicket: 'Buy ticket',
|
||||||
|
buyAnotherTicket: 'Buy another ticket',
|
||||||
|
viewMyTickets: 'View in My Tickets',
|
||||||
soldOut: 'Sold Out',
|
soldOut: 'Sold Out',
|
||||||
free: 'Free',
|
free: 'Free',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ const messages: LocaleMessages = {
|
||||||
tomorrow: 'Mañana',
|
tomorrow: 'Mañana',
|
||||||
thisWeek: 'Esta semana',
|
thisWeek: 'Esta semana',
|
||||||
thisMonth: 'Este mes',
|
thisMonth: 'Este mes',
|
||||||
|
myTickets: 'Mis boletos',
|
||||||
},
|
},
|
||||||
categories: {
|
categories: {
|
||||||
concert: 'Concierto',
|
concert: 'Concierto',
|
||||||
|
|
@ -96,6 +97,11 @@ const messages: LocaleMessages = {
|
||||||
when: 'Cuándo',
|
when: 'Cuándo',
|
||||||
tickets: 'Boletos',
|
tickets: 'Boletos',
|
||||||
ticketsAvailable: '{count} boletos disponibles',
|
ticketsAvailable: '{count} boletos disponibles',
|
||||||
|
ticketsOwned: 'Tienes {count} boleto | Tienes {count} boletos',
|
||||||
|
unlimitedTickets: 'Boletos ilimitados',
|
||||||
|
buyTicket: 'Comprar boleto',
|
||||||
|
buyAnotherTicket: 'Comprar otro boleto',
|
||||||
|
viewMyTickets: 'Ver en Mis boletos',
|
||||||
soldOut: 'Agotado',
|
soldOut: 'Agotado',
|
||||||
free: 'Gratis',
|
free: 'Gratis',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ const messages: LocaleMessages = {
|
||||||
tomorrow: 'Demain',
|
tomorrow: 'Demain',
|
||||||
thisWeek: 'Cette semaine',
|
thisWeek: 'Cette semaine',
|
||||||
thisMonth: 'Ce mois-ci',
|
thisMonth: 'Ce mois-ci',
|
||||||
|
myTickets: 'Mes billets',
|
||||||
},
|
},
|
||||||
categories: {
|
categories: {
|
||||||
concert: 'Concert',
|
concert: 'Concert',
|
||||||
|
|
@ -96,6 +97,11 @@ const messages: LocaleMessages = {
|
||||||
when: 'Quand',
|
when: 'Quand',
|
||||||
tickets: 'Billets',
|
tickets: 'Billets',
|
||||||
ticketsAvailable: '{count} billets disponibles',
|
ticketsAvailable: '{count} billets disponibles',
|
||||||
|
ticketsOwned: 'Vous avez {count} billet | Vous avez {count} billets',
|
||||||
|
unlimitedTickets: 'Billets illimités',
|
||||||
|
buyTicket: 'Acheter un billet',
|
||||||
|
buyAnotherTicket: 'Acheter un autre billet',
|
||||||
|
viewMyTickets: 'Voir dans Mes billets',
|
||||||
soldOut: 'Épuisé',
|
soldOut: 'Épuisé',
|
||||||
free: 'Gratuit',
|
free: 'Gratuit',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ export interface LocaleMessages {
|
||||||
tomorrow: string
|
tomorrow: string
|
||||||
thisWeek: string
|
thisWeek: string
|
||||||
thisMonth: string
|
thisMonth: string
|
||||||
|
myTickets: string
|
||||||
}
|
}
|
||||||
categories: Record<string, string>
|
categories: Record<string, string>
|
||||||
detail: {
|
detail: {
|
||||||
|
|
@ -71,6 +72,11 @@ export interface LocaleMessages {
|
||||||
when: string
|
when: string
|
||||||
tickets: string
|
tickets: string
|
||||||
ticketsAvailable: string
|
ticketsAvailable: string
|
||||||
|
ticketsOwned: string
|
||||||
|
unlimitedTickets: string
|
||||||
|
buyTicket: string
|
||||||
|
buyAnotherTicket: string
|
||||||
|
viewMyTickets: string
|
||||||
soldOut: string
|
soldOut: string
|
||||||
free: string
|
free: string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,8 @@ function paidCount(activityId: string): number {
|
||||||
export function useOwnedTickets() {
|
export function useOwnedTickets() {
|
||||||
const { isAuthenticated, currentUser } = useAuth()
|
const { isAuthenticated, currentUser } = useAuth()
|
||||||
|
|
||||||
// First call kicks off the initial load. Subsequent calls just
|
// First call kicks off the initial load + sets up the auth-change
|
||||||
// attach to the shared state.
|
// watcher. Subsequent calls attach to the shared state.
|
||||||
if (!hasAutoLoaded) {
|
if (!hasAutoLoaded) {
|
||||||
hasAutoLoaded = true
|
hasAutoLoaded = true
|
||||||
fetchTickets()
|
fetchTickets()
|
||||||
|
|
@ -97,6 +97,17 @@ export function useOwnedTickets() {
|
||||||
if (id !== lastLoadedUserId) fetchTickets()
|
if (id !== lastLoadedUserId) fetchTickets()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
} else if (
|
||||||
|
!isLoading.value &&
|
||||||
|
isAuthenticated.value &&
|
||||||
|
currentUser.value &&
|
||||||
|
lastLoadedUserId !== currentUser.value.id
|
||||||
|
) {
|
||||||
|
// A previous load failed (lastLoadedUserId stayed null) or the
|
||||||
|
// user changed identity while the singleton was idle. Retry —
|
||||||
|
// the buyer landing on a fresh detail page after a transient
|
||||||
|
// backend hiccup shouldn't be stuck with empty tickets.
|
||||||
|
fetchTickets()
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue