@moonpay/platform-sdk-react-native 1.1.0 → 1.3.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/README.md +2 -0
- package/dist/index.cjs +120 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -10
- package/dist/index.d.ts +9 -10
- package/dist/index.js +130 -36
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -61,6 +61,8 @@ export function App() {
|
|
|
61
61
|
|
|
62
62
|
`MoonPayProvider` also accepts optional `apiBaseUrl` and `frameBaseUrl` props that point the SDK at a different MoonPay environment — useful if MoonPay gives you test endpoints during onboarding. Leave them unset to use production.
|
|
63
63
|
|
|
64
|
+
Pass an optional `theme={{ appearance: 'light' | 'dark' }}` to render every MoonPay frame in light or dark mode. Omit it to follow the customer's system preference. The connect flow can override it per call (`client.connect({ theme })` or `<MoonPayConnect theme={...} />`).
|
|
65
|
+
|
|
64
66
|
**3. Check the connection and connect the customer:**
|
|
65
67
|
|
|
66
68
|
```tsx
|
package/dist/index.cjs
CHANGED
|
@@ -158,6 +158,8 @@ var buyButtonSpec = {
|
|
|
158
158
|
},
|
|
159
159
|
mapMessage: (msg) => {
|
|
160
160
|
switch (msg.kind) {
|
|
161
|
+
case "ready":
|
|
162
|
+
return { kind: "ready" };
|
|
161
163
|
case "complete":
|
|
162
164
|
return { kind: "complete", payload: msg.payload };
|
|
163
165
|
case "challenge":
|
|
@@ -220,7 +222,9 @@ var connectSpec = {
|
|
|
220
222
|
buildParams: (ctx, props, publicKey) => ({
|
|
221
223
|
sessionToken: ctx.sessionToken,
|
|
222
224
|
publicKey,
|
|
223
|
-
|
|
225
|
+
// Per-call override; `applyThemeParam` in the engine fills the client
|
|
226
|
+
// default for connect (and every other visible frame) when this is absent.
|
|
227
|
+
...(0, import_platform_sdk_core.themeParams)(props.theme)
|
|
224
228
|
}),
|
|
225
229
|
onMessageEffect: applyCredentialsOnComplete,
|
|
226
230
|
mapMessage: (msg) => {
|
|
@@ -521,11 +525,12 @@ function createFrameSession(spec, ctx, props, overrides = {}) {
|
|
|
521
525
|
const generated = `${spec.channelPrefix}-${Date.now()}`;
|
|
522
526
|
const channelId = spec.resolveChannelId ? spec.resolveChannelId(props, generated) : generated;
|
|
523
527
|
const keyPair = spec.needsKeyPair ? (0, import_platform_sdk_core2.generateKeyPair)() : void 0;
|
|
524
|
-
const
|
|
528
|
+
const builtUrl = spec.buildUrl ? spec.buildUrl(ctx, props, channelId, keyPair?.publicKey) : (0, import_platform_sdk_core2.buildFrameUrl)(
|
|
525
529
|
spec.path,
|
|
526
530
|
{ ...spec.buildParams?.(ctx, props, keyPair?.publicKey), channelId },
|
|
527
531
|
{ frameBaseUrl: ctx.frameBaseUrl }
|
|
528
532
|
);
|
|
533
|
+
const url = spec.hidden ? builtUrl : (0, import_platform_sdk_core2.applyThemeParam)(builtUrl, ctx.theme);
|
|
529
534
|
const transport = (overrides.createTransport ?? (() => new WebViewTransport()))();
|
|
530
535
|
let resolvedHandle = null;
|
|
531
536
|
let rejectDisposed;
|
|
@@ -909,7 +914,34 @@ function createMountSession(specCtx, host) {
|
|
|
909
914
|
// src/VisibleFrameSlotSheet.tsx
|
|
910
915
|
var import_react2 = require("react");
|
|
911
916
|
var import_react_native2 = require("react-native");
|
|
917
|
+
|
|
918
|
+
// src/sheet-presentation.ts
|
|
919
|
+
var NOOP = {
|
|
920
|
+
present: false,
|
|
921
|
+
liftSpinner: false,
|
|
922
|
+
dismissSilent: false,
|
|
923
|
+
close: false
|
|
924
|
+
};
|
|
925
|
+
function resolvePresentation(phase, signal) {
|
|
926
|
+
if (phase === "closed")
|
|
927
|
+
return { phase: "closed", ...NOOP };
|
|
928
|
+
switch (signal) {
|
|
929
|
+
case "ready":
|
|
930
|
+
return { ...NOOP, phase: "presented", present: phase === "hidden", liftSpinner: true };
|
|
931
|
+
case "grace":
|
|
932
|
+
if (phase === "presented")
|
|
933
|
+
return { phase: "presented", ...NOOP };
|
|
934
|
+
return { ...NOOP, phase: "presented", present: true };
|
|
935
|
+
case "terminal":
|
|
936
|
+
if (phase === "hidden")
|
|
937
|
+
return { ...NOOP, phase: "closed", dismissSilent: true };
|
|
938
|
+
return { ...NOOP, phase: "closed", close: true };
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// src/VisibleFrameSlotSheet.tsx
|
|
912
943
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
944
|
+
var PRESENT_GRACE_MS = 3e3;
|
|
913
945
|
function animateSheet(value, toValue, onDone) {
|
|
914
946
|
const animation = import_react_native2.Platform.OS === "android" ? import_react_native2.Animated.timing(value, {
|
|
915
947
|
toValue,
|
|
@@ -936,6 +968,8 @@ function VisibleFrameSlotSheet({
|
|
|
936
968
|
const sheetHeight = Math.round(height * 0.9);
|
|
937
969
|
const translateY = (0, import_react2.useRef)(new import_react_native2.Animated.Value(sheetHeight)).current;
|
|
938
970
|
const [frameReady, setFrameReady] = (0, import_react2.useState)(false);
|
|
971
|
+
const [presented, setPresented] = (0, import_react2.useState)(false);
|
|
972
|
+
const phaseRef = (0, import_react2.useRef)("hidden");
|
|
939
973
|
const onCloseRef = (0, import_react2.useRef)(onClose);
|
|
940
974
|
onCloseRef.current = onClose;
|
|
941
975
|
const closingRef = (0, import_react2.useRef)(false);
|
|
@@ -943,29 +977,80 @@ function VisibleFrameSlotSheet({
|
|
|
943
977
|
if (closingRef.current)
|
|
944
978
|
return;
|
|
945
979
|
closingRef.current = true;
|
|
980
|
+
phaseRef.current = "closed";
|
|
946
981
|
animateSheet(translateY, sheetHeight, ({ finished }) => {
|
|
947
982
|
if (finished)
|
|
948
983
|
onCloseRef.current();
|
|
949
984
|
});
|
|
950
985
|
}, [translateY, sheetHeight]);
|
|
986
|
+
const panResponder = (0, import_react2.useMemo)(
|
|
987
|
+
() => import_react_native2.PanResponder.create({
|
|
988
|
+
// Claim the gesture only for a clearly downward drag — never on a tap
|
|
989
|
+
// (so the handle's onPress still fires) or a mostly-horizontal swipe.
|
|
990
|
+
onMoveShouldSetPanResponder: (_evt, { dy, dx }) => dy > 5 && dy > Math.abs(dx),
|
|
991
|
+
onPanResponderGrant: () => {
|
|
992
|
+
translateY.stopAnimation(() => {
|
|
993
|
+
translateY.extractOffset();
|
|
994
|
+
});
|
|
995
|
+
},
|
|
996
|
+
// Track the finger downward; clamp upward travel to the open position.
|
|
997
|
+
onPanResponderMove: (_evt, { dy }) => {
|
|
998
|
+
translateY.setValue(Math.max(0, dy));
|
|
999
|
+
},
|
|
1000
|
+
onPanResponderRelease: (_evt, { dy, vy }) => {
|
|
1001
|
+
translateY.flattenOffset();
|
|
1002
|
+
const draggedPastThreshold = dy > sheetHeight * 0.35;
|
|
1003
|
+
const flungDownFast = vy > 0.5;
|
|
1004
|
+
if (draggedPastThreshold || flungDownFast) {
|
|
1005
|
+
close();
|
|
1006
|
+
} else {
|
|
1007
|
+
animateSheet(translateY, 0);
|
|
1008
|
+
}
|
|
1009
|
+
},
|
|
1010
|
+
// Another responder stole the gesture: settle to a known state.
|
|
1011
|
+
onPanResponderTerminate: () => {
|
|
1012
|
+
translateY.flattenOffset();
|
|
1013
|
+
animateSheet(translateY, 0);
|
|
1014
|
+
}
|
|
1015
|
+
}),
|
|
1016
|
+
[translateY, sheetHeight, close]
|
|
1017
|
+
);
|
|
1018
|
+
const dispatch = (0, import_react2.useCallback)(
|
|
1019
|
+
(signal) => {
|
|
1020
|
+
const result = resolvePresentation(phaseRef.current, signal);
|
|
1021
|
+
phaseRef.current = result.phase;
|
|
1022
|
+
if (result.present) {
|
|
1023
|
+
setPresented(true);
|
|
1024
|
+
animateSheet(translateY, 0);
|
|
1025
|
+
}
|
|
1026
|
+
if (result.liftSpinner)
|
|
1027
|
+
setFrameReady(true);
|
|
1028
|
+
if (result.dismissSilent)
|
|
1029
|
+
onCloseRef.current();
|
|
1030
|
+
if (result.close)
|
|
1031
|
+
close();
|
|
1032
|
+
},
|
|
1033
|
+
[translateY, close]
|
|
1034
|
+
);
|
|
951
1035
|
(0, import_react2.useEffect)(() => {
|
|
952
|
-
|
|
953
|
-
|
|
1036
|
+
const timer = setTimeout(() => dispatch("grace"), PRESENT_GRACE_MS);
|
|
1037
|
+
return () => clearTimeout(timer);
|
|
1038
|
+
}, [dispatch]);
|
|
954
1039
|
(0, import_react2.useEffect)(() => {
|
|
955
1040
|
const unsubscribe = slot.transport.onMessage((msg) => {
|
|
956
1041
|
if (msg.kind === "ready")
|
|
957
|
-
|
|
1042
|
+
dispatch("ready");
|
|
958
1043
|
else if (msg.kind === "complete" || msg.kind === "error")
|
|
959
|
-
|
|
1044
|
+
dispatch("terminal");
|
|
960
1045
|
});
|
|
961
1046
|
return unsubscribe;
|
|
962
|
-
}, [slot.transport,
|
|
1047
|
+
}, [slot.transport, dispatch]);
|
|
963
1048
|
const backdropOpacity = translateY.interpolate({
|
|
964
1049
|
inputRange: [0, sheetHeight],
|
|
965
1050
|
outputRange: [1, 0],
|
|
966
1051
|
extrapolate: "clamp"
|
|
967
1052
|
});
|
|
968
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Modal, { visible: true, transparent: true, animationType: "none", onRequestClose: close, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles.root, children: [
|
|
1053
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Modal, { visible: true, transparent: true, animationType: "none", onRequestClose: close, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles.root, pointerEvents: presented ? "auto" : "none", children: [
|
|
969
1054
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Animated.View, { style: [styles.backdrop, { opacity: backdropOpacity }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
970
1055
|
import_react_native2.Pressable,
|
|
971
1056
|
{
|
|
@@ -978,17 +1063,16 @@ function VisibleFrameSlotSheet({
|
|
|
978
1063
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.Animated.View, { style: [styles.sheet, { height: sheetHeight, transform: [{ translateY }] }], children: [
|
|
979
1064
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MoonPayFrame, { transport: slot.transport }),
|
|
980
1065
|
!frameReady && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles.loading, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.ActivityIndicator, { size: "large", color: "#000" }) }),
|
|
981
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1066
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles.handleArea, ...panResponder.panHandlers, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
982
1067
|
import_react_native2.Pressable,
|
|
983
1068
|
{
|
|
984
1069
|
onPress: close,
|
|
985
1070
|
accessibilityRole: "button",
|
|
986
1071
|
accessibilityLabel: "Close",
|
|
987
|
-
hitSlop:
|
|
988
|
-
style: styles.
|
|
989
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: styles.closeButtonText, children: "\u2715" })
|
|
1072
|
+
hitSlop: 12,
|
|
1073
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles.handleBar })
|
|
990
1074
|
}
|
|
991
|
-
)
|
|
1075
|
+
) })
|
|
992
1076
|
] })
|
|
993
1077
|
] }) });
|
|
994
1078
|
}
|
|
@@ -1018,25 +1102,25 @@ var styles = import_react_native2.StyleSheet.create({
|
|
|
1018
1102
|
alignItems: "center",
|
|
1019
1103
|
justifyContent: "center"
|
|
1020
1104
|
},
|
|
1021
|
-
//
|
|
1022
|
-
|
|
1105
|
+
// Drag-to-dismiss strip pinned across the top of the sheet, floating over the
|
|
1106
|
+
// frame. Tall enough to be an easy grab target without eating much of the UI.
|
|
1107
|
+
handleArea: {
|
|
1023
1108
|
position: "absolute",
|
|
1024
|
-
top:
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
height:
|
|
1028
|
-
borderRadius: 16,
|
|
1029
|
-
backgroundColor: "rgba(0, 0, 0, 0.06)",
|
|
1109
|
+
top: 0,
|
|
1110
|
+
left: 0,
|
|
1111
|
+
right: 0,
|
|
1112
|
+
height: 28,
|
|
1030
1113
|
alignItems: "center",
|
|
1031
1114
|
justifyContent: "center",
|
|
1032
1115
|
zIndex: 1,
|
|
1033
1116
|
elevation: 2
|
|
1034
1117
|
},
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1118
|
+
// The visible pill.
|
|
1119
|
+
handleBar: {
|
|
1120
|
+
width: 36,
|
|
1121
|
+
height: 5,
|
|
1122
|
+
borderRadius: 3,
|
|
1123
|
+
backgroundColor: "rgba(0, 0, 0, 0.2)"
|
|
1040
1124
|
}
|
|
1041
1125
|
});
|
|
1042
1126
|
|
|
@@ -1047,6 +1131,7 @@ function MoonPayProvider({
|
|
|
1047
1131
|
sessionToken,
|
|
1048
1132
|
apiBaseUrl,
|
|
1049
1133
|
frameBaseUrl,
|
|
1134
|
+
theme,
|
|
1050
1135
|
children
|
|
1051
1136
|
}) {
|
|
1052
1137
|
const [frameSlots, setFrameSlots] = (0, import_react3.useState)([]);
|
|
@@ -1066,13 +1151,13 @@ function MoonPayProvider({
|
|
|
1066
1151
|
}
|
|
1067
1152
|
const core = coreRef.current;
|
|
1068
1153
|
const client = (0, import_react3.useMemo)(() => {
|
|
1069
|
-
const specCtx = { sessionToken, core, frameBaseUrl };
|
|
1154
|
+
const specCtx = { sessionToken, core, frameBaseUrl, theme };
|
|
1070
1155
|
return createRNClient({
|
|
1071
1156
|
mountSession: createMountSession(specCtx, { addSlot, removeSlot }),
|
|
1072
1157
|
core
|
|
1073
1158
|
});
|
|
1074
|
-
}, [sessionToken, frameBaseUrl, core, addSlot, removeSlot]);
|
|
1075
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MoonPayContext.Provider, { value: { client, sessionToken, core, frameBaseUrl }, children: [
|
|
1159
|
+
}, [sessionToken, frameBaseUrl, core, addSlot, removeSlot, theme?.appearance]);
|
|
1160
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MoonPayContext.Provider, { value: { client, sessionToken, core, frameBaseUrl, theme }, children: [
|
|
1076
1161
|
children,
|
|
1077
1162
|
frameSlots.filter((slot) => slot.hidden).map((slot) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MoonPayFrame, { transport: slot.transport, hidden: true }, slot.id)),
|
|
1078
1163
|
frameSlots.filter((slot) => !slot.hidden).map((slot) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -1117,7 +1202,7 @@ function MoonPayFrameView({
|
|
|
1117
1202
|
defaultStyle,
|
|
1118
1203
|
onEvent
|
|
1119
1204
|
}) {
|
|
1120
|
-
const { sessionToken, core, frameBaseUrl } = useMoonPayContext();
|
|
1205
|
+
const { sessionToken, core, frameBaseUrl, theme } = useMoonPayContext();
|
|
1121
1206
|
const [transport, setTransport] = (0, import_react4.useState)(null);
|
|
1122
1207
|
const handleRef = (0, import_react4.useRef)(null);
|
|
1123
1208
|
const onEventRef = (0, import_react4.useRef)(onEvent);
|
|
@@ -1143,7 +1228,11 @@ function MoonPayFrameView({
|
|
|
1143
1228
|
}
|
|
1144
1229
|
appliedRef.current = applied;
|
|
1145
1230
|
try {
|
|
1146
|
-
session = createFrameSession(
|
|
1231
|
+
session = createFrameSession(
|
|
1232
|
+
spec,
|
|
1233
|
+
{ sessionToken, core, frameBaseUrl, theme },
|
|
1234
|
+
propsRef.current
|
|
1235
|
+
);
|
|
1147
1236
|
} catch (e) {
|
|
1148
1237
|
onEventRef.current?.(
|
|
1149
1238
|
spec.errorEvent(e instanceof Error ? e.message : "Failed to create frame")
|