@heyama/pay-react-native 0.0.0-stage → 1.0.0
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/LICENSE +21 -0
- package/README.md +99 -2
- package/assets/fonts/Figtree-Bold.ttf +0 -0
- package/assets/fonts/Figtree-ExtraBold.ttf +0 -0
- package/assets/fonts/Figtree-Medium.ttf +0 -0
- package/assets/fonts/Figtree-Regular.ttf +0 -0
- package/assets/fonts/Figtree-SemiBold.ttf +0 -0
- package/assets/fonts/OFL.txt +93 -0
- package/assets/logos/apple-pay.png +0 -0
- package/assets/logos/card.png +0 -0
- package/assets/logos/google-pay.png +0 -0
- package/assets/logos/mastercard.png +0 -0
- package/assets/logos/mixx.png +0 -0
- package/assets/logos/moov.png +0 -0
- package/assets/logos/mtn.png +0 -0
- package/assets/logos/orange.png +0 -0
- package/assets/logos/paypal.png +0 -0
- package/assets/logos/visa.png +0 -0
- package/assets/logos/wave.png +0 -0
- package/dist/checkout-core/hooks/use-checkout-controller.d.ts +9 -0
- package/dist/checkout-core/hooks/use-checkout-controller.js +319 -0
- package/dist/checkout-core/hooks/use-checkout-timers.d.ts +15 -0
- package/dist/checkout-core/hooks/use-checkout-timers.js +44 -0
- package/dist/checkout-core/hooks/use-phone-number.d.ts +7 -0
- package/dist/checkout-core/hooks/use-phone-number.js +82 -0
- package/dist/checkout-core/messages/en.d.ts +252 -0
- package/dist/checkout-core/messages/en.js +242 -0
- package/dist/checkout-core/messages/fr.d.ts +4 -0
- package/dist/checkout-core/messages/fr.js +244 -0
- package/dist/checkout-core/models/checkout.card.utils.d.ts +22 -0
- package/dist/checkout-core/models/checkout.card.utils.js +101 -0
- package/dist/checkout-core/models/checkout.constants.d.ts +56 -0
- package/dist/checkout-core/models/checkout.constants.js +142 -0
- package/dist/checkout-core/models/checkout.enum.d.ts +131 -0
- package/dist/checkout-core/models/checkout.enum.js +148 -0
- package/dist/checkout-core/models/checkout.interface.d.ts +417 -0
- package/dist/checkout-core/models/checkout.interface.js +1 -0
- package/dist/checkout-core/models/checkout.money.utils.d.ts +7 -0
- package/dist/checkout-core/models/checkout.money.utils.js +21 -0
- package/dist/checkout-core/models/checkout.phone.utils.d.ts +33 -0
- package/dist/checkout-core/models/checkout.phone.utils.js +159 -0
- package/dist/checkout-core/models/checkout.view.utils.d.ts +34 -0
- package/dist/checkout-core/models/checkout.view.utils.js +205 -0
- package/dist/checkout-core/models/domain.enum.d.ts +56 -0
- package/dist/checkout-core/models/domain.enum.js +62 -0
- package/dist/components/BrissotoPayProvider.d.ts +6 -0
- package/dist/components/BrissotoPayProvider.js +41 -0
- package/dist/components/PaymentSheetModal.d.ts +8 -0
- package/dist/components/PaymentSheetModal.js +72 -0
- package/dist/components/SheetBone.d.ts +3 -0
- package/dist/components/SheetBone.js +12 -0
- package/dist/components/SheetButton.d.ts +3 -0
- package/dist/components/SheetButton.js +33 -0
- package/dist/components/SheetCardFields.d.ts +8 -0
- package/dist/components/SheetCardFields.js +66 -0
- package/dist/components/SheetCodeField.d.ts +8 -0
- package/dist/components/SheetCodeField.js +95 -0
- package/dist/components/SheetContent.d.ts +3 -0
- package/dist/components/SheetContent.js +106 -0
- package/dist/components/SheetFailed.d.ts +3 -0
- package/dist/components/SheetFailed.js +43 -0
- package/dist/components/SheetForm.d.ts +7 -0
- package/dist/components/SheetForm.js +100 -0
- package/dist/components/SheetHeader.d.ts +6 -0
- package/dist/components/SheetHeader.js +69 -0
- package/dist/components/SheetIcon.d.ts +3 -0
- package/dist/components/SheetIcon.js +70 -0
- package/dist/components/SheetLayout.d.ts +8 -0
- package/dist/components/SheetLayout.js +35 -0
- package/dist/components/SheetLogo.d.ts +3 -0
- package/dist/components/SheetLogo.js +20 -0
- package/dist/components/SheetMessage.d.ts +3 -0
- package/dist/components/SheetMessage.js +15 -0
- package/dist/components/SheetMethodCard.d.ts +3 -0
- package/dist/components/SheetMethodCard.js +39 -0
- package/dist/components/SheetPhoneField.d.ts +3 -0
- package/dist/components/SheetPhoneField.js +56 -0
- package/dist/components/SheetRow.d.ts +3 -0
- package/dist/components/SheetRow.js +28 -0
- package/dist/components/SheetSimulator.d.ts +6 -0
- package/dist/components/SheetSimulator.js +39 -0
- package/dist/components/SheetSkeleton.d.ts +5 -0
- package/dist/components/SheetSkeleton.js +23 -0
- package/dist/components/SheetSuccess.d.ts +3 -0
- package/dist/components/SheetSuccess.js +51 -0
- package/dist/components/SheetSummary.d.ts +3 -0
- package/dist/components/SheetSummary.js +32 -0
- package/dist/components/SheetTerminal.d.ts +3 -0
- package/dist/components/SheetTerminal.js +21 -0
- package/dist/components/SheetText.d.ts +5 -0
- package/dist/components/SheetText.js +25 -0
- package/dist/components/SheetToast.d.ts +3 -0
- package/dist/components/SheetToast.js +24 -0
- package/dist/components/SheetWaiting.d.ts +7 -0
- package/dist/components/SheetWaiting.js +77 -0
- package/dist/hooks/use-native-platform.d.ts +10 -0
- package/dist/hooks/use-native-platform.js +47 -0
- package/dist/hooks/use-payment-sheet.d.ts +4 -0
- package/dist/hooks/use-payment-sheet.js +10 -0
- package/dist/hooks/use-sheet-motion.d.ts +7 -0
- package/dist/hooks/use-sheet-motion.js +62 -0
- package/dist/hooks/use-sheet-resize.d.ts +6 -0
- package/dist/hooks/use-sheet-resize.js +15 -0
- package/dist/hooks/use-sheet-theme.d.ts +4 -0
- package/dist/hooks/use-sheet-theme.js +12 -0
- package/dist/hooks/use-watched-payment.d.ts +6 -0
- package/dist/hooks/use-watched-payment.js +17 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/models/sheet.constants.d.ts +39 -0
- package/dist/models/sheet.constants.js +78 -0
- package/dist/models/sheet.enum.d.ts +16 -0
- package/dist/models/sheet.enum.js +18 -0
- package/dist/models/sheet.interface.d.ts +225 -0
- package/dist/models/sheet.interface.js +1 -0
- package/dist/models/sheet.utils.d.ts +43 -0
- package/dist/models/sheet.utils.js +132 -0
- package/dist/services/checkout.service.d.ts +7 -0
- package/dist/services/checkout.service.js +39 -0
- package/package.json +58 -4
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useFonts } from 'expo-font';
|
|
3
|
+
import { useMemo, useState } from 'react';
|
|
4
|
+
import { Alert, Animated, KeyboardAvoidingView, Modal, Pressable, StyleSheet, View, useColorScheme, } from 'react-native';
|
|
5
|
+
import { IntlProvider, createTranslator } from 'use-intl';
|
|
6
|
+
import { CHECKOUT_COLORS, INVALID_BRAND_DARK, INVALID_BRAND_LIGHT, } from "../checkout-core/models/checkout.constants.js";
|
|
7
|
+
import { CheckoutThemeEnum } from "../checkout-core/models/checkout.enum.js";
|
|
8
|
+
import { useSheetMotion } from "../hooks/use-sheet-motion.js";
|
|
9
|
+
import { useWatchedPayment } from "../hooks/use-watched-payment.js";
|
|
10
|
+
import { SheetThemeContext } from "../hooks/use-sheet-theme.js";
|
|
11
|
+
import { FIGTREE_FILES, MESSAGES, TIME_ZONE, } from "../models/sheet.constants.js";
|
|
12
|
+
import { sheetLanguage, sheetResult, sheetTheme, } from "../models/sheet.utils.js";
|
|
13
|
+
import { SheetContent } from "./SheetContent.js";
|
|
14
|
+
/**
|
|
15
|
+
* One payment in a bottom sheet over the dimmed app, as tall as what it
|
|
16
|
+
* shows, in the payment's language and theme, else the device's. Close,
|
|
17
|
+
* a swipe down, a tap on the app behind, back or escape leave it; leaving
|
|
18
|
+
* while a prompt waits on the phone asks first, and resolves `pending`.
|
|
19
|
+
*/
|
|
20
|
+
export function PaymentSheetModal({ calls, request, onClose, }) {
|
|
21
|
+
const watched = useWatchedPayment(calls, request.open ? request.paymentId : null);
|
|
22
|
+
const { view } = watched;
|
|
23
|
+
const scheme = useColorScheme();
|
|
24
|
+
const [deviceLocale] = useState(() => Intl.DateTimeFormat().resolvedOptions().locale);
|
|
25
|
+
const [fontsLoaded, fontsError] = useFonts(FIGTREE_FILES);
|
|
26
|
+
const lang = sheetLanguage(view?.language ?? null, deviceLocale);
|
|
27
|
+
const dark = sheetTheme(view?.theme ?? null, scheme) === CheckoutThemeEnum.DARK;
|
|
28
|
+
const color = view?.app.color;
|
|
29
|
+
const theme = useMemo(() => ({
|
|
30
|
+
colors: CHECKOUT_COLORS[dark ? CheckoutThemeEnum.DARK : CheckoutThemeEnum.LIGHT],
|
|
31
|
+
brand: color ?? (dark ? INVALID_BRAND_DARK : INVALID_BRAND_LIGHT),
|
|
32
|
+
dark,
|
|
33
|
+
}), [dark, color]);
|
|
34
|
+
const close = () => {
|
|
35
|
+
const result = sheetResult(view, watched.failed);
|
|
36
|
+
if (result.status !== 'pending') {
|
|
37
|
+
onClose(result);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// A prompt still waits: approved later, the payment goes through.
|
|
41
|
+
const t = createTranslator({
|
|
42
|
+
locale: lang,
|
|
43
|
+
messages: MESSAGES[lang],
|
|
44
|
+
namespace: 'checkout.sheet',
|
|
45
|
+
});
|
|
46
|
+
Alert.alert(t('close_title'), t('close_body'), [
|
|
47
|
+
{ text: t('keep_waiting'), style: 'cancel' },
|
|
48
|
+
{
|
|
49
|
+
text: MESSAGES[lang].checkout.close,
|
|
50
|
+
style: 'destructive',
|
|
51
|
+
onPress: () => onClose(result),
|
|
52
|
+
},
|
|
53
|
+
]);
|
|
54
|
+
};
|
|
55
|
+
const motion = useSheetMotion(request.open, close);
|
|
56
|
+
return (_jsx(Modal, { testID: "payment-sheet-modal", visible: motion.shown, transparent: true, animationType: "none", statusBarTranslucent: true, onRequestClose: close, children: _jsxs(View, { style: [styles.screen, !request.open && styles.leaving], children: [_jsx(Pressable, { testID: "payment-sheet-backdrop", accessible: false, onPress: close, style: StyleSheet.absoluteFill, children: _jsx(Animated.View, { style: [
|
|
57
|
+
StyleSheet.absoluteFill,
|
|
58
|
+
styles.dim,
|
|
59
|
+
{ opacity: motion.dim },
|
|
60
|
+
] }) }), _jsx(Animated.View, { style: [
|
|
61
|
+
styles.stage,
|
|
62
|
+
{ transform: [{ translateY: motion.translateY }] },
|
|
63
|
+
], children: _jsx(KeyboardAvoidingView, { behavior: "padding", style: [styles.stage, { paddingTop: motion.topGap }], children: _jsx(View, { style: styles.sheet, children: _jsx(IntlProvider, { locale: lang, messages: MESSAGES[lang], timeZone: TIME_ZONE, children: _jsx(SheetThemeContext, { value: theme, children: _jsx(SheetContent, { calls: calls, watched: watched, fontsReady: fontsLoaded || fontsError !== null, deviceLocale: deviceLocale, onClose: close, dragHandlers: motion.dragHandlers }) }) }) }) }) })] }) }));
|
|
64
|
+
}
|
|
65
|
+
const styles = StyleSheet.create({
|
|
66
|
+
screen: { flex: 1 },
|
|
67
|
+
leaving: { pointerEvents: 'none' },
|
|
68
|
+
dim: { backgroundColor: 'black' },
|
|
69
|
+
// The app's dim shows through around the sheet.
|
|
70
|
+
stage: { flex: 1, justifyContent: 'flex-end', pointerEvents: 'box-none' },
|
|
71
|
+
sheet: { flexShrink: 1 },
|
|
72
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { StyleSheet, View } from 'react-native';
|
|
3
|
+
import { useSheetTheme } from "../hooks/use-sheet-theme.js";
|
|
4
|
+
/** A rounded block standing in for what is loading, sized like it. */
|
|
5
|
+
export function SheetBone({ width, height, radius = 6 }) {
|
|
6
|
+
const { colors } = useSheetTheme();
|
|
7
|
+
return (_jsx(View, { style: [
|
|
8
|
+
styles.bone,
|
|
9
|
+
{ width, height, borderRadius: radius, backgroundColor: colors.fill },
|
|
10
|
+
] }));
|
|
11
|
+
}
|
|
12
|
+
const styles = StyleSheet.create({ bone: { flex: 0 } });
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SheetButtonProps } from '../models/sheet.interface.js';
|
|
2
|
+
/** The sheet's buttons: filled in the app colour for the main action, outlined for the rest. */
|
|
3
|
+
export declare function SheetButton({ label, onPress, variant, busy, disabled, inactive, }: SheetButtonProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ActivityIndicator, StyleSheet, TouchableOpacity } from 'react-native';
|
|
3
|
+
import { CheckoutButtonVariantEnum } from "../checkout-core/models/checkout.enum.js";
|
|
4
|
+
import { useSheetTheme } from "../hooks/use-sheet-theme.js";
|
|
5
|
+
import { FigtreeEnum } from "../models/sheet.enum.js";
|
|
6
|
+
import { SheetText } from "./SheetText.js";
|
|
7
|
+
/** The sheet's buttons: filled in the app colour for the main action, outlined for the rest. */
|
|
8
|
+
export function SheetButton({ label, onPress, variant = CheckoutButtonVariantEnum.PRIMARY, busy = false, disabled = false, inactive = false, }) {
|
|
9
|
+
const { colors, brand } = useSheetTheme();
|
|
10
|
+
const primary = variant === CheckoutButtonVariantEnum.PRIMARY;
|
|
11
|
+
const ink = primary ? 'white' : colors.ink;
|
|
12
|
+
const off = disabled || busy;
|
|
13
|
+
return (_jsxs(TouchableOpacity, { onPress: onPress, disabled: off, activeOpacity: 0.8, accessibilityRole: "button", accessibilityState: { disabled: off, busy }, style: [
|
|
14
|
+
styles.button,
|
|
15
|
+
primary
|
|
16
|
+
? { backgroundColor: brand }
|
|
17
|
+
: { borderWidth: 1, borderColor: colors.line3 },
|
|
18
|
+
(disabled || inactive) && styles.disabled,
|
|
19
|
+
], children: [busy && _jsx(ActivityIndicator, { color: ink }), _jsx(SheetText, { weight: FigtreeEnum.BOLD, size: 16, color: ink, style: styles.label, children: label })] }));
|
|
20
|
+
}
|
|
21
|
+
const styles = StyleSheet.create({
|
|
22
|
+
button: {
|
|
23
|
+
minHeight: 50,
|
|
24
|
+
borderRadius: 14,
|
|
25
|
+
flexDirection: 'row',
|
|
26
|
+
alignItems: 'center',
|
|
27
|
+
justifyContent: 'center',
|
|
28
|
+
gap: 10,
|
|
29
|
+
paddingHorizontal: 16,
|
|
30
|
+
},
|
|
31
|
+
label: { textAlign: 'center', fontVariant: ['tabular-nums'] },
|
|
32
|
+
disabled: { opacity: 0.5 },
|
|
33
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SheetCardFieldsProps } from '../models/sheet.interface.js';
|
|
2
|
+
/**
|
|
3
|
+
* A card method: its provider's card fields page, and only it, in a WebView
|
|
4
|
+
* sized to it. The card is typed there and goes from there to the provider;
|
|
5
|
+
* the sheet only hears, from that page's origin, how it went. The labels and
|
|
6
|
+
* bones stand in until the page says how tall it is.
|
|
7
|
+
*/
|
|
8
|
+
export declare function SheetCardFields({ core, bridge }: SheetCardFieldsProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { StyleSheet, View } from 'react-native';
|
|
4
|
+
import { WebView } from 'react-native-webview';
|
|
5
|
+
import { useLocale, useTranslations } from 'use-intl';
|
|
6
|
+
import { CARD_FIELDS_HEIGHT } from "../checkout-core/models/checkout.constants.js";
|
|
7
|
+
import { useSheetTheme } from "../hooks/use-sheet-theme.js";
|
|
8
|
+
import { FigtreeEnum } from "../models/sheet.enum.js";
|
|
9
|
+
import { cardFieldsBridge, cardFieldsMessageOf, cardFieldsUrl, isCardFieldsPage, originOf, } from "../models/sheet.utils.js";
|
|
10
|
+
import { SheetBone } from "./SheetBone.js";
|
|
11
|
+
import { SheetText } from "./SheetText.js";
|
|
12
|
+
/** Every load goes through `mayLoad`: none is handed to the phone's browser. */
|
|
13
|
+
const ANY_ORIGIN = ['*'];
|
|
14
|
+
/**
|
|
15
|
+
* A card method: its provider's card fields page, and only it, in a WebView
|
|
16
|
+
* sized to it. The card is typed there and goes from there to the provider;
|
|
17
|
+
* the sheet only hears, from that page's origin, how it went. The labels and
|
|
18
|
+
* bones stand in until the page says how tall it is.
|
|
19
|
+
*/
|
|
20
|
+
export function SheetCardFields({ core, bridge }) {
|
|
21
|
+
const t = useTranslations('checkout.card_fields');
|
|
22
|
+
const lang = useLocale();
|
|
23
|
+
const { colors, dark } = useSheetTheme();
|
|
24
|
+
const { view, derived, cardHeight } = core;
|
|
25
|
+
// Shown only while a card method is picked, and they all come with their page.
|
|
26
|
+
const method = derived.selected;
|
|
27
|
+
const uri = cardFieldsUrl({
|
|
28
|
+
siteUrl: view.siteUrl,
|
|
29
|
+
path: method.cardFieldsPath,
|
|
30
|
+
paymentId: view.id,
|
|
31
|
+
method: method.id,
|
|
32
|
+
color: view.app.color,
|
|
33
|
+
dark,
|
|
34
|
+
lang,
|
|
35
|
+
});
|
|
36
|
+
const source = useMemo(() => ({ uri }), [uri]);
|
|
37
|
+
const loaded = cardHeight !== null;
|
|
38
|
+
// The page itself, in either language; the provider's own frames inside it.
|
|
39
|
+
const mayLoad = (request) => request.isTopFrame === false || isCardFieldsPage(request.url, uri);
|
|
40
|
+
// Only what comes from the card fields page's origin reaches the core.
|
|
41
|
+
const hear = ({ nativeEvent }) => {
|
|
42
|
+
if (originOf(nativeEvent.url) !== originOf(uri))
|
|
43
|
+
return;
|
|
44
|
+
const message = cardFieldsMessageOf(nativeEvent.data);
|
|
45
|
+
if (message)
|
|
46
|
+
bridge.receive(message);
|
|
47
|
+
};
|
|
48
|
+
const label = (text) => (_jsx(SheetText, { weight: FigtreeEnum.BOLD, size: 13, style: styles.label, children: text }));
|
|
49
|
+
return (_jsxs(View, { style: styles.section, children: [!loaded && (_jsxs(View, { style: styles.bones, children: [_jsxs(View, { children: [label(t('number')), _jsx(SheetBone, { width: "100%", height: 48, radius: 13 })] }), _jsxs(View, { style: styles.row, children: [_jsxs(View, { style: styles.grow, children: [label(t('expiry')), _jsx(SheetBone, { width: "100%", height: 48, radius: 13 })] }), _jsxs(View, { style: styles.grow, children: [label(t('cvc')), _jsx(SheetBone, { width: "100%", height: 48, radius: 13 })] })] })] })), _jsx(WebView, { ref: bridge.page, source: source, accessibilityLabel: t('title'), originWhitelist: ANY_ORIGIN, onShouldStartLoadWithRequest: mayLoad, injectedJavaScriptBeforeContentLoaded: cardFieldsBridge(), onMessage: hear, setSupportMultipleWindows: false, scrollEnabled: false, style: [
|
|
50
|
+
styles.page,
|
|
51
|
+
{
|
|
52
|
+
height: cardHeight ?? CARD_FIELDS_HEIGHT,
|
|
53
|
+
backgroundColor: colors.surface,
|
|
54
|
+
},
|
|
55
|
+
!loaded && styles.hidden,
|
|
56
|
+
] })] }));
|
|
57
|
+
}
|
|
58
|
+
const styles = StyleSheet.create({
|
|
59
|
+
section: { marginTop: 14 },
|
|
60
|
+
bones: { gap: 14, height: CARD_FIELDS_HEIGHT },
|
|
61
|
+
label: { marginBottom: 8 },
|
|
62
|
+
row: { flexDirection: 'row', gap: 12 },
|
|
63
|
+
grow: { flex: 1 },
|
|
64
|
+
page: { width: '100%' },
|
|
65
|
+
hidden: { position: 'absolute', top: 0, left: 0, right: 0, opacity: 0 },
|
|
66
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SheetScreenProps } from '../models/sheet.interface.js';
|
|
2
|
+
/**
|
|
3
|
+
* OTP methods: Dial opens the network's code in the phone dialler for a
|
|
4
|
+
* one-time payment code (on Android; on iOS it says to dial it by hand);
|
|
5
|
+
* back in the app the code field comes up, filled from the SMS when the
|
|
6
|
+
* phone offers it. Sent once with Pay, never kept.
|
|
7
|
+
*/
|
|
8
|
+
export declare function SheetCodeField({ core }: SheetScreenProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { AppState, Linking, Platform, StyleSheet, TouchableOpacity, View, } from 'react-native';
|
|
4
|
+
import { useTranslations } from 'use-intl';
|
|
5
|
+
import { OTP_MAX_LENGTH } from "../checkout-core/models/checkout.constants.js";
|
|
6
|
+
import { telHref } from "../checkout-core/models/checkout.phone.utils.js";
|
|
7
|
+
import { useSheetTheme } from "../hooks/use-sheet-theme.js";
|
|
8
|
+
import { FigtreeEnum } from "../models/sheet.enum.js";
|
|
9
|
+
import { SheetInput, SheetText } from "./SheetText.js";
|
|
10
|
+
/**
|
|
11
|
+
* OTP methods: Dial opens the network's code in the phone dialler for a
|
|
12
|
+
* one-time payment code (on Android; on iOS it says to dial it by hand);
|
|
13
|
+
* back in the app the code field comes up, filled from the SMS when the
|
|
14
|
+
* phone offers it. Sent once with Pay, never kept.
|
|
15
|
+
*/
|
|
16
|
+
export function SheetCodeField({ core }) {
|
|
17
|
+
const t = useTranslations('checkout');
|
|
18
|
+
const { colors, brand } = useSheetTheme();
|
|
19
|
+
const { ui, derived, actions } = core;
|
|
20
|
+
// Shown only while an OTP method, which always has its code, is picked.
|
|
21
|
+
const method = derived.selected;
|
|
22
|
+
const code = method.otpCode;
|
|
23
|
+
const field = useRef(null);
|
|
24
|
+
const [focused, setFocused] = useState(false);
|
|
25
|
+
/** Waiting for the payer to come back from the dialler. */
|
|
26
|
+
const away = useRef(null);
|
|
27
|
+
const stopWaiting = () => {
|
|
28
|
+
away.current?.remove();
|
|
29
|
+
away.current = null;
|
|
30
|
+
};
|
|
31
|
+
useEffect(() => stopWaiting, []);
|
|
32
|
+
const dial = () => {
|
|
33
|
+
// The iPhone's Phone app refuses a tel: link with * or #: say to dial it by hand.
|
|
34
|
+
if (Platform.OS === 'ios') {
|
|
35
|
+
actions.cannotDial();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
stopWaiting();
|
|
39
|
+
away.current = AppState.addEventListener('change', (state) => {
|
|
40
|
+
if (state !== 'active')
|
|
41
|
+
return;
|
|
42
|
+
stopWaiting();
|
|
43
|
+
field.current?.focus();
|
|
44
|
+
});
|
|
45
|
+
Linking.openURL(telHref(code)).catch(() => {
|
|
46
|
+
stopWaiting();
|
|
47
|
+
actions.cannotDial();
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
return (_jsxs(View, { style: styles.section, children: [_jsx(SheetText, { weight: FigtreeEnum.BOLD, style: styles.label, children: t('otp_label') }), _jsx(SheetInput, { ref: field, accessibilityLabel: t('otp_label'), keyboardType: "number-pad", textContentType: "oneTimeCode", autoComplete: "sms-otp", returnKeyType: "go", maxLength: OTP_MAX_LENGTH, value: ui.code, onChangeText: actions.setCode, onFocus: () => setFocused(true), onBlur: () => setFocused(false), onSubmitEditing: actions.pay, editable: !ui.submitting, style: [styles.input, { borderColor: focused ? brand : colors.line3 }] }), _jsxs(View, { style: [styles.help, { backgroundColor: colors.fill2 }], children: [_jsx(SheetText, { size: 13, color: colors.ink2, style: styles.grow, children: t('otp_help', { method: method.name }) }), _jsxs(TouchableOpacity, { onPress: dial, activeOpacity: 0.7, accessibilityRole: "button", accessibilityLabel: t('sheet.dial', { code }), style: [
|
|
51
|
+
styles.dial,
|
|
52
|
+
{ borderColor: colors.line3, backgroundColor: colors.surface },
|
|
53
|
+
], children: [_jsx(View, { style: [styles.phone, { borderColor: colors.ink }], children: _jsx(View, { style: [styles.speaker, { backgroundColor: colors.ink }] }) }), _jsx(SheetText, { weight: FigtreeEnum.BOLD, size: 14, children: code })] })] })] }));
|
|
54
|
+
}
|
|
55
|
+
const styles = StyleSheet.create({
|
|
56
|
+
section: { marginTop: 10 },
|
|
57
|
+
label: { marginBottom: 8 },
|
|
58
|
+
input: {
|
|
59
|
+
height: 50,
|
|
60
|
+
borderWidth: 1,
|
|
61
|
+
borderRadius: 13,
|
|
62
|
+
paddingHorizontal: 14,
|
|
63
|
+
fontSize: 18,
|
|
64
|
+
letterSpacing: 5,
|
|
65
|
+
},
|
|
66
|
+
help: {
|
|
67
|
+
flexDirection: 'row',
|
|
68
|
+
alignItems: 'center',
|
|
69
|
+
gap: 12,
|
|
70
|
+
borderRadius: 12,
|
|
71
|
+
paddingHorizontal: 14,
|
|
72
|
+
paddingVertical: 12,
|
|
73
|
+
marginTop: 12,
|
|
74
|
+
},
|
|
75
|
+
grow: { flex: 1 },
|
|
76
|
+
dial: {
|
|
77
|
+
flexDirection: 'row',
|
|
78
|
+
alignItems: 'center',
|
|
79
|
+
gap: 6,
|
|
80
|
+
borderWidth: 1,
|
|
81
|
+
borderRadius: 9,
|
|
82
|
+
paddingHorizontal: 12,
|
|
83
|
+
minHeight: 40,
|
|
84
|
+
},
|
|
85
|
+
phone: {
|
|
86
|
+
width: 10,
|
|
87
|
+
height: 16,
|
|
88
|
+
borderWidth: 1.6,
|
|
89
|
+
borderRadius: 3,
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
justifyContent: 'flex-end',
|
|
92
|
+
paddingBottom: 1.5,
|
|
93
|
+
},
|
|
94
|
+
speaker: { width: 3, height: 1.4, borderRadius: 0.7 },
|
|
95
|
+
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SheetContentProps } from '../models/sheet.interface.js';
|
|
2
|
+
/** The sheet's screens, driven by the shared checkout controller. */
|
|
3
|
+
export declare function SheetContent({ calls, watched, fontsReady, deviceLocale, onClose, dragHandlers, }: SheetContentProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
import { StyleSheet, View } from 'react-native';
|
|
4
|
+
import { useTranslations } from 'use-intl';
|
|
5
|
+
import { useCheckoutController } from "../checkout-core/hooks/use-checkout-controller.js";
|
|
6
|
+
import { CURRENCY_LABEL } from "../checkout-core/models/checkout.constants.js";
|
|
7
|
+
import { CheckoutScreenEnum, TerminalKindEnum, } from "../checkout-core/models/checkout.enum.js";
|
|
8
|
+
import { formatAmount } from "../checkout-core/models/checkout.money.utils.js";
|
|
9
|
+
import { useNativePlatform } from "../hooks/use-native-platform.js";
|
|
10
|
+
import { useSheetResize } from "../hooks/use-sheet-resize.js";
|
|
11
|
+
import { useSheetTheme } from "../hooks/use-sheet-theme.js";
|
|
12
|
+
import { SheetButton } from "./SheetButton.js";
|
|
13
|
+
import { SheetFailed } from "./SheetFailed.js";
|
|
14
|
+
import { SheetForm } from "./SheetForm.js";
|
|
15
|
+
import { SheetHeader } from "./SheetHeader.js";
|
|
16
|
+
import { SheetLayout } from "./SheetLayout.js";
|
|
17
|
+
import { SheetMessage } from "./SheetMessage.js";
|
|
18
|
+
import { SheetSkeleton } from "./SheetSkeleton.js";
|
|
19
|
+
import { SheetSuccess } from "./SheetSuccess.js";
|
|
20
|
+
import { SheetSummary } from "./SheetSummary.js";
|
|
21
|
+
import { SheetTerminal } from "./SheetTerminal.js";
|
|
22
|
+
import { SheetToast } from "./SheetToast.js";
|
|
23
|
+
import { SheetWaiting } from "./SheetWaiting.js";
|
|
24
|
+
const TERMINAL_KIND = {
|
|
25
|
+
[CheckoutScreenEnum.EXPIRED]: TerminalKindEnum.EXPIRED,
|
|
26
|
+
[CheckoutScreenEnum.CANCELLED]: TerminalKindEnum.CANCELLED,
|
|
27
|
+
[CheckoutScreenEnum.LOCKED]: TerminalKindEnum.LOCKED,
|
|
28
|
+
[CheckoutScreenEnum.NO_METHOD]: TerminalKindEnum.NO_METHOD,
|
|
29
|
+
};
|
|
30
|
+
/** The sheet's screens, driven by the shared checkout controller. */
|
|
31
|
+
export function SheetContent({ calls, watched, fontsReady, deviceLocale, onClose, dragHandlers, }) {
|
|
32
|
+
const t = useTranslations('checkout');
|
|
33
|
+
const { colors } = useSheetTheme();
|
|
34
|
+
const [clues] = useState(() => ({
|
|
35
|
+
ipCountry: null,
|
|
36
|
+
languages: [deviceLocale],
|
|
37
|
+
}));
|
|
38
|
+
const phoneRef = useRef(null);
|
|
39
|
+
const { platform, cardFields } = useNativePlatform(onClose, phoneRef);
|
|
40
|
+
const { view } = watched;
|
|
41
|
+
const core = useCheckoutController(view, {
|
|
42
|
+
clues,
|
|
43
|
+
mutations: calls.mutations,
|
|
44
|
+
platform,
|
|
45
|
+
});
|
|
46
|
+
const { ui, derived, actions } = core;
|
|
47
|
+
const { screen } = derived;
|
|
48
|
+
// A payment that could not load is not loading any more.
|
|
49
|
+
const failed = watched.failed && view === undefined;
|
|
50
|
+
const loading = !fontsReady || (screen === CheckoutScreenEnum.LOADING && !failed);
|
|
51
|
+
const terminal = TERMINAL_KIND[screen];
|
|
52
|
+
useSheetResize([
|
|
53
|
+
loading,
|
|
54
|
+
failed,
|
|
55
|
+
screen,
|
|
56
|
+
ui.tab,
|
|
57
|
+
ui.country,
|
|
58
|
+
derived.selected?.id,
|
|
59
|
+
core.cardHeight,
|
|
60
|
+
Boolean(core.phone.status.error || derived.mismatch),
|
|
61
|
+
].join('|'));
|
|
62
|
+
let body;
|
|
63
|
+
if (loading) {
|
|
64
|
+
body = (_jsx(SheetLayout, { secure: false, children: _jsx(SheetSkeleton, {}) }));
|
|
65
|
+
}
|
|
66
|
+
else if (failed) {
|
|
67
|
+
body = (_jsx(SheetLayout, { secure: false, children: _jsx(SheetMessage, { title: t('error.title'), body: t('error.body') }) }));
|
|
68
|
+
}
|
|
69
|
+
else if (screen === CheckoutScreenEnum.INVALID) {
|
|
70
|
+
body = (_jsx(SheetLayout, { secure: false, children: _jsx(SheetMessage, { title: t('invalid.title'), body: t('invalid.body') }) }));
|
|
71
|
+
}
|
|
72
|
+
else if (terminal) {
|
|
73
|
+
body = _jsx(SheetTerminal, { core: core, kind: terminal, onClose: onClose });
|
|
74
|
+
}
|
|
75
|
+
else if (screen === CheckoutScreenEnum.WAITING) {
|
|
76
|
+
body = _jsx(SheetWaiting, { core: core });
|
|
77
|
+
}
|
|
78
|
+
else if (screen === CheckoutScreenEnum.FAILED) {
|
|
79
|
+
body = _jsx(SheetFailed, { core: core });
|
|
80
|
+
}
|
|
81
|
+
else if (screen === CheckoutScreenEnum.SUCCESS ||
|
|
82
|
+
// Done counted down: the sheet slides away on its receipt.
|
|
83
|
+
screen === CheckoutScreenEnum.REDIRECT) {
|
|
84
|
+
body = _jsx(SheetSuccess, { core: core, onClose: onClose });
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const { canPay } = derived;
|
|
88
|
+
const busy = ui.submitting || ui.cardPaying;
|
|
89
|
+
body = (_jsxs(SheetLayout, { foot: _jsx(SheetButton, { label: busy
|
|
90
|
+
? t('sending')
|
|
91
|
+
: t('pay_plain', {
|
|
92
|
+
amount: formatAmount(view.amount, view.currency),
|
|
93
|
+
currency: CURRENCY_LABEL[view.currency],
|
|
94
|
+
}), busy: busy, inactive: !canPay, onPress: actions.pay }), children: [_jsx(SheetSummary, { view: view }), _jsx(SheetForm, { core: core, phoneRef: phoneRef, cardFields: cardFields })] }));
|
|
95
|
+
}
|
|
96
|
+
return (_jsxs(View, { testID: "payment-sheet", accessibilityViewIsModal: true, onAccessibilityEscape: onClose, style: [styles.sheet, { backgroundColor: colors.surface }], children: [_jsx(SheetHeader, { view: view, loading: loading, onClose: onClose, dragHandlers: dragHandlers }), body, _jsx(SheetToast, { message: core.toastMessage })] }));
|
|
97
|
+
}
|
|
98
|
+
const styles = StyleSheet.create({
|
|
99
|
+
// As tall as its screen; past the room it has, the screen scrolls.
|
|
100
|
+
sheet: {
|
|
101
|
+
flexShrink: 1,
|
|
102
|
+
borderTopLeftRadius: 14,
|
|
103
|
+
borderTopRightRadius: 14,
|
|
104
|
+
overflow: 'hidden',
|
|
105
|
+
},
|
|
106
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { StyleSheet, View } from 'react-native';
|
|
3
|
+
import { useTranslations } from 'use-intl';
|
|
4
|
+
import { CheckoutButtonVariantEnum, MethodFlowEnum, } from "../checkout-core/models/checkout.enum.js";
|
|
5
|
+
import { FailureReasonEnum } from "../checkout-core/models/domain.enum.js";
|
|
6
|
+
import { formatMoney } from "../checkout-core/models/checkout.money.utils.js";
|
|
7
|
+
import { logoOf } from "../checkout-core/models/checkout.view.utils.js";
|
|
8
|
+
import { useSheetTheme } from "../hooks/use-sheet-theme.js";
|
|
9
|
+
import { FigtreeEnum, SheetIconEnum } from "../models/sheet.enum.js";
|
|
10
|
+
import { SheetButton } from "./SheetButton.js";
|
|
11
|
+
import { SheetIcon } from "./SheetIcon.js";
|
|
12
|
+
import { SheetLayout } from "./SheetLayout.js";
|
|
13
|
+
import { SheetLogo } from "./SheetLogo.js";
|
|
14
|
+
import { SheetRow } from "./SheetRow.js";
|
|
15
|
+
import { SheetText } from "./SheetText.js";
|
|
16
|
+
/** The last attempt failed: why, in plain words, Try again and another method. */
|
|
17
|
+
export function SheetFailed({ core }) {
|
|
18
|
+
const t = useTranslations('checkout');
|
|
19
|
+
const { colors } = useSheetTheme();
|
|
20
|
+
const { view, ui, derived, actions } = core;
|
|
21
|
+
const attempt = view.attempt;
|
|
22
|
+
// A failure only ever comes with its reason.
|
|
23
|
+
const reason = attempt.failureReason;
|
|
24
|
+
const values = { method: attempt.methodName };
|
|
25
|
+
const { suggestion } = derived;
|
|
26
|
+
return (_jsx(SheetLayout, { foot: _jsxs(_Fragment, { children: [_jsx(SheetButton, { label: ui.submitting
|
|
27
|
+
? t('sending')
|
|
28
|
+
: attempt.flow === MethodFlowEnum.OTP
|
|
29
|
+
? t('retry_code')
|
|
30
|
+
: t('retry'), busy: ui.submitting, onPress: actions.retry }), _jsx(SheetButton, { variant: CheckoutButtonVariantEnum.SECONDARY, label: suggestion
|
|
31
|
+
? t('try_with', { method: suggestion.name })
|
|
32
|
+
: t('use_another'), onPress: actions.useAnother })] }), children: _jsxs(View, { style: styles.failed, children: [_jsx(SheetIcon, { kind: SheetIconEnum.CROSS, color: colors.danger, background: colors.dangerSoft }), _jsx(SheetText, { weight: FigtreeEnum.BOLD, size: 22, accessibilityRole: "header", style: styles.title, children: t(`failure.${reason}.title`, values) }), _jsx(SheetText, { size: 15, color: colors.inkStrong, children: t(`failure.${reason}.body`, values) }), _jsx(SheetText, { color: colors.ink2, style: styles.next, children: t(`failure.${reason}.next`, values) }), _jsxs(View, { style: [styles.rows, { borderColor: colors.line }], children: [_jsx(SheetRow, { label: t('amount'), children: formatMoney(view.amount, view.currency) }), _jsxs(SheetRow, { label: t('method'), children: [_jsx(SheetLogo, { logo: logoOf(attempt) }), _jsx(SheetText, { weight: FigtreeEnum.SEMI_BOLD, children: attempt.methodName })] }), _jsx(SheetRow, { label: t('tries_left'), children: t('tries_value', {
|
|
33
|
+
left: view.attemptsLeft,
|
|
34
|
+
max: view.maxAttempts,
|
|
35
|
+
}) })] }), reason !== FailureReasonEnum.MISMATCH && (_jsx(SheetText, { size: 13, color: colors.muted, style: styles.note, children: t('not_charged') }))] }) }));
|
|
36
|
+
}
|
|
37
|
+
const styles = StyleSheet.create({
|
|
38
|
+
failed: { paddingHorizontal: 18, paddingTop: 28 },
|
|
39
|
+
title: { marginTop: 16, marginBottom: 8, letterSpacing: -0.4 },
|
|
40
|
+
next: { marginTop: 8 },
|
|
41
|
+
rows: { borderTopWidth: 1, marginTop: 20 },
|
|
42
|
+
note: { marginTop: 16, textAlign: 'center' },
|
|
43
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SheetFormProps } from '../models/sheet.interface.js';
|
|
2
|
+
/**
|
|
3
|
+
* The form of design A: the country when there are several, the Mobile
|
|
4
|
+
* Money / Card tabs, the methods two by two, the number, the payment code
|
|
5
|
+
* of an OTP method, and a card's fields.
|
|
6
|
+
*/
|
|
7
|
+
export declare function SheetForm({ core, phoneRef, cardFields }: SheetFormProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Pressable, StyleSheet, View } from 'react-native';
|
|
3
|
+
import { useTranslations } from 'use-intl';
|
|
4
|
+
import { CheckoutTabEnum, MethodFlowEnum, MethodStateEnum, } from "../checkout-core/models/checkout.enum.js";
|
|
5
|
+
import { logoOf } from "../checkout-core/models/checkout.view.utils.js";
|
|
6
|
+
import { useSheetTheme } from "../hooks/use-sheet-theme.js";
|
|
7
|
+
import { FigtreeEnum } from "../models/sheet.enum.js";
|
|
8
|
+
import { flagOf } from "../models/sheet.utils.js";
|
|
9
|
+
import { SheetCardFields } from "./SheetCardFields.js";
|
|
10
|
+
import { SheetCodeField } from "./SheetCodeField.js";
|
|
11
|
+
import { SheetMethodCard } from "./SheetMethodCard.js";
|
|
12
|
+
import { SheetPhoneField } from "./SheetPhoneField.js";
|
|
13
|
+
import { SheetText } from "./SheetText.js";
|
|
14
|
+
/** Two cards a row, the last one alone when they are odd. */
|
|
15
|
+
function MethodGrid({ cards }) {
|
|
16
|
+
const rows = [];
|
|
17
|
+
for (let index = 0; index < cards.length; index += 2) {
|
|
18
|
+
rows.push(cards.slice(index, index + 2));
|
|
19
|
+
}
|
|
20
|
+
return (_jsx(View, { accessibilityRole: "radiogroup", style: styles.grid, children: rows.map((row) => (_jsxs(View, { style: styles.gridRow, children: [row.map(({ id, ...card }) => (_jsx(SheetMethodCard, { ...card }, id))), row.length === 1 && _jsx(View, { style: styles.gridSpacer })] }, row[0].id))) }));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The form of design A: the country when there are several, the Mobile
|
|
24
|
+
* Money / Card tabs, the methods two by two, the number, the payment code
|
|
25
|
+
* of an OTP method, and a card's fields.
|
|
26
|
+
*/
|
|
27
|
+
export function SheetForm({ core, phoneRef, cardFields }) {
|
|
28
|
+
const t = useTranslations('checkout');
|
|
29
|
+
const { colors } = useSheetTheme();
|
|
30
|
+
const { ui, derived, actions } = core;
|
|
31
|
+
const onCard = ui.tab === CheckoutTabEnum.CARD;
|
|
32
|
+
const subOf = (method) => method.state === MethodStateEnum.SOON
|
|
33
|
+
? t('soon')
|
|
34
|
+
: method.state === MethodStateEnum.MAINTENANCE
|
|
35
|
+
? (method.note ?? t('unavailable'))
|
|
36
|
+
: method.network;
|
|
37
|
+
const cardOf = (method) => ({
|
|
38
|
+
id: method.id,
|
|
39
|
+
logo: logoOf(method),
|
|
40
|
+
name: method.name,
|
|
41
|
+
sub: subOf(method),
|
|
42
|
+
checked: derived.selected?.id === method.id,
|
|
43
|
+
muted: method.state !== MethodStateEnum.AVAILABLE,
|
|
44
|
+
onSelect: () => actions.selectMethod(method),
|
|
45
|
+
});
|
|
46
|
+
// Wallets still to come stay off the sheet: only what can be paid with today.
|
|
47
|
+
const cards = onCard ? derived.cards.map(cardOf) : derived.wallets.map(cardOf);
|
|
48
|
+
const tab = (value, label) => {
|
|
49
|
+
const on = ui.tab === value;
|
|
50
|
+
return (_jsx(Pressable, { onPress: () => actions.setTab(value), accessibilityRole: "tab", accessibilityState: { selected: on }, style: [
|
|
51
|
+
styles.tab,
|
|
52
|
+
on && [styles.tabOn, { backgroundColor: colors.tabOn }],
|
|
53
|
+
], children: _jsx(SheetText, { weight: FigtreeEnum.SEMI_BOLD, size: 13.5, color: on ? colors.ink : colors.muted, children: label }) }));
|
|
54
|
+
};
|
|
55
|
+
return (_jsxs(View, { style: styles.form, children: [!onCard && derived.countries.length > 1 && (_jsx(View, { accessibilityRole: "radiogroup", accessibilityLabel: t('country'), style: styles.countries, children: derived.countries.map((country) => {
|
|
56
|
+
const on = country === ui.country;
|
|
57
|
+
return (_jsxs(Pressable, { onPress: () => actions.setCountry(country), accessibilityRole: "radio", accessibilityState: { checked: on }, style: [
|
|
58
|
+
styles.country,
|
|
59
|
+
on
|
|
60
|
+
? { backgroundColor: colors.ink, borderColor: colors.ink }
|
|
61
|
+
: { borderColor: colors.line3 },
|
|
62
|
+
], children: [_jsx(SheetText, { size: 14, children: flagOf(country) }), _jsx(SheetText, { weight: FigtreeEnum.SEMI_BOLD, size: 13, color: on ? colors.surface : colors.ink, children: t(`countries.${country}`) })] }, country));
|
|
63
|
+
}) })), _jsxs(View, { accessibilityRole: "tablist", style: [styles.tabs, { backgroundColor: colors.fill }], children: [tab(CheckoutTabEnum.MOBILE_MONEY, t('tab_wallet')), tab(CheckoutTabEnum.CARD, t('card'))] }), _jsx(MethodGrid, { cards: cards }), !onCard && _jsx(SheetPhoneField, { core: core, phoneRef: phoneRef }), !onCard && derived.selected?.flow === MethodFlowEnum.OTP && (_jsx(SheetCodeField, { core: core })), onCard && derived.selected?.flow === MethodFlowEnum.CARD_FIELDS && (_jsx(SheetCardFields, { core: core, bridge: cardFields }))] }));
|
|
64
|
+
}
|
|
65
|
+
const styles = StyleSheet.create({
|
|
66
|
+
form: { paddingHorizontal: 18, paddingTop: 14, paddingBottom: 8 },
|
|
67
|
+
countries: {
|
|
68
|
+
flexDirection: 'row',
|
|
69
|
+
flexWrap: 'wrap',
|
|
70
|
+
gap: 8,
|
|
71
|
+
marginBottom: 12,
|
|
72
|
+
},
|
|
73
|
+
country: {
|
|
74
|
+
flexDirection: 'row',
|
|
75
|
+
alignItems: 'center',
|
|
76
|
+
gap: 6,
|
|
77
|
+
borderWidth: 1,
|
|
78
|
+
borderRadius: 16,
|
|
79
|
+
paddingHorizontal: 12,
|
|
80
|
+
paddingVertical: 6,
|
|
81
|
+
},
|
|
82
|
+
tabs: { flexDirection: 'row', borderRadius: 12, padding: 3 },
|
|
83
|
+
tab: {
|
|
84
|
+
flex: 1,
|
|
85
|
+
alignItems: 'center',
|
|
86
|
+
justifyContent: 'center',
|
|
87
|
+
minHeight: 36,
|
|
88
|
+
borderRadius: 9,
|
|
89
|
+
},
|
|
90
|
+
tabOn: {
|
|
91
|
+
shadowColor: 'black',
|
|
92
|
+
shadowOpacity: 0.12,
|
|
93
|
+
shadowRadius: 2,
|
|
94
|
+
shadowOffset: { width: 0, height: 1 },
|
|
95
|
+
elevation: 1,
|
|
96
|
+
},
|
|
97
|
+
grid: { gap: 8, marginTop: 12 },
|
|
98
|
+
gridRow: { flexDirection: 'row', gap: 8 },
|
|
99
|
+
gridSpacer: { flex: 1 },
|
|
100
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SheetHeaderProps } from '../models/sheet.interface.js';
|
|
2
|
+
/**
|
|
3
|
+
* The grabber, then the app being paid: its logo and name, the Test badge,
|
|
4
|
+
* and close. Dragged down, the sheet follows.
|
|
5
|
+
*/
|
|
6
|
+
export declare function SheetHeader({ view, loading, onClose, dragHandlers, }: SheetHeaderProps): import("react").JSX.Element;
|