@heyama/pay-react-native 0.0.0-stage → 1.0.1
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 +253 -0
- package/dist/checkout-core/messages/en.js +243 -0
- package/dist/checkout-core/messages/fr.d.ts +4 -0
- package/dist/checkout-core/messages/fr.js +245 -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 +133 -0
- package/dist/checkout-core/models/checkout.enum.js +150 -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 +77 -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/SheetChosenMethod.d.ts +3 -0
- package/dist/components/SheetChosenMethod.js +60 -0
- package/dist/components/SheetCodeField.d.ts +8 -0
- package/dist/components/SheetCodeField.js +97 -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 +8 -0
- package/dist/components/SheetForm.js +101 -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 +9 -0
- package/dist/components/SheetLayout.js +48 -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 +32 -0
- package/dist/components/SheetPhoneField.d.ts +3 -0
- package/dist/components/SheetPhoneField.js +58 -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 +232 -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,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,97 @@
|
|
|
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",
|
|
51
|
+
// iOS's number keypads have no Go key: asking for one adds a bar above them.
|
|
52
|
+
returnKeyType: Platform.OS === 'android' ? 'go' : undefined, 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: [
|
|
53
|
+
styles.dial,
|
|
54
|
+
{ borderColor: colors.line3, backgroundColor: colors.surface },
|
|
55
|
+
], 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 })] })] })] }));
|
|
56
|
+
}
|
|
57
|
+
const styles = StyleSheet.create({
|
|
58
|
+
section: { marginTop: 10 },
|
|
59
|
+
label: { marginBottom: 8 },
|
|
60
|
+
input: {
|
|
61
|
+
height: 50,
|
|
62
|
+
borderWidth: 1,
|
|
63
|
+
borderRadius: 13,
|
|
64
|
+
paddingHorizontal: 14,
|
|
65
|
+
fontSize: 18,
|
|
66
|
+
letterSpacing: 5,
|
|
67
|
+
},
|
|
68
|
+
help: {
|
|
69
|
+
flexDirection: 'row',
|
|
70
|
+
alignItems: 'center',
|
|
71
|
+
gap: 12,
|
|
72
|
+
borderRadius: 12,
|
|
73
|
+
paddingHorizontal: 14,
|
|
74
|
+
paddingVertical: 12,
|
|
75
|
+
marginTop: 12,
|
|
76
|
+
},
|
|
77
|
+
grow: { flex: 1 },
|
|
78
|
+
dial: {
|
|
79
|
+
flexDirection: 'row',
|
|
80
|
+
alignItems: 'center',
|
|
81
|
+
gap: 6,
|
|
82
|
+
borderWidth: 1,
|
|
83
|
+
borderRadius: 9,
|
|
84
|
+
paddingHorizontal: 12,
|
|
85
|
+
minHeight: 40,
|
|
86
|
+
},
|
|
87
|
+
phone: {
|
|
88
|
+
width: 10,
|
|
89
|
+
height: 16,
|
|
90
|
+
borderWidth: 1.6,
|
|
91
|
+
borderRadius: 3,
|
|
92
|
+
alignItems: 'center',
|
|
93
|
+
justifyContent: 'flex-end',
|
|
94
|
+
paddingBottom: 1.5,
|
|
95
|
+
},
|
|
96
|
+
speaker: { width: 3, height: 1.4, borderRadius: 0.7 },
|
|
97
|
+
});
|
|
@@ -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,8 @@
|
|
|
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 one picked on its own row),
|
|
5
|
+
* the number, the payment code
|
|
6
|
+
* of an OTP method, and a card's fields.
|
|
7
|
+
*/
|
|
8
|
+
export declare function SheetForm({ core, phoneRef, cardFields }: SheetFormProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,101 @@
|
|
|
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 { SheetChosenMethod } from "./SheetChosenMethod.js";
|
|
11
|
+
import { SheetCodeField } from "./SheetCodeField.js";
|
|
12
|
+
import { SheetMethodCard } from "./SheetMethodCard.js";
|
|
13
|
+
import { SheetPhoneField } from "./SheetPhoneField.js";
|
|
14
|
+
import { SheetText } from "./SheetText.js";
|
|
15
|
+
/** Two cards a row, the last one alone when they are odd. */
|
|
16
|
+
function MethodGrid({ cards }) {
|
|
17
|
+
const rows = [];
|
|
18
|
+
for (let index = 0; index < cards.length; index += 2) {
|
|
19
|
+
rows.push(cards.slice(index, index + 2));
|
|
20
|
+
}
|
|
21
|
+
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))) }));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The form of design A: the country when there are several, the Mobile
|
|
25
|
+
* Money / Card tabs, the methods two by two (the one picked on its own row),
|
|
26
|
+
* the number, the payment code
|
|
27
|
+
* of an OTP method, and a card's fields.
|
|
28
|
+
*/
|
|
29
|
+
export function SheetForm({ core, phoneRef, cardFields }) {
|
|
30
|
+
const t = useTranslations('checkout');
|
|
31
|
+
const { colors } = useSheetTheme();
|
|
32
|
+
const { ui, derived, actions } = core;
|
|
33
|
+
const onCard = ui.tab === CheckoutTabEnum.CARD;
|
|
34
|
+
const subOf = (method) => method.state === MethodStateEnum.SOON
|
|
35
|
+
? t('soon')
|
|
36
|
+
: method.state === MethodStateEnum.MAINTENANCE
|
|
37
|
+
? (method.note ?? t('unavailable'))
|
|
38
|
+
: method.network;
|
|
39
|
+
const cardOf = (method) => ({
|
|
40
|
+
id: method.id,
|
|
41
|
+
logo: logoOf(method),
|
|
42
|
+
name: method.name,
|
|
43
|
+
sub: subOf(method),
|
|
44
|
+
muted: method.state !== MethodStateEnum.AVAILABLE,
|
|
45
|
+
onSelect: () => actions.selectMethod(method),
|
|
46
|
+
});
|
|
47
|
+
// Wallets still to come stay off the sheet: only what can be paid with today.
|
|
48
|
+
const cards = onCard ? derived.cards.map(cardOf) : derived.wallets.map(cardOf);
|
|
49
|
+
const tab = (value, label) => {
|
|
50
|
+
const on = ui.tab === value;
|
|
51
|
+
return (_jsx(Pressable, { onPress: () => actions.setTab(value), accessibilityRole: "tab", accessibilityState: { selected: on }, style: [
|
|
52
|
+
styles.tab,
|
|
53
|
+
on && [styles.tabOn, { backgroundColor: colors.tabOn }],
|
|
54
|
+
], children: _jsx(SheetText, { weight: FigtreeEnum.SEMI_BOLD, size: 13.5, color: on ? colors.ink : colors.muted, children: label }) }));
|
|
55
|
+
};
|
|
56
|
+
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) => {
|
|
57
|
+
const on = country === ui.country;
|
|
58
|
+
return (_jsxs(Pressable, { onPress: () => actions.setCountry(country), accessibilityRole: "radio", accessibilityState: { checked: on }, style: [
|
|
59
|
+
styles.country,
|
|
60
|
+
on
|
|
61
|
+
? { backgroundColor: colors.ink, borderColor: colors.ink }
|
|
62
|
+
: { borderColor: colors.line3 },
|
|
63
|
+
], 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));
|
|
64
|
+
}) })), _jsxs(View, { accessibilityRole: "tablist", style: [styles.tabs, { backgroundColor: colors.fill }], children: [tab(CheckoutTabEnum.MOBILE_MONEY, t('tab_wallet')), tab(CheckoutTabEnum.CARD, t('card'))] }), derived.selected ? (_jsx(SheetChosenMethod, { logo: logoOf(derived.selected), name: derived.selected.name, sub: derived.selected.network, onChange: actions.openPicker })) : (_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 }))] }));
|
|
65
|
+
}
|
|
66
|
+
const styles = StyleSheet.create({
|
|
67
|
+
form: { paddingHorizontal: 18, paddingTop: 14, paddingBottom: 8 },
|
|
68
|
+
countries: {
|
|
69
|
+
flexDirection: 'row',
|
|
70
|
+
flexWrap: 'wrap',
|
|
71
|
+
gap: 8,
|
|
72
|
+
marginBottom: 12,
|
|
73
|
+
},
|
|
74
|
+
country: {
|
|
75
|
+
flexDirection: 'row',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
gap: 6,
|
|
78
|
+
borderWidth: 1,
|
|
79
|
+
borderRadius: 16,
|
|
80
|
+
paddingHorizontal: 12,
|
|
81
|
+
paddingVertical: 6,
|
|
82
|
+
},
|
|
83
|
+
tabs: { flexDirection: 'row', borderRadius: 12, padding: 3 },
|
|
84
|
+
tab: {
|
|
85
|
+
flex: 1,
|
|
86
|
+
alignItems: 'center',
|
|
87
|
+
justifyContent: 'center',
|
|
88
|
+
minHeight: 36,
|
|
89
|
+
borderRadius: 9,
|
|
90
|
+
},
|
|
91
|
+
tabOn: {
|
|
92
|
+
shadowColor: 'black',
|
|
93
|
+
shadowOpacity: 0.12,
|
|
94
|
+
shadowRadius: 2,
|
|
95
|
+
shadowOffset: { width: 0, height: 1 },
|
|
96
|
+
elevation: 1,
|
|
97
|
+
},
|
|
98
|
+
grid: { gap: 8, marginTop: 12 },
|
|
99
|
+
gridRow: { flexDirection: 'row', gap: 8 },
|
|
100
|
+
gridSpacer: { flex: 1 },
|
|
101
|
+
});
|
|
@@ -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;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Image, Pressable, StyleSheet, View } from 'react-native';
|
|
3
|
+
import { useTranslations } from 'use-intl';
|
|
4
|
+
import { MEDIA_LINE } from "../checkout-core/models/checkout.constants.js";
|
|
5
|
+
import { useSheetTheme } from "../hooks/use-sheet-theme.js";
|
|
6
|
+
import { FigtreeEnum } from "../models/sheet.enum.js";
|
|
7
|
+
import { SheetBone } from "./SheetBone.js";
|
|
8
|
+
import { SheetText } from "./SheetText.js";
|
|
9
|
+
/**
|
|
10
|
+
* The grabber, then the app being paid: its logo and name, the Test badge,
|
|
11
|
+
* and close. Dragged down, the sheet follows.
|
|
12
|
+
*/
|
|
13
|
+
export function SheetHeader({ view, loading, onClose, dragHandlers, }) {
|
|
14
|
+
const t = useTranslations('checkout');
|
|
15
|
+
const { colors, brand } = useSheetTheme();
|
|
16
|
+
const app = view?.app;
|
|
17
|
+
return (_jsxs(View, { ...dragHandlers, children: [_jsx(View, { style: [styles.grabber, { backgroundColor: colors.line3 }] }), _jsxs(View, { style: styles.row, children: [loading ? (_jsxs(_Fragment, { children: [_jsx(SheetBone, { width: 34, height: 34, radius: 9 }), _jsx(View, { style: styles.name, children: _jsx(SheetBone, { width: 84, height: 14 }) })] })) : (app &&
|
|
18
|
+
(app.logoUrl ? (_jsx(Image, { source: { uri: app.logoUrl }, accessibilityIgnoresInvertColors: true, style: styles.logo })) : (_jsx(View, { testID: "app-initial", style: [styles.initial, { backgroundColor: brand }], children: _jsx(SheetText, { weight: FigtreeEnum.EXTRA_BOLD, size: 15, color: "white", style: styles.centred, children: app.name.charAt(0) }) })))), !loading && (_jsx(SheetText, { weight: FigtreeEnum.BOLD, size: 16, numberOfLines: 1, accessibilityRole: "header", style: [styles.name, styles.centred], children: app?.name ?? 'Brissoto Pay' })), !loading && view?.test && (_jsx(View, { style: [styles.badge, { backgroundColor: colors.warnSoft }], children: _jsx(SheetText, { weight: FigtreeEnum.EXTRA_BOLD, size: 10.5, color: colors.warnInk, style: [styles.badgeText, styles.centred], children: t('test_badge') }) })), _jsxs(Pressable, { onPress: onClose, accessibilityRole: "button", accessibilityLabel: t('close'), hitSlop: 8, style: [styles.close, { backgroundColor: colors.fill }], children: [_jsx(View, { style: [styles.bar, styles.barUp, { backgroundColor: colors.ink2 }] }), _jsx(View, { style: [
|
|
19
|
+
styles.bar,
|
|
20
|
+
styles.barDown,
|
|
21
|
+
{ backgroundColor: colors.ink2 },
|
|
22
|
+
] })] })] })] }));
|
|
23
|
+
}
|
|
24
|
+
const styles = StyleSheet.create({
|
|
25
|
+
grabber: {
|
|
26
|
+
alignSelf: 'center',
|
|
27
|
+
width: 38,
|
|
28
|
+
height: 5,
|
|
29
|
+
borderRadius: 3,
|
|
30
|
+
marginTop: 7,
|
|
31
|
+
},
|
|
32
|
+
row: {
|
|
33
|
+
flexDirection: 'row',
|
|
34
|
+
alignItems: 'center',
|
|
35
|
+
gap: 10,
|
|
36
|
+
paddingHorizontal: 18,
|
|
37
|
+
paddingTop: 10,
|
|
38
|
+
},
|
|
39
|
+
logo: {
|
|
40
|
+
width: 34,
|
|
41
|
+
height: 34,
|
|
42
|
+
borderRadius: 9,
|
|
43
|
+
borderWidth: 1,
|
|
44
|
+
borderColor: MEDIA_LINE,
|
|
45
|
+
},
|
|
46
|
+
initial: {
|
|
47
|
+
width: 34,
|
|
48
|
+
height: 34,
|
|
49
|
+
borderRadius: 9,
|
|
50
|
+
alignItems: 'center',
|
|
51
|
+
justifyContent: 'center',
|
|
52
|
+
},
|
|
53
|
+
name: { flex: 1 },
|
|
54
|
+
// Android adds font padding above the ascender: without it, each text sits
|
|
55
|
+
// on the row's centre line like the logo, the badge and close.
|
|
56
|
+
centred: { includeFontPadding: false, textAlignVertical: 'center' },
|
|
57
|
+
badge: { borderRadius: 6, paddingHorizontal: 7, paddingVertical: 3 },
|
|
58
|
+
badgeText: { letterSpacing: 0.6, textTransform: 'uppercase' },
|
|
59
|
+
close: {
|
|
60
|
+
width: 30,
|
|
61
|
+
height: 30,
|
|
62
|
+
borderRadius: 15,
|
|
63
|
+
alignItems: 'center',
|
|
64
|
+
justifyContent: 'center',
|
|
65
|
+
},
|
|
66
|
+
bar: { position: 'absolute', width: 14, height: 2, borderRadius: 1 },
|
|
67
|
+
barUp: { transform: [{ rotate: '45deg' }] },
|
|
68
|
+
barDown: { transform: [{ rotate: '-45deg' }] },
|
|
69
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { StyleSheet, View } from 'react-native';
|
|
3
|
+
import { SheetIconEnum } from "../models/sheet.enum.js";
|
|
4
|
+
/** A sign in a disc, drawn with views: the sheet ships no icon font or SVG. */
|
|
5
|
+
export function SheetIcon({ kind, color, background, size = 48, }) {
|
|
6
|
+
const scale = size / 48;
|
|
7
|
+
const ink = { backgroundColor: color };
|
|
8
|
+
const line = { borderColor: color };
|
|
9
|
+
let sign;
|
|
10
|
+
switch (kind) {
|
|
11
|
+
case SheetIconEnum.CHECK:
|
|
12
|
+
sign = _jsx(View, { style: [styles.check, line] });
|
|
13
|
+
break;
|
|
14
|
+
case SheetIconEnum.CROSS:
|
|
15
|
+
sign = (_jsxs(_Fragment, { children: [_jsx(View, { style: [styles.bar, styles.up, ink] }), _jsx(View, { style: [styles.bar, styles.down, ink] })] }));
|
|
16
|
+
break;
|
|
17
|
+
case SheetIconEnum.CLOCK:
|
|
18
|
+
sign = (_jsxs(View, { style: [styles.ring, line], children: [_jsx(View, { style: [styles.hour, ink] }), _jsx(View, { style: [styles.minute, ink] })] }));
|
|
19
|
+
break;
|
|
20
|
+
case SheetIconEnum.LOCK:
|
|
21
|
+
sign = (_jsxs(View, { style: styles.lock, children: [_jsx(View, { style: [styles.shackle, line] }), _jsx(View, { style: [styles.body, ink] })] }));
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
sign = (_jsx(View, { style: [styles.ring, line], children: _jsx(View, { style: [styles.slash, ink] }) }));
|
|
25
|
+
}
|
|
26
|
+
return (_jsx(View, { testID: `icon-${kind}`, style: [
|
|
27
|
+
styles.disc,
|
|
28
|
+
{
|
|
29
|
+
width: size,
|
|
30
|
+
height: size,
|
|
31
|
+
borderRadius: size / 2,
|
|
32
|
+
backgroundColor: background,
|
|
33
|
+
},
|
|
34
|
+
], children: _jsx(View, { style: { transform: [{ scale }] }, children: sign }) }));
|
|
35
|
+
}
|
|
36
|
+
const styles = StyleSheet.create({
|
|
37
|
+
disc: { alignItems: 'center', justifyContent: 'center' },
|
|
38
|
+
check: {
|
|
39
|
+
width: 10,
|
|
40
|
+
height: 18,
|
|
41
|
+
marginTop: -4,
|
|
42
|
+
borderRightWidth: 3,
|
|
43
|
+
borderBottomWidth: 3,
|
|
44
|
+
transform: [{ rotate: '45deg' }],
|
|
45
|
+
},
|
|
46
|
+
bar: { position: 'absolute', width: 18, height: 2.6, borderRadius: 1.3 },
|
|
47
|
+
up: { transform: [{ rotate: '45deg' }] },
|
|
48
|
+
down: { transform: [{ rotate: '-45deg' }] },
|
|
49
|
+
ring: {
|
|
50
|
+
width: 20,
|
|
51
|
+
height: 20,
|
|
52
|
+
borderRadius: 10,
|
|
53
|
+
borderWidth: 2,
|
|
54
|
+
alignItems: 'center',
|
|
55
|
+
justifyContent: 'center',
|
|
56
|
+
},
|
|
57
|
+
slash: { width: 18, height: 2, transform: [{ rotate: '-45deg' }] },
|
|
58
|
+
hour: { position: 'absolute', width: 2, height: 6, top: 3, left: 7 },
|
|
59
|
+
minute: { position: 'absolute', width: 5, height: 2, top: 7, left: 7 },
|
|
60
|
+
lock: { alignItems: 'center' },
|
|
61
|
+
shackle: {
|
|
62
|
+
width: 12,
|
|
63
|
+
height: 10,
|
|
64
|
+
borderWidth: 2.4,
|
|
65
|
+
borderBottomWidth: 0,
|
|
66
|
+
borderTopLeftRadius: 6,
|
|
67
|
+
borderTopRightRadius: 6,
|
|
68
|
+
},
|
|
69
|
+
body: { width: 18, height: 12, borderRadius: 3 },
|
|
70
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SheetLayoutProps } from '../models/sheet.interface.js';
|
|
2
|
+
/**
|
|
3
|
+
* What scrolls, then "Payment secured by Brissoto Pay" over the actions
|
|
4
|
+
* pinned at the bottom, so Pay sits right on the keyboard. What scrolls is
|
|
5
|
+
* as tall as it is, until the sheet runs out of room. The keyboard pushes
|
|
6
|
+
* the actions up, without the room kept for the home bar: what scrolls
|
|
7
|
+
* follows to its end, where the number is.
|
|
8
|
+
*/
|
|
9
|
+
export declare function SheetLayout({ children, foot, secure, }: SheetLayoutProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { Keyboard, Platform, ScrollView, StyleSheet, View } from 'react-native';
|
|
4
|
+
import { useTranslations } from 'use-intl';
|
|
5
|
+
import { useSheetTheme } from "../hooks/use-sheet-theme.js";
|
|
6
|
+
import { FigtreeEnum } from "../models/sheet.enum.js";
|
|
7
|
+
import { SheetText } from "./SheetText.js";
|
|
8
|
+
/**
|
|
9
|
+
* What scrolls, then "Payment secured by Brissoto Pay" over the actions
|
|
10
|
+
* pinned at the bottom, so Pay sits right on the keyboard. What scrolls is
|
|
11
|
+
* as tall as it is, until the sheet runs out of room. The keyboard pushes
|
|
12
|
+
* the actions up, without the room kept for the home bar: what scrolls
|
|
13
|
+
* follows to its end, where the number is.
|
|
14
|
+
*/
|
|
15
|
+
export function SheetLayout({ children, foot, secure = true, }) {
|
|
16
|
+
const t = useTranslations('checkout');
|
|
17
|
+
const { colors } = useSheetTheme();
|
|
18
|
+
const scroller = useRef(null);
|
|
19
|
+
const [keyboard, setKeyboard] = useState(false);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
// iOS tells before the keyboard moves, Android only once it has.
|
|
22
|
+
const ios = Platform.OS === 'ios';
|
|
23
|
+
const listeners = [
|
|
24
|
+
Keyboard.addListener('keyboardDidShow', () => scroller.current?.scrollToEnd()),
|
|
25
|
+
Keyboard.addListener(ios ? 'keyboardWillShow' : 'keyboardDidShow', () => setKeyboard(true)),
|
|
26
|
+
Keyboard.addListener(ios ? 'keyboardWillHide' : 'keyboardDidHide', () => setKeyboard(false)),
|
|
27
|
+
];
|
|
28
|
+
return () => listeners.forEach((listener) => listener.remove());
|
|
29
|
+
}, []);
|
|
30
|
+
return (_jsxs(_Fragment, { children: [_jsx(ScrollView, { ref: scroller, style: styles.body, contentContainerStyle: styles.content, keyboardShouldPersistTaps: "handled", keyboardDismissMode: "interactive", children: children }), (foot || secure) && (_jsxs(View, { testID: "sheet-foot", style: [
|
|
31
|
+
styles.foot,
|
|
32
|
+
keyboard && styles.footOnKeyboard,
|
|
33
|
+
{ borderColor: colors.line },
|
|
34
|
+
], children: [secure && (_jsxs(SheetText, { size: 11.5, color: colors.muted, style: styles.secure, children: [t('secured_by'), ' ', _jsx(SheetText, { weight: FigtreeEnum.BOLD, size: 11.5, children: "Brissoto Pay" })] })), foot] }))] }));
|
|
35
|
+
}
|
|
36
|
+
const styles = StyleSheet.create({
|
|
37
|
+
body: { flexGrow: 0, flexShrink: 1, marginTop: 6 },
|
|
38
|
+
content: { paddingBottom: 16 },
|
|
39
|
+
foot: {
|
|
40
|
+
borderTopWidth: 1,
|
|
41
|
+
paddingHorizontal: 18,
|
|
42
|
+
paddingTop: 12,
|
|
43
|
+
paddingBottom: 28,
|
|
44
|
+
gap: 10,
|
|
45
|
+
},
|
|
46
|
+
footOnKeyboard: { paddingBottom: 12 },
|
|
47
|
+
secure: { textAlign: 'center' },
|
|
48
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Image, StyleSheet } from 'react-native';
|
|
3
|
+
import { LOGO_RADIUS_RATIO, LOGO_SIZE, MEDIA_LINE, } from "../checkout-core/models/checkout.constants.js";
|
|
4
|
+
import { PROVIDER_LOGOS } from "../models/sheet.constants.js";
|
|
5
|
+
/** A brand's square tile, the same as on the web checkout. */
|
|
6
|
+
export function SheetLogo({ logo, size = LOGO_SIZE.INLINE, muted = false, }) {
|
|
7
|
+
return (_jsx(Image, { source: PROVIDER_LOGOS[logo], accessibilityIgnoresInvertColors: true, style: [
|
|
8
|
+
styles.logo,
|
|
9
|
+
{
|
|
10
|
+
width: size,
|
|
11
|
+
height: size,
|
|
12
|
+
borderRadius: Math.round(size * LOGO_RADIUS_RATIO),
|
|
13
|
+
},
|
|
14
|
+
muted && styles.muted,
|
|
15
|
+
] }));
|
|
16
|
+
}
|
|
17
|
+
const styles = StyleSheet.create({
|
|
18
|
+
logo: { borderWidth: 1, borderColor: MEDIA_LINE },
|
|
19
|
+
muted: { opacity: 0.45 },
|
|
20
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { StyleSheet, View } from 'react-native';
|
|
3
|
+
import { useSheetTheme } from "../hooks/use-sheet-theme.js";
|
|
4
|
+
import { FigtreeEnum, SheetIconEnum } from "../models/sheet.enum.js";
|
|
5
|
+
import { SheetIcon } from "./SheetIcon.js";
|
|
6
|
+
import { SheetText } from "./SheetText.js";
|
|
7
|
+
/** A dead end the payer cannot fix here: its sign, what happened and why. */
|
|
8
|
+
export function SheetMessage({ title, body, icon = SheetIconEnum.BAN, danger = false, }) {
|
|
9
|
+
const { colors } = useSheetTheme();
|
|
10
|
+
return (_jsxs(View, { style: styles.message, children: [_jsx(SheetIcon, { kind: icon, color: danger ? colors.danger : colors.ink2, background: danger ? colors.dangerSoft : colors.fill2 }), _jsx(SheetText, { weight: FigtreeEnum.BOLD, size: 22, accessibilityRole: "header", style: styles.title, children: title }), _jsx(SheetText, { size: 15, color: colors.inkStrong, children: body })] }));
|
|
11
|
+
}
|
|
12
|
+
const styles = StyleSheet.create({
|
|
13
|
+
message: { paddingHorizontal: 18, paddingTop: 28 },
|
|
14
|
+
title: { marginTop: 16, marginBottom: 8, letterSpacing: -0.4 },
|
|
15
|
+
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { MethodCardProps } from '../models/sheet.interface.js';
|
|
2
|
+
/** One method of the grid: logo and radio dot, then its name and network, or why it is greyed. Picked, it becomes the chosen row. */
|
|
3
|
+
export declare function SheetMethodCard({ logo, name, sub, muted, onSelect, }: MethodCardProps): import("react").JSX.Element;
|