tf-checkout-react-native 2.0.0-beta.18 → 2.0.0-beta.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/index.d.mts +42 -2
- package/dist/index.d.ts +42 -2
- package/dist/index.js +500 -303
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +377 -180
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/sections/Tickets.tsx +99 -50
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tf-checkout-react-native",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.19",
|
|
4
4
|
"description": "TheTicketFairy ReactNative Checkout library",
|
|
5
5
|
"homepage": "https://github.com/theticketfairy/tf-shared-libraries#readme",
|
|
6
6
|
"author": "The Ticket Fairy",
|
package/src/sections/Tickets.tsx
CHANGED
|
@@ -116,7 +116,20 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
116
116
|
const checkout = useCheckoutContext()
|
|
117
117
|
const theme = useTheme()
|
|
118
118
|
const s = useTicketStyles(theme)
|
|
119
|
-
const
|
|
119
|
+
const catalog = hooks.useTicketCatalog(eventId, timeSlotDates)
|
|
120
|
+
const {
|
|
121
|
+
ticketsQuery,
|
|
122
|
+
eventInfo,
|
|
123
|
+
slotQuery,
|
|
124
|
+
isTimeSlot,
|
|
125
|
+
dates,
|
|
126
|
+
slotDate,
|
|
127
|
+
setSlotDate,
|
|
128
|
+
qty,
|
|
129
|
+
setQty,
|
|
130
|
+
refetchCatalog,
|
|
131
|
+
current: catalogCurrent,
|
|
132
|
+
} = catalog
|
|
120
133
|
const waitingBuyer = hooks.useAuthSession()
|
|
121
134
|
const cachedProfile = hooks.useProfile({ enabled: false }).data
|
|
122
135
|
const waitingPrefill = waitingListProfilePrefill(
|
|
@@ -132,42 +145,17 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
132
145
|
const bundleStarting = bundleWait !== null
|
|
133
146
|
const promoMeta = ticketsQuery.data?.promo
|
|
134
147
|
|
|
135
|
-
const
|
|
136
|
-
const [
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
)
|
|
145
|
-
useEffect(() => {
|
|
146
|
-
if (isTimeSlot && !slotDate && dates.length) setSlotDate(dates[0])
|
|
147
|
-
}, [isTimeSlot, slotDate, dates])
|
|
148
|
-
const slotQuery = hooks.useTimeSlotTickets(eventId, isTimeSlot ? slotDate : undefined)
|
|
149
|
-
const refetchCatalog = () =>
|
|
150
|
-
Promise.all([
|
|
151
|
-
ticketsQuery.refetch(),
|
|
152
|
-
...(isTimeSlot && slotDate ? [slotQuery.refetch()] : []),
|
|
153
|
-
])
|
|
154
|
-
|
|
155
|
-
const tickets = useMemo(
|
|
156
|
-
() => (isTimeSlot ? (slotQuery.data ?? []) : (ticketsQuery.data?.tickets ?? [])),
|
|
157
|
-
[isTimeSlot, slotQuery.data, ticketsQuery.data?.tickets]
|
|
148
|
+
const tickets = catalog.tickets
|
|
149
|
+
const [error, updateError] = hooks.useScopedState(catalog.scope, '')
|
|
150
|
+
const setError = (message: string) => {
|
|
151
|
+
if (catalogCurrent()) updateError(message)
|
|
152
|
+
}
|
|
153
|
+
const [code, setCode] = hooks.useScopedState(catalog.scope, '')
|
|
154
|
+
const [codeState, setCodeState] = hooks.useScopedState<'idle' | 'busy'>(
|
|
155
|
+
catalog.scope,
|
|
156
|
+
'idle'
|
|
158
157
|
)
|
|
159
|
-
const [
|
|
160
|
-
const [code, setCode] = useState('')
|
|
161
|
-
const [codeState, setCodeState] = useState<'idle' | 'busy'>('idle')
|
|
162
|
-
const [promoOpen, setPromoOpen] = useState(false)
|
|
163
|
-
// Another event: its tickets, date and code start fresh.
|
|
164
|
-
hooks.useOnEventChange(eventId, () => {
|
|
165
|
-
setQty({})
|
|
166
|
-
setSlotDate('')
|
|
167
|
-
setCode('')
|
|
168
|
-
setPromoOpen(false)
|
|
169
|
-
setError('')
|
|
170
|
-
})
|
|
158
|
+
const [promoOpen, setPromoOpen] = hooks.useScopedState(catalog.scope, false)
|
|
171
159
|
const [visibleDescription, setVisibleDescription] = useState<string | null>(null)
|
|
172
160
|
const attributionEvent = useRef<string | null>(null)
|
|
173
161
|
|
|
@@ -175,7 +163,12 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
175
163
|
// per event. Explicit props win. URL reading (web-only) is intentionally
|
|
176
164
|
// skipped on native — `readAttributionFromUrl` is accepted for prop parity.
|
|
177
165
|
useEffect(() => {
|
|
178
|
-
if (
|
|
166
|
+
if (
|
|
167
|
+
!catalogCurrent() ||
|
|
168
|
+
!catalog.attributionReady ||
|
|
169
|
+
attributionEvent.current === eventId
|
|
170
|
+
)
|
|
171
|
+
return
|
|
179
172
|
attributionEvent.current = eventId
|
|
180
173
|
|
|
181
174
|
const promo = initialPromoCode?.trim() ?? ''
|
|
@@ -192,13 +185,27 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
192
185
|
.applyPromoCode(eventId, promo)
|
|
193
186
|
.catch(() => undefined)
|
|
194
187
|
.finally(() => {
|
|
195
|
-
|
|
188
|
+
if (!catalogCurrent()) return
|
|
189
|
+
return refetchCatalog()
|
|
190
|
+
.catch((e) => {
|
|
191
|
+
if (catalogCurrent()) setError(errorMessage(e))
|
|
192
|
+
})
|
|
193
|
+
.finally(() => {
|
|
194
|
+
if (catalogCurrent()) setCodeState('idle')
|
|
195
|
+
})
|
|
196
196
|
})
|
|
197
197
|
}
|
|
198
198
|
// Checkout actions and query functions are stable; this effect is scoped to
|
|
199
199
|
// one attribution pass per event.
|
|
200
200
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
201
|
-
}, [
|
|
201
|
+
}, [
|
|
202
|
+
eventId,
|
|
203
|
+
initialPromoCode,
|
|
204
|
+
referrer,
|
|
205
|
+
readAttributionFromUrl,
|
|
206
|
+
catalog.instance,
|
|
207
|
+
catalog.attributionReady,
|
|
208
|
+
])
|
|
202
209
|
|
|
203
210
|
// Seed the currency state once so a multi-currency event can offer a switcher
|
|
204
211
|
// on the ticket screen (before the cart is primed). No-op for single-currency.
|
|
@@ -211,9 +218,11 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
211
218
|
checkout
|
|
212
219
|
.syncCurrency(eventId, nextCode)
|
|
213
220
|
.then((res) => {
|
|
214
|
-
if (res.requiresCatalogRefresh) {
|
|
221
|
+
if (catalogCurrent() && res.requiresCatalogRefresh) {
|
|
215
222
|
setQty({})
|
|
216
|
-
refetchCatalog()
|
|
223
|
+
refetchCatalog().catch((e) => {
|
|
224
|
+
if (catalogCurrent()) setError(errorMessage(e))
|
|
225
|
+
})
|
|
217
226
|
}
|
|
218
227
|
})
|
|
219
228
|
.catch(() => undefined)
|
|
@@ -259,7 +268,9 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
259
268
|
: undefined
|
|
260
269
|
|
|
261
270
|
const prime = () => {
|
|
271
|
+
if (!catalogCurrent() || catalog.busy || codeState === 'busy' || catalog.error) return
|
|
262
272
|
setError('')
|
|
273
|
+
const continuationCurrent = catalog.capturePrimeContinuation()
|
|
263
274
|
const selections: TicketSelection[] = selected.map((t) => ({
|
|
264
275
|
id: t.id,
|
|
265
276
|
name: t.name,
|
|
@@ -272,7 +283,9 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
272
283
|
// payment SDK nothing is primed, and the error names that flow.
|
|
273
284
|
checkout
|
|
274
285
|
.setTickets(eventId, selections)
|
|
275
|
-
.then(() =>
|
|
286
|
+
.then(() => {
|
|
287
|
+
if (continuationCurrent()) onContinue?.()
|
|
288
|
+
})
|
|
276
289
|
.catch((e) =>
|
|
277
290
|
setError(
|
|
278
291
|
e instanceof ConfirmerBusyError
|
|
@@ -283,19 +296,34 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
283
296
|
}
|
|
284
297
|
|
|
285
298
|
const applyCode = () => {
|
|
286
|
-
if (
|
|
299
|
+
if (
|
|
300
|
+
!catalogCurrent() ||
|
|
301
|
+
!catalog.attributionReady ||
|
|
302
|
+
catalog.busy ||
|
|
303
|
+
codeState === 'busy' ||
|
|
304
|
+
!code.trim()
|
|
305
|
+
)
|
|
306
|
+
return
|
|
307
|
+
setError('')
|
|
287
308
|
setCodeState('busy')
|
|
288
309
|
checkout
|
|
289
310
|
.applyPromoCode(eventId, code.trim())
|
|
290
311
|
.catch(() => undefined)
|
|
291
312
|
.finally(() => {
|
|
292
|
-
|
|
313
|
+
if (!catalogCurrent()) return
|
|
314
|
+
return refetchCatalog()
|
|
315
|
+
.catch((e) => {
|
|
316
|
+
if (catalogCurrent()) setError(errorMessage(e))
|
|
317
|
+
})
|
|
318
|
+
.finally(() => {
|
|
319
|
+
if (catalogCurrent()) setCodeState('idle')
|
|
320
|
+
})
|
|
293
321
|
})
|
|
294
322
|
}
|
|
295
323
|
|
|
296
324
|
const isAccess = promoMeta?.isAccessCodeEnabled
|
|
297
325
|
const showCodeInput = promoMeta?.isPromotionsEnabled || isAccess
|
|
298
|
-
const listQuery =
|
|
326
|
+
const listQuery = catalog
|
|
299
327
|
const currency = checkout.currency
|
|
300
328
|
|
|
301
329
|
const renderRow = (t: TicketType) => {
|
|
@@ -416,7 +444,7 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
416
444
|
// Time-slot layout groups the date's tickets under their time heading.
|
|
417
445
|
const timeGroups: { time: string; items: TicketType[] }[] = []
|
|
418
446
|
if (isTimeSlot)
|
|
419
|
-
for (const t of
|
|
447
|
+
for (const t of catalog.datedTickets) {
|
|
420
448
|
const time = t.timeSlot?.time ?? ''
|
|
421
449
|
const g = timeGroups.find((x) => x.time === time)
|
|
422
450
|
if (g) g.items.push(t)
|
|
@@ -452,7 +480,6 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
452
480
|
value={slotDate}
|
|
453
481
|
onChange={(d) => {
|
|
454
482
|
setSlotDate(d)
|
|
455
|
-
setQty({})
|
|
456
483
|
}}
|
|
457
484
|
options={dates.map((d) => ({ label: d, value: d }))}
|
|
458
485
|
/>
|
|
@@ -473,7 +500,7 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
473
500
|
</View>
|
|
474
501
|
) : null}
|
|
475
502
|
|
|
476
|
-
{isTimeSlot && !slotQuery.isLoading &&
|
|
503
|
+
{isTimeSlot && !slotQuery.isLoading && !slotQuery.data?.length && slotDate ? (
|
|
477
504
|
<MutedText>No sessions available on this date.</MutedText>
|
|
478
505
|
) : null}
|
|
479
506
|
|
|
@@ -486,6 +513,13 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
486
513
|
))
|
|
487
514
|
: tickets.map(renderRow)}
|
|
488
515
|
|
|
516
|
+
{isTimeSlot && catalog.regularTickets.length > 0 ? (
|
|
517
|
+
<>
|
|
518
|
+
<Text style={s.slotTime}>{createTranslator()('tickets.getTickets')}</Text>
|
|
519
|
+
{catalog.regularTickets.map(renderRow)}
|
|
520
|
+
</>
|
|
521
|
+
) : null}
|
|
522
|
+
|
|
489
523
|
{/* Promo / access code */}
|
|
490
524
|
{showCodeInput ? (
|
|
491
525
|
<View style={s.promo}>
|
|
@@ -511,7 +545,12 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
511
545
|
variant="ghost"
|
|
512
546
|
onPress={applyCode}
|
|
513
547
|
loading={codeState === 'busy'}
|
|
514
|
-
disabled={
|
|
548
|
+
disabled={
|
|
549
|
+
!catalog.attributionReady ||
|
|
550
|
+
catalog.busy ||
|
|
551
|
+
codeState === 'busy' ||
|
|
552
|
+
!code.trim()
|
|
553
|
+
}
|
|
515
554
|
>
|
|
516
555
|
{isAccess ? 'ENTER' : 'APPLY'}
|
|
517
556
|
</Button>
|
|
@@ -541,6 +580,9 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
541
580
|
checkout.orderFixed ||
|
|
542
581
|
checkout.isBusy ||
|
|
543
582
|
bundleStarting ||
|
|
583
|
+
catalog.busy ||
|
|
584
|
+
codeState === 'busy' ||
|
|
585
|
+
!!catalog.error ||
|
|
544
586
|
selected.length === 0,
|
|
545
587
|
title: continueTitle,
|
|
546
588
|
})
|
|
@@ -548,7 +590,14 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
548
590
|
<Button
|
|
549
591
|
onPress={prime}
|
|
550
592
|
loading={checkout.isBusy}
|
|
551
|
-
disabled={
|
|
593
|
+
disabled={
|
|
594
|
+
checkout.isBusy ||
|
|
595
|
+
bundleStarting ||
|
|
596
|
+
catalog.busy ||
|
|
597
|
+
codeState === 'busy' ||
|
|
598
|
+
!!catalog.error ||
|
|
599
|
+
selected.length === 0
|
|
600
|
+
}
|
|
552
601
|
>
|
|
553
602
|
{checkout.isBusy
|
|
554
603
|
? 'Reserving…'
|