@heyama/pay-react-native 1.0.3 → 1.0.4
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/checkout-core/hooks/use-checkout-controller.js +36 -8
- package/dist/checkout-core/models/checkout.interface.d.ts +2 -0
- package/dist/checkout-core/models/checkout.view.utils.d.ts +2 -0
- package/dist/checkout-core/models/checkout.view.utils.js +11 -0
- package/dist/components/SheetForm.js +1 -1
- package/dist/components/SheetMethodCard.d.ts +2 -2
- package/dist/components/SheetMethodCard.js +8 -5
- package/dist/components/SheetToast.js +1 -1
- package/dist/models/sheet.interface.d.ts +2 -0
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ import { CountryEnum } from "../models/domain.enum.js";
|
|
|
6
6
|
import { cardLabel } from "../models/checkout.card.utils.js";
|
|
7
7
|
import { formatMoney } from "../models/checkout.money.utils.js";
|
|
8
8
|
import { maskE164 } from "../models/checkout.phone.utils.js";
|
|
9
|
-
import { countriesOf, deriveCheckout, initialUi, logoOf, screenOf, waitLeftOf, } from "../models/checkout.view.utils.js";
|
|
9
|
+
import { countriesOf, deriveCheckout, initialUi, logoOf, pickedPatch, screenOf, waitLeftOf, } from "../models/checkout.view.utils.js";
|
|
10
10
|
import { useNow, useReturnCountdown, useToast } from "./use-checkout-timers.js";
|
|
11
11
|
import { usePhoneNumber } from "./use-phone-number.js";
|
|
12
12
|
/** Before the payment has loaded, or for a link that matches none. */
|
|
@@ -63,6 +63,7 @@ export function useCheckoutController(view, { clues, mutations, platform }) {
|
|
|
63
63
|
cards: [],
|
|
64
64
|
selected: null,
|
|
65
65
|
canPay: false,
|
|
66
|
+
detected: null,
|
|
66
67
|
mismatch: null,
|
|
67
68
|
suggestion: null,
|
|
68
69
|
};
|
|
@@ -87,6 +88,39 @@ export function useCheckoutController(view, { clues, mutations, platform }) {
|
|
|
87
88
|
update({ dismissed: attemptReference });
|
|
88
89
|
showToast(noAnswer, CheckoutToastToneEnum.WARNING);
|
|
89
90
|
}, [waiting, waitLeft, attemptReference, noAnswer, showToast, update]);
|
|
91
|
+
// A number that tells its method picks it when none is picked: once per
|
|
92
|
+
// number, so Change keeps the list open until the number changes. The app's
|
|
93
|
+
// own number picks without a word. Off the form, or on the Card tab, the
|
|
94
|
+
// number is not looked at: it counts once the payer is back on Mobile Money.
|
|
95
|
+
const detectedMethod = derived.detected;
|
|
96
|
+
const numberE164 = phone.e164;
|
|
97
|
+
const onMobileForm = screen === CheckoutScreenEnum.FORM &&
|
|
98
|
+
ui.tab === CheckoutTabEnum.MOBILE_MONEY;
|
|
99
|
+
const nonePicked = !derived.selected;
|
|
100
|
+
const lastKey = useRef('');
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!onMobileForm)
|
|
103
|
+
return;
|
|
104
|
+
const key = detectedMethod ? `${detectedMethod.id}|${numberE164}` : '';
|
|
105
|
+
if (key === lastKey.current)
|
|
106
|
+
return;
|
|
107
|
+
lastKey.current = key;
|
|
108
|
+
if (!detectedMethod || !nonePicked)
|
|
109
|
+
return;
|
|
110
|
+
update(pickedPatch(detectedMethod.id));
|
|
111
|
+
if (numberE164 !== prefill) {
|
|
112
|
+
showToast(t('toast.switched', { method: detectedMethod.name }), CheckoutToastToneEnum.SUCCESS, logoOf(detectedMethod));
|
|
113
|
+
}
|
|
114
|
+
}, [
|
|
115
|
+
onMobileForm,
|
|
116
|
+
detectedMethod,
|
|
117
|
+
numberE164,
|
|
118
|
+
nonePicked,
|
|
119
|
+
prefill,
|
|
120
|
+
t,
|
|
121
|
+
showToast,
|
|
122
|
+
update,
|
|
123
|
+
]);
|
|
90
124
|
const paymentId = view?.id;
|
|
91
125
|
// A payer opened the link, said once per page (the dashboard's preview has no mutations).
|
|
92
126
|
const opened = useRef(null);
|
|
@@ -192,13 +226,7 @@ export function useCheckoutController(view, { clues, mutations, platform }) {
|
|
|
192
226
|
toast.show(method.note ?? t('toast.unavailable', { method: method.name }), CheckoutToastToneEnum.WARNING, logoOf(method));
|
|
193
227
|
}
|
|
194
228
|
else {
|
|
195
|
-
update(
|
|
196
|
-
method: method.id,
|
|
197
|
-
pickerOpen: false,
|
|
198
|
-
code: '',
|
|
199
|
-
cardComplete: false,
|
|
200
|
-
methodMissing: 0,
|
|
201
|
-
});
|
|
229
|
+
update(pickedPatch(method.id));
|
|
202
230
|
}
|
|
203
231
|
},
|
|
204
232
|
comingSoon,
|
|
@@ -202,6 +202,8 @@ export interface CheckoutDerived {
|
|
|
202
202
|
/** The picked method, while it can still be used. */
|
|
203
203
|
selected: CheckoutMethod | null;
|
|
204
204
|
canPay: boolean;
|
|
205
|
+
/** The available method of the country the typed number belongs to. */
|
|
206
|
+
detected: CheckoutMethod | null;
|
|
205
207
|
/** The number belongs to another method of the country than the one picked. */
|
|
206
208
|
mismatch: CheckoutMethod | null;
|
|
207
209
|
/** After a failure: another method of the same country the payer can use now. */
|
|
@@ -22,6 +22,8 @@ export declare function initialUi(view: CheckoutView, clues: CountryClues): Chec
|
|
|
22
22
|
export declare function screenOf(view: CheckoutView | null | undefined, ui: CheckoutUi): CheckoutScreenEnum;
|
|
23
23
|
/** The brand of a Mobile Money method, which a number's first digits can reveal. */
|
|
24
24
|
export declare function brandOf(method: CheckoutMethod): string;
|
|
25
|
+
/** What the page keeps when a method is picked: the list closes, the rest starts over. */
|
|
26
|
+
export declare function pickedPatch(id: string): Partial<CheckoutUi>;
|
|
25
27
|
/** The tile of a method or an attempt: the generic card for cards, whatever networks they take. */
|
|
26
28
|
export declare function logoOf(of: Pick<CheckoutMethod, 'flow' | 'logos'>): ProviderLogoEnum;
|
|
27
29
|
/** Works out what the screens show from the payment, the page and the number typed. */
|
|
@@ -99,6 +99,16 @@ export function screenOf(view, ui) {
|
|
|
99
99
|
export function brandOf(method) {
|
|
100
100
|
return method.logos[0];
|
|
101
101
|
}
|
|
102
|
+
/** What the page keeps when a method is picked: the list closes, the rest starts over. */
|
|
103
|
+
export function pickedPatch(id) {
|
|
104
|
+
return {
|
|
105
|
+
method: id,
|
|
106
|
+
pickerOpen: false,
|
|
107
|
+
code: '',
|
|
108
|
+
cardComplete: false,
|
|
109
|
+
methodMissing: 0,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
102
112
|
/** Everything the picked method needs is in: the card, or the number and its payment code. */
|
|
103
113
|
function readyToPay(method, ui, phone) {
|
|
104
114
|
if (method.flow === MethodFlowEnum.CARD_FIELDS)
|
|
@@ -139,6 +149,7 @@ export function deriveCheckout(view, ui, phone) {
|
|
|
139
149
|
readyToPay(selected, ui, phone) &&
|
|
140
150
|
!ui.submitting &&
|
|
141
151
|
!ui.cardPaying,
|
|
152
|
+
detected,
|
|
142
153
|
// Only a Mobile Money method has a number to belong to another one.
|
|
143
154
|
mismatch: selected && !onCard && detected && detected.id !== selected.id
|
|
144
155
|
? detected
|
|
@@ -23,7 +23,7 @@ function MethodGrid({ cards, missing }) {
|
|
|
23
23
|
for (let index = 0; index < cards.length; index += 2) {
|
|
24
24
|
rows.push(cards.slice(index, index + 2));
|
|
25
25
|
}
|
|
26
|
-
return (_jsxs(Animated.View, { testID: "method-grid", accessibilityRole: "radiogroup", style: [styles.grid, { transform: [{ translateX: shake }] }], children: [rows.map((row) => (_jsxs(View, { style: styles.gridRow, children: [row.map(({ id, ...card }) => (_jsx(SheetMethodCard, { ...card, missing: missing > 0 }, id))), row.length === 1 && _jsx(View, { style: styles.gridSpacer })] }, row[0].id))), missing > 0 && (_jsx(SheetText, { accessibilityRole: "alert", weight: FigtreeEnum.MEDIUM, size: 13, color: colors.danger, children: t('method_missing') }))] }));
|
|
26
|
+
return (_jsxs(Animated.View, { testID: "method-grid", accessibilityRole: "radiogroup", style: [styles.grid, { transform: [{ translateX: shake }] }], children: [rows.map((row) => (_jsxs(View, { style: styles.gridRow, children: [row.map(({ id, ...card }) => (_jsx(SheetMethodCard, { ...card, missing: missing > 0, alone: cards.length === 1 }, id))), row.length === 1 && cards.length > 1 && (_jsx(View, { style: styles.gridSpacer }))] }, row[0].id))), missing > 0 && (_jsx(SheetText, { accessibilityRole: "alert", weight: FigtreeEnum.MEDIUM, size: 13, color: colors.danger, children: t('method_missing') }))] }));
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
29
|
* The form of design A: the country when there are several, the Mobile
|
|
@@ -1,3 +1,3 @@
|
|
|
1
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, missing, onSelect, }: MethodCardProps): import("react").JSX.Element;
|
|
2
|
+
/** One method of the grid: logo and radio dot, then its name and network, or why it is greyed. Alone, one full-width row. Picked, it becomes the chosen row. */
|
|
3
|
+
export declare function SheetMethodCard({ logo, name, sub, muted, missing, alone, onSelect, }: MethodCardProps): import("react").JSX.Element;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
3
3
|
import { LOGO_SIZE } from "../checkout-core/models/checkout.constants.js";
|
|
4
4
|
import { useSheetTheme } from "../hooks/use-sheet-theme.js";
|
|
@@ -6,16 +6,17 @@ import { FigtreeEnum } from "../models/sheet.enum.js";
|
|
|
6
6
|
import { SheetLogo } from "./SheetLogo.js";
|
|
7
7
|
import { SheetText } from "./SheetText.js";
|
|
8
8
|
import { FIELD_RADIUS } from "../models/sheet.constants.js";
|
|
9
|
-
/** 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. */
|
|
10
|
-
export function SheetMethodCard({ logo, name, sub, muted, missing
|
|
9
|
+
/** One method of the grid: logo and radio dot, then its name and network, or why it is greyed. Alone, one full-width row. Picked, it becomes the chosen row. */
|
|
10
|
+
export function SheetMethodCard({ logo, name, sub, muted, missing, alone, onSelect, }) {
|
|
11
11
|
const { colors } = useSheetTheme();
|
|
12
12
|
const red = missing && !muted;
|
|
13
|
+
const texts = (_jsxs(_Fragment, { children: [_jsx(SheetText, { weight: FigtreeEnum.BOLD, size: 15, numberOfLines: 1, color: muted ? colors.muted : colors.ink, children: name }), _jsx(SheetText, { size: 12.5, numberOfLines: 1, color: muted ? colors.warnInk : colors.muted, children: sub })] }));
|
|
13
14
|
return (_jsxs(TouchableOpacity, { onPress: onSelect, disabled: muted, activeOpacity: 0.7, accessibilityRole: "radio", accessibilityLabel: `${name}, ${sub}`, accessibilityState: { checked: false, disabled: muted }, style: [
|
|
14
15
|
styles.card,
|
|
15
16
|
red
|
|
16
17
|
? [styles.cardMissing, { borderColor: colors.danger }]
|
|
17
18
|
: { borderColor: colors.line2 },
|
|
18
|
-
], children: [_jsxs(View, { style: styles.top, children: [_jsx(SheetLogo, { logo: logo, size: LOGO_SIZE.ROW, muted: muted }), _jsx(View, { style: [
|
|
19
|
+
], children: [_jsxs(View, { style: alone ? styles.row : styles.top, children: [_jsx(SheetLogo, { logo: logo, size: LOGO_SIZE.ROW, muted: muted }), alone && _jsx(View, { style: styles.text, children: texts }), _jsx(View, { style: [
|
|
19
20
|
styles.radio,
|
|
20
21
|
{
|
|
21
22
|
borderColor: muted
|
|
@@ -24,7 +25,7 @@ export function SheetMethodCard({ logo, name, sub, muted, missing = false, onSel
|
|
|
24
25
|
? colors.danger
|
|
25
26
|
: colors.line4,
|
|
26
27
|
},
|
|
27
|
-
] })] }),
|
|
28
|
+
] })] }), !alone && texts] }));
|
|
28
29
|
}
|
|
29
30
|
const styles = StyleSheet.create({
|
|
30
31
|
card: {
|
|
@@ -43,5 +44,7 @@ const styles = StyleSheet.create({
|
|
|
43
44
|
justifyContent: 'space-between',
|
|
44
45
|
marginBottom: 10,
|
|
45
46
|
},
|
|
47
|
+
row: { flexDirection: 'row', alignItems: 'center', gap: 12 },
|
|
48
|
+
text: { flex: 1, minWidth: 0 },
|
|
46
49
|
radio: { width: 20, height: 20, borderRadius: 10, borderWidth: 1.5 },
|
|
47
50
|
});
|
|
@@ -181,6 +181,8 @@ export interface MethodCardProps {
|
|
|
181
181
|
muted: boolean;
|
|
182
182
|
/** Pay was pressed with no method picked: the card is ringed in red. */
|
|
183
183
|
missing?: boolean;
|
|
184
|
+
/** The only method of the tab: a full-width row. */
|
|
185
|
+
alone?: boolean;
|
|
184
186
|
onSelect?: () => void;
|
|
185
187
|
}
|
|
186
188
|
export interface ChosenMethodProps {
|