tf-checkout-react-native 2.0.0-beta.12 → 2.0.0-beta.14
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/dist/index.d.mts +30 -7
- package/dist/index.d.ts +30 -7
- package/dist/index.js +257 -75
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +257 -75
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -4
- package/src/confirmer.ts +26 -5
- package/src/sections/AddOns.tsx +28 -13
- package/src/sections/BillingInfo.tsx +21 -4
- package/src/sections/Tickets.tsx +39 -7
- package/src/ui/CustomFields.tsx +7 -4
- package/src/ui/ProviderPages.tsx +14 -2
- package/src/ui/QuantitySelect.tsx +4 -3
- package/src/ui/primitives.tsx +2 -0
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.14",
|
|
4
4
|
"description": "TheTicketFairy ReactNative Checkout library",
|
|
5
5
|
"homepage": "https://github.com/theticketfairy/tf-shared-libraries#readme",
|
|
6
6
|
"author": "The Ticket Fairy",
|
|
@@ -34,10 +34,13 @@
|
|
|
34
34
|
"!**/__tests__",
|
|
35
35
|
"!**/__fixtures__",
|
|
36
36
|
"!**/__mocks__",
|
|
37
|
-
"!**/.*"
|
|
37
|
+
"!**/.*",
|
|
38
|
+
"!scripts/verify-wallet-context.cjs",
|
|
39
|
+
"!scripts/verify-provider-uri.cjs",
|
|
40
|
+
"!scripts/verify-native-textarea.cjs"
|
|
38
41
|
],
|
|
39
42
|
"scripts": {
|
|
40
|
-
"test": "node scripts/verify-discussion-support.cjs && jest --passWithNoTests",
|
|
43
|
+
"test": "node scripts/verify-discussion-support.cjs && node scripts/verify-wallet-context.cjs && node scripts/verify-provider-uri.cjs && node scripts/verify-native-textarea.cjs && jest --passWithNoTests",
|
|
41
44
|
"typecheck": "tsc -p tsconfig.build.json",
|
|
42
45
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
43
46
|
"lint:fix": "eslint \"**/*.{js,ts,tsx}\" --fix",
|
|
@@ -157,7 +160,7 @@
|
|
|
157
160
|
"react-native-mmkv": "^3.0.0",
|
|
158
161
|
"react-native-webview": ">=13.15.0"
|
|
159
162
|
},
|
|
160
|
-
"//dependencies": "tf-checkout-shared is INLINED into this bundle (tsup noExternal), so its own runtime deps are ours too
|
|
163
|
+
"//dependencies": "tf-checkout-shared is INLINED into this bundle (tsup noExternal), so its own runtime deps are ours too \u2014 axios, entities and htmlparser2 must stay declared here or tsup bundles them (axios drags Node's stream into a browser build) / the consumer never installs them.",
|
|
161
164
|
"engines": {
|
|
162
165
|
"node": ">=20.19.4"
|
|
163
166
|
}
|
package/src/confirmer.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
nativeStripeErrorOutcome,
|
|
31
31
|
confirmedStripeIntentStatus,
|
|
32
32
|
paymentProviderError,
|
|
33
|
+
stripeWalletPaymentContext,
|
|
33
34
|
type CheckoutBilling,
|
|
34
35
|
type CheckoutPorts,
|
|
35
36
|
type NativeStripeErrorLike,
|
|
@@ -119,14 +120,24 @@ export const createRnStripeConfirmer = (): PaymentConfirmer => {
|
|
|
119
120
|
},
|
|
120
121
|
|
|
121
122
|
confirmApplePay: async (clientSecret, total, currencyCode, merchant) => {
|
|
123
|
+
const context = stripeWalletPaymentContext(
|
|
124
|
+
total,
|
|
125
|
+
currencyCode,
|
|
126
|
+
merchant.merchantCountryCode
|
|
127
|
+
)
|
|
128
|
+
if (!context)
|
|
129
|
+
throw paymentProviderError(
|
|
130
|
+
'The order wallet details are unavailable.',
|
|
131
|
+
'notStarted'
|
|
132
|
+
)
|
|
122
133
|
const { error, paymentIntent } = await confirmPlatformPayPayment(clientSecret, {
|
|
123
134
|
applePay: {
|
|
124
|
-
merchantCountryCode:
|
|
125
|
-
currencyCode,
|
|
135
|
+
merchantCountryCode: context.merchantCountry,
|
|
136
|
+
currencyCode: context.currency,
|
|
126
137
|
cartItems: [
|
|
127
138
|
{
|
|
128
139
|
label: merchant.merchantName ?? 'Order',
|
|
129
|
-
amount:
|
|
140
|
+
amount: context.amount,
|
|
130
141
|
paymentType: PlatformPay.PaymentType.Immediate,
|
|
131
142
|
},
|
|
132
143
|
],
|
|
@@ -137,11 +148,21 @@ export const createRnStripeConfirmer = (): PaymentConfirmer => {
|
|
|
137
148
|
},
|
|
138
149
|
|
|
139
150
|
confirmGooglePay: async (clientSecret, total, currencyCode, merchant) => {
|
|
151
|
+
const context = stripeWalletPaymentContext(
|
|
152
|
+
total,
|
|
153
|
+
currencyCode,
|
|
154
|
+
merchant.merchantCountryCode
|
|
155
|
+
)
|
|
156
|
+
if (!context)
|
|
157
|
+
throw paymentProviderError(
|
|
158
|
+
'The order wallet details are unavailable.',
|
|
159
|
+
'notStarted'
|
|
160
|
+
)
|
|
140
161
|
const { error, paymentIntent } = await confirmPlatformPayPayment(clientSecret, {
|
|
141
162
|
googlePay: {
|
|
142
163
|
testEnv: merchant.testEnv === true,
|
|
143
|
-
merchantCountryCode:
|
|
144
|
-
currencyCode,
|
|
164
|
+
merchantCountryCode: context.merchantCountry,
|
|
165
|
+
currencyCode: context.currency,
|
|
145
166
|
merchantName: merchant.merchantName ?? 'Order',
|
|
146
167
|
},
|
|
147
168
|
})
|
package/src/sections/AddOns.tsx
CHANGED
|
@@ -152,16 +152,18 @@ export const AddOns: FC<AddOnsProps> = ({
|
|
|
152
152
|
</Text>
|
|
153
153
|
{desc ? <Text style={s.desc}>{desc}</Text> : null}
|
|
154
154
|
</View>
|
|
155
|
-
{
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
155
|
+
<View style={s.quantity}>
|
|
156
|
+
{soldOut(a) ? (
|
|
157
|
+
<Text style={s.soldOut}>Sold out</Text>
|
|
158
|
+
) : (
|
|
159
|
+
<QuantitySelect
|
|
160
|
+
value={n}
|
|
161
|
+
min={0}
|
|
162
|
+
max={max(a)}
|
|
163
|
+
onChange={(v) => setQty((q) => ({ ...q, [a.id]: v }))}
|
|
164
|
+
/>
|
|
165
|
+
)}
|
|
166
|
+
</View>
|
|
165
167
|
</View>
|
|
166
168
|
{n > 0 && group ? (
|
|
167
169
|
<View style={s.fields}>
|
|
@@ -223,7 +225,14 @@ const useAddonStyles = (theme: ResolvedTheme) =>
|
|
|
223
225
|
fontWeight: '700',
|
|
224
226
|
fontFamily: theme.fontFamily,
|
|
225
227
|
},
|
|
226
|
-
skipBtn: {
|
|
228
|
+
skipBtn: {
|
|
229
|
+
maxWidth: '100%',
|
|
230
|
+
minWidth: 44,
|
|
231
|
+
minHeight: 44,
|
|
232
|
+
paddingHorizontal: 8,
|
|
233
|
+
paddingVertical: 10,
|
|
234
|
+
justifyContent: 'center',
|
|
235
|
+
},
|
|
227
236
|
skip: {
|
|
228
237
|
color: theme.accent,
|
|
229
238
|
fontSize: theme.fontSize - 1,
|
|
@@ -247,8 +256,14 @@ const useAddonStyles = (theme: ResolvedTheme) =>
|
|
|
247
256
|
fontFamily: theme.fontFamily,
|
|
248
257
|
},
|
|
249
258
|
item: { gap: 10 },
|
|
250
|
-
row: {
|
|
251
|
-
|
|
259
|
+
row: {
|
|
260
|
+
flexDirection: 'row',
|
|
261
|
+
flexWrap: 'wrap',
|
|
262
|
+
alignItems: 'flex-start',
|
|
263
|
+
gap: 16,
|
|
264
|
+
},
|
|
265
|
+
info: { flexGrow: 1, flexShrink: 1, flexBasis: 220, gap: 8 },
|
|
266
|
+
quantity: { marginStart: 'auto', maxWidth: '100%' },
|
|
252
267
|
name: {
|
|
253
268
|
color: theme.text,
|
|
254
269
|
fontSize: theme.fontSize,
|
|
@@ -489,7 +489,11 @@ export const BillingInfo: FC<BillingInfoProps> = ({
|
|
|
489
489
|
{onLogin && !checkout.isLoggedIn ? (
|
|
490
490
|
<View style={s.loginRow}>
|
|
491
491
|
<Text style={s.note}>Login & skip ahead:</Text>
|
|
492
|
-
<Pressable
|
|
492
|
+
<Pressable
|
|
493
|
+
accessibilityRole="button"
|
|
494
|
+
style={s.loginAction}
|
|
495
|
+
onPress={() => onLogin()}
|
|
496
|
+
>
|
|
493
497
|
<Text style={s.link}>Login</Text>
|
|
494
498
|
</Pressable>
|
|
495
499
|
</View>
|
|
@@ -740,7 +744,20 @@ const useStyles = (theme: ResolvedTheme) =>
|
|
|
740
744
|
useMemo(
|
|
741
745
|
() =>
|
|
742
746
|
StyleSheet.create({
|
|
743
|
-
loginRow: {
|
|
747
|
+
loginRow: {
|
|
748
|
+
flexDirection: 'row',
|
|
749
|
+
flexWrap: 'wrap',
|
|
750
|
+
alignItems: 'center',
|
|
751
|
+
gap: 12,
|
|
752
|
+
},
|
|
753
|
+
loginAction: {
|
|
754
|
+
maxWidth: '100%',
|
|
755
|
+
minWidth: 44,
|
|
756
|
+
minHeight: 44,
|
|
757
|
+
paddingHorizontal: 8,
|
|
758
|
+
paddingVertical: 10,
|
|
759
|
+
justifyContent: 'center',
|
|
760
|
+
},
|
|
744
761
|
note: {
|
|
745
762
|
color: theme.muted,
|
|
746
763
|
fontSize: theme.fontSize - 1,
|
|
@@ -765,8 +782,8 @@ const useStyles = (theme: ResolvedTheme) =>
|
|
|
765
782
|
fontSize: theme.fontSize - 1,
|
|
766
783
|
fontFamily: theme.fontFamily,
|
|
767
784
|
},
|
|
768
|
-
row: { flexDirection: 'row', gap:
|
|
769
|
-
col: {
|
|
785
|
+
row: { flexDirection: 'row', flexWrap: 'wrap', gap: 12 },
|
|
786
|
+
col: { flexGrow: 1, flexShrink: 1, flexBasis: 180 },
|
|
770
787
|
}),
|
|
771
788
|
[theme]
|
|
772
789
|
)
|
package/src/sections/Tickets.tsx
CHANGED
|
@@ -397,6 +397,7 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
397
397
|
{t.descriptionRich ? (
|
|
398
398
|
<Pressable
|
|
399
399
|
accessibilityRole="button"
|
|
400
|
+
style={s.textAction}
|
|
400
401
|
onPress={() =>
|
|
401
402
|
setVisibleDescription((cur) => (cur === t.id ? null : t.id))
|
|
402
403
|
}
|
|
@@ -469,6 +470,7 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
469
470
|
) : (
|
|
470
471
|
<Pressable
|
|
471
472
|
accessibilityRole="button"
|
|
473
|
+
style={s.textAction}
|
|
472
474
|
onPress={() => setWlOpen((w) => ({ ...w, [t.id]: true }))}
|
|
473
475
|
>
|
|
474
476
|
<Text style={s.wlToggle}>Join the waiting list</Text>
|
|
@@ -557,7 +559,11 @@ export const Tickets: FC<TicketsProps> = ({
|
|
|
557
559
|
{showCodeInput ? (
|
|
558
560
|
<View style={s.promo}>
|
|
559
561
|
{!isAccess && !promoOpen ? (
|
|
560
|
-
<Pressable
|
|
562
|
+
<Pressable
|
|
563
|
+
accessibilityRole="button"
|
|
564
|
+
style={s.textAction}
|
|
565
|
+
onPress={() => setPromoOpen(true)}
|
|
566
|
+
>
|
|
561
567
|
<Text style={s.promoToggle}>Got a promo code? Tap here</Text>
|
|
562
568
|
</Pressable>
|
|
563
569
|
) : (
|
|
@@ -672,10 +678,25 @@ const useTicketStyles = (theme: ResolvedTheme) =>
|
|
|
672
678
|
marginTop: 4,
|
|
673
679
|
fontFamily: theme.fontFamily,
|
|
674
680
|
},
|
|
675
|
-
rowWrap: { gap:
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
681
|
+
rowWrap: { gap: 12 },
|
|
682
|
+
// Keep a readable text column; a narrow host moves controls to the next line.
|
|
683
|
+
row: {
|
|
684
|
+
flexDirection: 'row',
|
|
685
|
+
flexWrap: 'wrap',
|
|
686
|
+
alignItems: 'flex-start',
|
|
687
|
+
gap: 16,
|
|
688
|
+
},
|
|
689
|
+
info: { flexGrow: 1, flexShrink: 1, flexBasis: 220, gap: 8 },
|
|
690
|
+
nameRow: { gap: 4 },
|
|
691
|
+
textAction: {
|
|
692
|
+
alignSelf: 'flex-start',
|
|
693
|
+
maxWidth: '100%',
|
|
694
|
+
minWidth: 44,
|
|
695
|
+
minHeight: 44,
|
|
696
|
+
paddingHorizontal: 8,
|
|
697
|
+
paddingVertical: 10,
|
|
698
|
+
justifyContent: 'center',
|
|
699
|
+
},
|
|
679
700
|
name: {
|
|
680
701
|
color: theme.text,
|
|
681
702
|
fontSize: theme.fontSize,
|
|
@@ -692,7 +713,7 @@ const useTicketStyles = (theme: ResolvedTheme) =>
|
|
|
692
713
|
flexDirection: 'row',
|
|
693
714
|
alignItems: 'center',
|
|
694
715
|
flexWrap: 'wrap',
|
|
695
|
-
gap:
|
|
716
|
+
gap: 8,
|
|
696
717
|
},
|
|
697
718
|
oldPrice: {
|
|
698
719
|
color: theme.muted,
|
|
@@ -726,12 +747,22 @@ const useTicketStyles = (theme: ResolvedTheme) =>
|
|
|
726
747
|
fontFamily: theme.fontFamily,
|
|
727
748
|
},
|
|
728
749
|
soldout: {
|
|
750
|
+
marginStart: 'auto',
|
|
751
|
+
maxWidth: '100%',
|
|
729
752
|
color: theme.muted,
|
|
730
753
|
fontSize: theme.fontSize - 1,
|
|
731
754
|
fontWeight: '600',
|
|
732
755
|
fontFamily: theme.fontFamily,
|
|
733
756
|
},
|
|
734
|
-
qtyWrap: {
|
|
757
|
+
qtyWrap: {
|
|
758
|
+
flexDirection: 'row',
|
|
759
|
+
flexWrap: 'wrap',
|
|
760
|
+
maxWidth: '100%',
|
|
761
|
+
alignItems: 'center',
|
|
762
|
+
justifyContent: 'flex-end',
|
|
763
|
+
gap: 12,
|
|
764
|
+
marginStart: 'auto',
|
|
765
|
+
},
|
|
735
766
|
guests: {
|
|
736
767
|
color: theme.muted,
|
|
737
768
|
fontSize: theme.fontSize - 2,
|
|
@@ -741,6 +772,7 @@ const useTicketStyles = (theme: ResolvedTheme) =>
|
|
|
741
772
|
description: {
|
|
742
773
|
color: theme.muted,
|
|
743
774
|
fontSize: theme.fontSize - 1,
|
|
775
|
+
lineHeight: theme.fontSize * 1.5,
|
|
744
776
|
fontFamily: theme.fontFamily,
|
|
745
777
|
},
|
|
746
778
|
wlToggle: {
|
package/src/ui/CustomFields.tsx
CHANGED
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
* - `<select>` and `radio` (single choice) both render as the native `Select`
|
|
9
9
|
* sheet — there is no native radio primitive.
|
|
10
10
|
* - `select_multi` renders a column of native `Checkbox` rows.
|
|
11
|
-
* -
|
|
12
|
-
* single-line `Input` (value parity preserved).
|
|
11
|
+
* - `textarea` uses the native multiline Input, preserving line breaks.
|
|
13
12
|
*/
|
|
14
13
|
import React, { type FC } from 'react'
|
|
15
14
|
import { Image, View } from 'react-native'
|
|
@@ -94,11 +93,15 @@ export const CustomFieldInput: FC<CustomFieldInputProps> = ({
|
|
|
94
93
|
|
|
95
94
|
switch (field.type) {
|
|
96
95
|
case 'textarea':
|
|
97
|
-
// Native adaptation: no multiline Textarea primitive — single-line Input.
|
|
98
96
|
return (
|
|
99
97
|
<Field label={field.label} required={field.required} error={error}>
|
|
100
98
|
{desc}
|
|
101
|
-
<Input
|
|
99
|
+
<Input
|
|
100
|
+
multiline
|
|
101
|
+
value={asString(value)}
|
|
102
|
+
onChangeText={onChange}
|
|
103
|
+
invalid={invalid}
|
|
104
|
+
/>
|
|
102
105
|
</Field>
|
|
103
106
|
)
|
|
104
107
|
case 'select':
|
package/src/ui/ProviderPages.tsx
CHANGED
|
@@ -35,9 +35,21 @@ const secureInitialUri = (uri: string): boolean => {
|
|
|
35
35
|
if (hasUnsafeNativeUriCharacters(uri)) return false
|
|
36
36
|
if (!/^https:\/\/[^/?#\s\\]+(?:[/?#][^\s\\]*)?$/i.test(uri)) return false
|
|
37
37
|
try {
|
|
38
|
-
|
|
38
|
+
// RN's URL runtime exposes these fields, but its global type omits them.
|
|
39
|
+
// Check the runtime surface rather than assuming a host polyfill's shape.
|
|
40
|
+
const url: unknown = new URL(uri)
|
|
39
41
|
return (
|
|
40
|
-
url
|
|
42
|
+
url !== null &&
|
|
43
|
+
typeof url === 'object' &&
|
|
44
|
+
'protocol' in url &&
|
|
45
|
+
url.protocol === 'https:' &&
|
|
46
|
+
'hostname' in url &&
|
|
47
|
+
typeof url.hostname === 'string' &&
|
|
48
|
+
Boolean(url.hostname) &&
|
|
49
|
+
'username' in url &&
|
|
50
|
+
url.username === '' &&
|
|
51
|
+
'password' in url &&
|
|
52
|
+
url.password === ''
|
|
41
53
|
)
|
|
42
54
|
} catch {
|
|
43
55
|
return false
|
|
@@ -105,9 +105,10 @@ const useQuantityStyles = (theme: ResolvedTheme) =>
|
|
|
105
105
|
StyleSheet.create({
|
|
106
106
|
stepper: { flexDirection: 'row', alignItems: 'center', gap: 14 },
|
|
107
107
|
btn: {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
minWidth: 44,
|
|
109
|
+
minHeight: 44,
|
|
110
|
+
padding: 10,
|
|
111
|
+
borderRadius: 22,
|
|
111
112
|
borderWidth: StyleSheet.hairlineWidth,
|
|
112
113
|
borderColor: theme.border,
|
|
113
114
|
alignItems: 'center',
|
package/src/ui/primitives.tsx
CHANGED
|
@@ -191,6 +191,8 @@ export const useStyles = (theme: ResolvedTheme) =>
|
|
|
191
191
|
buttonDisabled: { opacity: 0.5 },
|
|
192
192
|
buttonPressed: { opacity: 0.85 },
|
|
193
193
|
buttonLabel: {
|
|
194
|
+
maxWidth: '100%',
|
|
195
|
+
textAlign: 'center',
|
|
194
196
|
color: theme.accentContrast,
|
|
195
197
|
fontSize: theme.fontSize,
|
|
196
198
|
fontWeight: '600',
|