@oobit/react-native-sdk 3.0.1 → 3.1.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/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -4
- package/dist/index.js +243 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +238 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +20 -10
- package/src/{WidgetSDK.tsx → components/WidgetSDK.tsx} +10 -9
- package/src/index.ts +5 -5
- package/src/utils/walletUtils.ts +94 -0
- package/dist/WidgetSDK.d.ts +0 -12
- package/dist/WidgetSDK.d.ts.map +0 -1
- package/dist/WidgetSDK.js +0 -143
- package/dist/WidgetSDK.js.map +0 -1
- package/dist/config.d.ts +0 -24
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -53
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/types.d.ts +0 -73
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -17
- package/dist/types.js.map +0 -1
- package/dist/walletUtils.d.ts +0 -14
- package/dist/walletUtils.d.ts.map +0 -1
- package/dist/walletUtils.js +0 -144
- package/dist/walletUtils.js.map +0 -1
- package/src/config.ts +0 -51
- package/src/types.ts +0 -76
- package/src/walletUtils.ts +0 -155
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { WidgetSDKConfig } from '@oobit/core';
|
|
3
|
+
export { EvmTransactionRequest, SolanaTransactionRequest, TransactionRequest, TransactionTokenMetadata, WidgetSDKConfig } from '@oobit/core';
|
|
4
|
+
|
|
5
|
+
interface WidgetSDKRef {
|
|
6
|
+
navigateBack: () => void;
|
|
7
|
+
reload: () => void;
|
|
8
|
+
}
|
|
9
|
+
declare const WidgetSDK: React.ForwardRefExoticComponent<WidgetSDKConfig & React.RefAttributes<WidgetSDKRef>>;
|
|
10
|
+
|
|
11
|
+
export { WidgetSDK, type WidgetSDKRef };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { WidgetSDKConfig } from '@oobit/core';
|
|
3
|
+
export { EvmTransactionRequest, SolanaTransactionRequest, TransactionRequest, TransactionTokenMetadata, WidgetSDKConfig } from '@oobit/core';
|
|
4
|
+
|
|
5
|
+
interface WidgetSDKRef {
|
|
6
|
+
navigateBack: () => void;
|
|
7
|
+
reload: () => void;
|
|
8
|
+
}
|
|
9
|
+
declare const WidgetSDK: React.ForwardRefExoticComponent<WidgetSDKConfig & React.RefAttributes<WidgetSDKRef>>;
|
|
10
|
+
|
|
11
|
+
export { WidgetSDK, type WidgetSDKRef };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,244 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var
|
|
5
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var reactNative = require('react-native');
|
|
5
|
+
var reactNativeWebview = require('react-native-webview');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
10
|
+
|
|
11
|
+
// src/components/WidgetSDK.tsx
|
|
12
|
+
|
|
13
|
+
// ../core/dist/index.mjs
|
|
14
|
+
var WALLET_URLS = {
|
|
15
|
+
ios: {
|
|
16
|
+
passkit: "shoebox://",
|
|
17
|
+
fallback: "https://wallet.apple.com"
|
|
18
|
+
},
|
|
19
|
+
android: {
|
|
20
|
+
googlePay: "https://pay.google.com/gp/w/home/wallet",
|
|
21
|
+
fallback: "https://wallet.google.com"
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var TOKEN_PREFIX = {
|
|
25
|
+
SANDBOX: "sbx_",
|
|
26
|
+
PRODUCTION: "prod_"
|
|
27
|
+
};
|
|
28
|
+
var WIDGET_URLS = {
|
|
29
|
+
sandbox: "https://oobit-widget-dev.web.app",
|
|
30
|
+
production: "https://oobit-widget.web.app"
|
|
31
|
+
};
|
|
32
|
+
function getWidgetUrl(accessToken) {
|
|
33
|
+
if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {
|
|
34
|
+
return WIDGET_URLS.sandbox;
|
|
35
|
+
}
|
|
36
|
+
if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {
|
|
37
|
+
return WIDGET_URLS.production;
|
|
38
|
+
}
|
|
39
|
+
return WIDGET_URLS.production;
|
|
40
|
+
}
|
|
41
|
+
function stripTokenPrefix(accessToken) {
|
|
42
|
+
if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {
|
|
43
|
+
return accessToken.slice(TOKEN_PREFIX.SANDBOX.length);
|
|
44
|
+
}
|
|
45
|
+
if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {
|
|
46
|
+
return accessToken.slice(TOKEN_PREFIX.PRODUCTION.length);
|
|
47
|
+
}
|
|
48
|
+
return accessToken;
|
|
49
|
+
}
|
|
50
|
+
var GOOGLE_WALLET_PACKAGE = "com.google.android.apps.walletnfcrel";
|
|
51
|
+
async function openNativeWallet() {
|
|
52
|
+
try {
|
|
53
|
+
if (reactNative.Platform.OS === "ios") {
|
|
54
|
+
return await openAppleWallet();
|
|
55
|
+
}
|
|
56
|
+
if (reactNative.Platform.OS === "android") {
|
|
57
|
+
return await openGoogleWallet();
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
} catch {
|
|
61
|
+
reactNative.Alert.alert(
|
|
62
|
+
"Error",
|
|
63
|
+
"Could not open wallet. Please check if the app is installed."
|
|
64
|
+
);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function openAppleWallet() {
|
|
69
|
+
const { passkit, fallback } = WALLET_URLS.ios;
|
|
70
|
+
if (await tryOpenUrl("wallet://")) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (await tryOpenUrl(passkit)) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
if (await tryOpenUrl(fallback)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
throw new Error("Apple Wallet not available");
|
|
80
|
+
}
|
|
81
|
+
async function openGoogleWallet() {
|
|
82
|
+
const launchUrl = `market://launch?id=${GOOGLE_WALLET_PACKAGE}`;
|
|
83
|
+
if (await tryOpenUrl(launchUrl)) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
const playStoreUrl = `market://details?id=${GOOGLE_WALLET_PACKAGE}`;
|
|
87
|
+
const webPlayStoreUrl = `https://play.google.com/store/apps/details?id=${GOOGLE_WALLET_PACKAGE}`;
|
|
88
|
+
const opened = await tryOpenUrl(playStoreUrl) || await tryOpenUrl(webPlayStoreUrl);
|
|
89
|
+
if (opened) {
|
|
90
|
+
reactNative.Alert.alert(
|
|
91
|
+
"Google Wallet",
|
|
92
|
+
"Google Wallet is not installed. Opening Play Store to install the app."
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
async function tryOpenUrl(url) {
|
|
98
|
+
try {
|
|
99
|
+
const canOpen = await reactNative.Linking.canOpenURL(url);
|
|
100
|
+
if (canOpen) {
|
|
101
|
+
await reactNative.Linking.openURL(url);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
async function isWalletAvailable() {
|
|
109
|
+
try {
|
|
110
|
+
if (reactNative.Platform.OS === "ios") {
|
|
111
|
+
return await reactNative.Linking.canOpenURL(WALLET_URLS.ios.passkit);
|
|
112
|
+
}
|
|
113
|
+
if (reactNative.Platform.OS === "android") {
|
|
114
|
+
return await reactNative.Linking.canOpenURL(WALLET_URLS.android.googlePay);
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
} catch {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/components/WidgetSDK.tsx
|
|
123
|
+
var WidgetSDK = React.forwardRef(
|
|
124
|
+
({ accessToken, userWalletAddress, onClose, onTransactionRequested }, ref) => {
|
|
125
|
+
const webViewRef = React.useRef(null);
|
|
126
|
+
const [walletAvailable, setWalletAvailable] = React.useState(false);
|
|
127
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
128
|
+
const hasLoadedOnce = React.useRef(false);
|
|
129
|
+
React.useEffect(() => {
|
|
130
|
+
isWalletAvailable().then(setWalletAvailable);
|
|
131
|
+
}, []);
|
|
132
|
+
React.useEffect(() => {
|
|
133
|
+
const backHandler = reactNative.BackHandler.addEventListener(
|
|
134
|
+
"hardwareBackPress",
|
|
135
|
+
() => {
|
|
136
|
+
sendMessageToWidget({ type: "native:back-pressed" });
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
return () => backHandler.remove();
|
|
141
|
+
}, []);
|
|
142
|
+
React.useImperativeHandle(ref, () => ({
|
|
143
|
+
navigateBack: () => sendMessageToWidget({ type: "native:navigate-back" }),
|
|
144
|
+
reload: () => {
|
|
145
|
+
setIsLoading(true);
|
|
146
|
+
hasLoadedOnce.current = false;
|
|
147
|
+
webViewRef.current?.reload();
|
|
148
|
+
}
|
|
149
|
+
}));
|
|
150
|
+
const handleMessage = (event) => {
|
|
151
|
+
try {
|
|
152
|
+
const message = JSON.parse(event.nativeEvent.data);
|
|
153
|
+
switch (message.type) {
|
|
154
|
+
case "widget:ready":
|
|
155
|
+
sendMessageToWidget({
|
|
156
|
+
type: "native:platform-info",
|
|
157
|
+
payload: { platform: reactNative.Platform.OS, walletAvailable }
|
|
158
|
+
});
|
|
159
|
+
break;
|
|
160
|
+
case "widget:open-wallet":
|
|
161
|
+
openNativeWallet().then((success) => {
|
|
162
|
+
sendMessageToWidget({
|
|
163
|
+
type: "native:wallet-opened",
|
|
164
|
+
payload: { success }
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
break;
|
|
168
|
+
case "widget:close":
|
|
169
|
+
onClose?.();
|
|
170
|
+
break;
|
|
171
|
+
case "widget:transaction-requested":
|
|
172
|
+
onTransactionRequested(message.payload);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
} catch {
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const sendMessageToWidget = (message) => {
|
|
179
|
+
webViewRef.current?.injectJavaScript(
|
|
180
|
+
`window.postMessage(${JSON.stringify(message)}, '*'); true;`
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
const finalWidgetUrl = React.useMemo(() => {
|
|
184
|
+
const baseUrl = getWidgetUrl(accessToken);
|
|
185
|
+
const params = new URLSearchParams({
|
|
186
|
+
token: stripTokenPrefix(accessToken),
|
|
187
|
+
platform: reactNative.Platform.OS,
|
|
188
|
+
userWalletAddress,
|
|
189
|
+
sdk: "react-native"
|
|
190
|
+
});
|
|
191
|
+
return `${baseUrl}?${params.toString()}`;
|
|
192
|
+
}, [accessToken, userWalletAddress]);
|
|
193
|
+
return /* @__PURE__ */ React__default.default.createElement(reactNative.View, { style: styles.container }, /* @__PURE__ */ React__default.default.createElement(
|
|
194
|
+
reactNativeWebview.WebView,
|
|
195
|
+
{
|
|
196
|
+
ref: webViewRef,
|
|
197
|
+
style: styles.webview,
|
|
198
|
+
source: { uri: finalWidgetUrl },
|
|
199
|
+
onMessage: handleMessage,
|
|
200
|
+
onLoadEnd: () => {
|
|
201
|
+
if (!hasLoadedOnce.current) {
|
|
202
|
+
hasLoadedOnce.current = true;
|
|
203
|
+
setIsLoading(false);
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
onShouldStartLoadWithRequest: (request) => {
|
|
207
|
+
const { url } = request;
|
|
208
|
+
if (url.startsWith("shoebox://") || url.includes("pay.google.com") || url.includes("wallet.google.com") || url.includes("wallet.apple.com")) {
|
|
209
|
+
reactNative.Linking.openURL(url).catch(() => {
|
|
210
|
+
});
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
return true;
|
|
214
|
+
},
|
|
215
|
+
onOpenWindow: (syntheticEvent) => {
|
|
216
|
+
const { nativeEvent } = syntheticEvent;
|
|
217
|
+
const { targetUrl } = nativeEvent;
|
|
218
|
+
reactNative.Linking.openURL(targetUrl).catch(() => {
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
javaScriptEnabled: true,
|
|
222
|
+
domStorageEnabled: true,
|
|
223
|
+
allowsInlineMediaPlayback: true,
|
|
224
|
+
bounces: false,
|
|
225
|
+
allowsBackForwardNavigationGestures: true,
|
|
226
|
+
mixedContentMode: "always"
|
|
227
|
+
}
|
|
228
|
+
), isLoading && /* @__PURE__ */ React__default.default.createElement(reactNative.View, { style: styles.loadingOverlay }, /* @__PURE__ */ React__default.default.createElement(reactNative.ActivityIndicator, { size: "large", color: "#007AFF" })));
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
var styles = reactNative.StyleSheet.create({
|
|
232
|
+
container: { flex: 1 },
|
|
233
|
+
webview: { flex: 1 },
|
|
234
|
+
loadingOverlay: {
|
|
235
|
+
...reactNative.StyleSheet.absoluteFillObject,
|
|
236
|
+
backgroundColor: "rgba(255, 255, 255, 0.9)",
|
|
237
|
+
justifyContent: "center",
|
|
238
|
+
alignItems: "center"
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
exports.WidgetSDK = WidgetSDK;
|
|
243
|
+
//# sourceMappingURL=index.js.map
|
|
6
244
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAQA,yCAAwC;AAA/B,sGAAA,SAAS,OAAA"}
|
|
1
|
+
{"version":3,"sources":["../../core/src/types/messages.ts","../../core/src/config/environment.ts","../src/utils/walletUtils.ts","../src/components/WidgetSDK.tsx"],"names":["Platform","Alert","Linking","forwardRef","useRef","useState","useEffect","BackHandler","useImperativeHandle","useMemo","React","View","WebView","ActivityIndicator","StyleSheet"],"mappings":"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","file":"index.js","sourcesContent":["import type { TransactionRequest } from './transactions';\n\n/**\n * Internal: Messages from widget\n */\nexport type WidgetMessage =\n | { type: \"widget:ready\" }\n | { type: \"widget:open-wallet\" }\n | { type: \"widget:close\" }\n | { type: \"widget:transaction-requested\"; payload: TransactionRequest };\n\n/**\n * Internal: Wallet URLs\n */\nexport const WALLET_URLS = {\n ios: {\n passkit: \"shoebox://\",\n fallback: \"https://wallet.apple.com\",\n },\n android: {\n googlePay: \"https://pay.google.com/gp/w/home/wallet\",\n fallback: \"https://wallet.google.com\",\n },\n} as const;\n","/**\n * Widget SDK Configuration\n * Environment detection based on access token prefix\n */\n\n/**\n * Token prefixes that determine the environment\n */\nconst TOKEN_PREFIX = {\n SANDBOX: \"sbx_\",\n PRODUCTION: \"prod_\",\n} as const;\n\n/**\n * Widget URLs by environment\n */\nexport const WIDGET_URLS = {\n sandbox: \"https://oobit-widget-dev.web.app\",\n production: \"https://oobit-widget.web.app\",\n} as const;\n\n/**\n * Get the widget URL based on the access token prefix\n * @param accessToken - The access token (prefixed with sbx_ or prod_)\n * @returns The widget URL for the corresponding environment\n */\nexport function getWidgetUrl(accessToken: string): string {\n if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {\n return WIDGET_URLS.sandbox;\n }\n if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {\n return WIDGET_URLS.production;\n }\n // Default to production for backwards compatibility\n return WIDGET_URLS.production;\n}\n\n/**\n * Strip the environment prefix from an access token\n * @param accessToken - The access token (prefixed with sbx_ or prod_)\n * @returns The token without the environment prefix\n */\nexport function stripTokenPrefix(accessToken: string): string {\n if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {\n return accessToken.slice(TOKEN_PREFIX.SANDBOX.length);\n }\n if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {\n return accessToken.slice(TOKEN_PREFIX.PRODUCTION.length);\n }\n return accessToken;\n}\n","import { Alert, Linking, Platform } from \"react-native\";\nimport { WALLET_URLS } from \"@oobit/core\";\n\nconst GOOGLE_WALLET_PACKAGE = \"com.google.android.apps.walletnfcrel\";\n\nexport async function openNativeWallet(): Promise<boolean> {\n try {\n if (Platform.OS === \"ios\") {\n return await openAppleWallet();\n }\n if (Platform.OS === \"android\") {\n return await openGoogleWallet();\n }\n return false;\n } catch {\n Alert.alert(\n \"Error\",\n \"Could not open wallet. Please check if the app is installed.\"\n );\n return false;\n }\n}\n\nasync function openAppleWallet(): Promise<boolean> {\n const { passkit, fallback } = WALLET_URLS.ios;\n\n // Try wallet:// scheme first (documented for recent iOS)\n if (await tryOpenUrl(\"wallet://\")) {\n return true;\n }\n\n // Try shoebox:// scheme (unofficial but widely used)\n if (await tryOpenUrl(passkit)) {\n return true;\n }\n\n // Fallback to wallet.apple.com (opens in browser)\n if (await tryOpenUrl(fallback)) {\n return true;\n }\n\n throw new Error(\"Apple Wallet not available\");\n}\n\nasync function openGoogleWallet(): Promise<boolean> {\n const launchUrl = `market://launch?id=${GOOGLE_WALLET_PACKAGE}`;\n\n // Try launching the app directly\n if (await tryOpenUrl(launchUrl)) {\n return true;\n }\n\n // App not installed - open Play Store\n const playStoreUrl = `market://details?id=${GOOGLE_WALLET_PACKAGE}`;\n const webPlayStoreUrl = `https://play.google.com/store/apps/details?id=${GOOGLE_WALLET_PACKAGE}`;\n\n const opened = await tryOpenUrl(playStoreUrl) || await tryOpenUrl(webPlayStoreUrl);\n\n if (opened) {\n Alert.alert(\n \"Google Wallet\",\n \"Google Wallet is not installed. Opening Play Store to install the app.\"\n );\n }\n\n return false;\n}\n\nasync function tryOpenUrl(url: string): Promise<boolean> {\n try {\n const canOpen = await Linking.canOpenURL(url);\n if (canOpen) {\n await Linking.openURL(url);\n return true;\n }\n } catch {\n // URL scheme not available\n }\n return false;\n}\n\nexport async function isWalletAvailable(): Promise<boolean> {\n try {\n if (Platform.OS === \"ios\") {\n return await Linking.canOpenURL(WALLET_URLS.ios.passkit);\n }\n if (Platform.OS === \"android\") {\n return await Linking.canOpenURL(WALLET_URLS.android.googlePay);\n }\n return false;\n } catch {\n return false;\n }\n}\n","import React, {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n ActivityIndicator,\n BackHandler,\n Linking,\n Platform,\n StyleSheet,\n View,\n} from \"react-native\";\nimport { WebView, WebViewMessageEvent } from \"react-native-webview\";\nimport type { WidgetMessage, WidgetSDKConfig } from \"@oobit/core\";\nimport { getWidgetUrl, stripTokenPrefix } from \"@oobit/core\";\nimport { isWalletAvailable, openNativeWallet } from \"../utils/walletUtils\";\n\nexport interface WidgetSDKRef {\n navigateBack: () => void;\n reload: () => void;\n}\n\nexport const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(\n (\n { accessToken, userWalletAddress, onClose, onTransactionRequested },\n ref\n ) => {\n const webViewRef = useRef<WebView>(null);\n const [walletAvailable, setWalletAvailable] = useState(false);\n const [isLoading, setIsLoading] = useState(true);\n const hasLoadedOnce = useRef(false);\n\n useEffect(() => {\n isWalletAvailable().then(setWalletAvailable);\n }, []);\n\n useEffect(() => {\n const backHandler = BackHandler.addEventListener(\n \"hardwareBackPress\",\n () => {\n sendMessageToWidget({ type: \"native:back-pressed\" });\n return true;\n }\n );\n return () => backHandler.remove();\n }, []);\n\n useImperativeHandle(ref, () => ({\n navigateBack: () => sendMessageToWidget({ type: \"native:navigate-back\" }),\n reload: () => {\n setIsLoading(true);\n hasLoadedOnce.current = false;\n webViewRef.current?.reload();\n },\n }));\n\n const handleMessage = (event: WebViewMessageEvent) => {\n try {\n const message: WidgetMessage = JSON.parse(event.nativeEvent.data);\n\n switch (message.type) {\n case \"widget:ready\":\n sendMessageToWidget({\n type: \"native:platform-info\",\n payload: { platform: Platform.OS, walletAvailable },\n });\n break;\n\n case \"widget:open-wallet\":\n openNativeWallet().then((success) => {\n sendMessageToWidget({\n type: \"native:wallet-opened\",\n payload: { success },\n });\n });\n break;\n\n case \"widget:close\":\n onClose?.();\n break;\n\n case \"widget:transaction-requested\":\n onTransactionRequested(message.payload);\n break;\n }\n } catch {\n // Invalid JSON from WebView - ignore\n }\n };\n\n const sendMessageToWidget = (message: unknown) => {\n webViewRef.current?.injectJavaScript(\n `window.postMessage(${JSON.stringify(message)}, '*'); true;`\n );\n };\n\n const finalWidgetUrl = useMemo(() => {\n const baseUrl = getWidgetUrl(accessToken);\n const params = new URLSearchParams({\n token: stripTokenPrefix(accessToken),\n platform: Platform.OS,\n userWalletAddress,\n sdk: 'react-native',\n });\n return `${baseUrl}?${params.toString()}`;\n }, [accessToken, userWalletAddress]);\n\n return (\n <View style={styles.container}>\n <WebView\n ref={webViewRef}\n style={styles.webview}\n source={{ uri: finalWidgetUrl }}\n onMessage={handleMessage}\n onLoadEnd={() => {\n if (!hasLoadedOnce.current) {\n hasLoadedOnce.current = true;\n setIsLoading(false);\n }\n }}\n onShouldStartLoadWithRequest={(request) => {\n const { url } = request;\n if (\n url.startsWith(\"shoebox://\") ||\n url.includes(\"pay.google.com\") ||\n url.includes(\"wallet.google.com\") ||\n url.includes(\"wallet.apple.com\")\n ) {\n Linking.openURL(url).catch(() => {});\n return false;\n }\n return true;\n }}\n onOpenWindow={(syntheticEvent) => {\n const { nativeEvent } = syntheticEvent;\n const { targetUrl } = nativeEvent;\n Linking.openURL(targetUrl).catch(() => {});\n }}\n javaScriptEnabled={true}\n domStorageEnabled={true}\n allowsInlineMediaPlayback={true}\n bounces={false}\n allowsBackForwardNavigationGestures={true}\n mixedContentMode=\"always\"\n />\n {isLoading && (\n <View style={styles.loadingOverlay}>\n <ActivityIndicator size=\"large\" color=\"#007AFF\" />\n </View>\n )}\n </View>\n );\n }\n);\n\nconst styles = StyleSheet.create({\n container: { flex: 1 },\n webview: { flex: 1 },\n loadingOverlay: {\n ...StyleSheet.absoluteFillObject,\n backgroundColor: \"rgba(255, 255, 255, 0.9)\",\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n});\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import React, { forwardRef, useRef, useState, useEffect, useImperativeHandle, useMemo } from 'react';
|
|
2
|
+
import { BackHandler, Platform, View, Linking, ActivityIndicator, StyleSheet, Alert } from 'react-native';
|
|
3
|
+
import { WebView } from 'react-native-webview';
|
|
4
|
+
|
|
5
|
+
// src/components/WidgetSDK.tsx
|
|
6
|
+
|
|
7
|
+
// ../core/dist/index.mjs
|
|
8
|
+
var WALLET_URLS = {
|
|
9
|
+
ios: {
|
|
10
|
+
passkit: "shoebox://",
|
|
11
|
+
fallback: "https://wallet.apple.com"
|
|
12
|
+
},
|
|
13
|
+
android: {
|
|
14
|
+
googlePay: "https://pay.google.com/gp/w/home/wallet",
|
|
15
|
+
fallback: "https://wallet.google.com"
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var TOKEN_PREFIX = {
|
|
19
|
+
SANDBOX: "sbx_",
|
|
20
|
+
PRODUCTION: "prod_"
|
|
21
|
+
};
|
|
22
|
+
var WIDGET_URLS = {
|
|
23
|
+
sandbox: "https://oobit-widget-dev.web.app",
|
|
24
|
+
production: "https://oobit-widget.web.app"
|
|
25
|
+
};
|
|
26
|
+
function getWidgetUrl(accessToken) {
|
|
27
|
+
if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {
|
|
28
|
+
return WIDGET_URLS.sandbox;
|
|
29
|
+
}
|
|
30
|
+
if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {
|
|
31
|
+
return WIDGET_URLS.production;
|
|
32
|
+
}
|
|
33
|
+
return WIDGET_URLS.production;
|
|
34
|
+
}
|
|
35
|
+
function stripTokenPrefix(accessToken) {
|
|
36
|
+
if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {
|
|
37
|
+
return accessToken.slice(TOKEN_PREFIX.SANDBOX.length);
|
|
38
|
+
}
|
|
39
|
+
if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {
|
|
40
|
+
return accessToken.slice(TOKEN_PREFIX.PRODUCTION.length);
|
|
41
|
+
}
|
|
42
|
+
return accessToken;
|
|
43
|
+
}
|
|
44
|
+
var GOOGLE_WALLET_PACKAGE = "com.google.android.apps.walletnfcrel";
|
|
45
|
+
async function openNativeWallet() {
|
|
46
|
+
try {
|
|
47
|
+
if (Platform.OS === "ios") {
|
|
48
|
+
return await openAppleWallet();
|
|
49
|
+
}
|
|
50
|
+
if (Platform.OS === "android") {
|
|
51
|
+
return await openGoogleWallet();
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
} catch {
|
|
55
|
+
Alert.alert(
|
|
56
|
+
"Error",
|
|
57
|
+
"Could not open wallet. Please check if the app is installed."
|
|
58
|
+
);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function openAppleWallet() {
|
|
63
|
+
const { passkit, fallback } = WALLET_URLS.ios;
|
|
64
|
+
if (await tryOpenUrl("wallet://")) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
if (await tryOpenUrl(passkit)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
if (await tryOpenUrl(fallback)) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
throw new Error("Apple Wallet not available");
|
|
74
|
+
}
|
|
75
|
+
async function openGoogleWallet() {
|
|
76
|
+
const launchUrl = `market://launch?id=${GOOGLE_WALLET_PACKAGE}`;
|
|
77
|
+
if (await tryOpenUrl(launchUrl)) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
const playStoreUrl = `market://details?id=${GOOGLE_WALLET_PACKAGE}`;
|
|
81
|
+
const webPlayStoreUrl = `https://play.google.com/store/apps/details?id=${GOOGLE_WALLET_PACKAGE}`;
|
|
82
|
+
const opened = await tryOpenUrl(playStoreUrl) || await tryOpenUrl(webPlayStoreUrl);
|
|
83
|
+
if (opened) {
|
|
84
|
+
Alert.alert(
|
|
85
|
+
"Google Wallet",
|
|
86
|
+
"Google Wallet is not installed. Opening Play Store to install the app."
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
async function tryOpenUrl(url) {
|
|
92
|
+
try {
|
|
93
|
+
const canOpen = await Linking.canOpenURL(url);
|
|
94
|
+
if (canOpen) {
|
|
95
|
+
await Linking.openURL(url);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
async function isWalletAvailable() {
|
|
103
|
+
try {
|
|
104
|
+
if (Platform.OS === "ios") {
|
|
105
|
+
return await Linking.canOpenURL(WALLET_URLS.ios.passkit);
|
|
106
|
+
}
|
|
107
|
+
if (Platform.OS === "android") {
|
|
108
|
+
return await Linking.canOpenURL(WALLET_URLS.android.googlePay);
|
|
109
|
+
}
|
|
110
|
+
return false;
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/components/WidgetSDK.tsx
|
|
117
|
+
var WidgetSDK = forwardRef(
|
|
118
|
+
({ accessToken, userWalletAddress, onClose, onTransactionRequested }, ref) => {
|
|
119
|
+
const webViewRef = useRef(null);
|
|
120
|
+
const [walletAvailable, setWalletAvailable] = useState(false);
|
|
121
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
122
|
+
const hasLoadedOnce = useRef(false);
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
isWalletAvailable().then(setWalletAvailable);
|
|
125
|
+
}, []);
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
const backHandler = BackHandler.addEventListener(
|
|
128
|
+
"hardwareBackPress",
|
|
129
|
+
() => {
|
|
130
|
+
sendMessageToWidget({ type: "native:back-pressed" });
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
return () => backHandler.remove();
|
|
135
|
+
}, []);
|
|
136
|
+
useImperativeHandle(ref, () => ({
|
|
137
|
+
navigateBack: () => sendMessageToWidget({ type: "native:navigate-back" }),
|
|
138
|
+
reload: () => {
|
|
139
|
+
setIsLoading(true);
|
|
140
|
+
hasLoadedOnce.current = false;
|
|
141
|
+
webViewRef.current?.reload();
|
|
142
|
+
}
|
|
143
|
+
}));
|
|
144
|
+
const handleMessage = (event) => {
|
|
145
|
+
try {
|
|
146
|
+
const message = JSON.parse(event.nativeEvent.data);
|
|
147
|
+
switch (message.type) {
|
|
148
|
+
case "widget:ready":
|
|
149
|
+
sendMessageToWidget({
|
|
150
|
+
type: "native:platform-info",
|
|
151
|
+
payload: { platform: Platform.OS, walletAvailable }
|
|
152
|
+
});
|
|
153
|
+
break;
|
|
154
|
+
case "widget:open-wallet":
|
|
155
|
+
openNativeWallet().then((success) => {
|
|
156
|
+
sendMessageToWidget({
|
|
157
|
+
type: "native:wallet-opened",
|
|
158
|
+
payload: { success }
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
break;
|
|
162
|
+
case "widget:close":
|
|
163
|
+
onClose?.();
|
|
164
|
+
break;
|
|
165
|
+
case "widget:transaction-requested":
|
|
166
|
+
onTransactionRequested(message.payload);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const sendMessageToWidget = (message) => {
|
|
173
|
+
webViewRef.current?.injectJavaScript(
|
|
174
|
+
`window.postMessage(${JSON.stringify(message)}, '*'); true;`
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
const finalWidgetUrl = useMemo(() => {
|
|
178
|
+
const baseUrl = getWidgetUrl(accessToken);
|
|
179
|
+
const params = new URLSearchParams({
|
|
180
|
+
token: stripTokenPrefix(accessToken),
|
|
181
|
+
platform: Platform.OS,
|
|
182
|
+
userWalletAddress,
|
|
183
|
+
sdk: "react-native"
|
|
184
|
+
});
|
|
185
|
+
return `${baseUrl}?${params.toString()}`;
|
|
186
|
+
}, [accessToken, userWalletAddress]);
|
|
187
|
+
return /* @__PURE__ */ React.createElement(View, { style: styles.container }, /* @__PURE__ */ React.createElement(
|
|
188
|
+
WebView,
|
|
189
|
+
{
|
|
190
|
+
ref: webViewRef,
|
|
191
|
+
style: styles.webview,
|
|
192
|
+
source: { uri: finalWidgetUrl },
|
|
193
|
+
onMessage: handleMessage,
|
|
194
|
+
onLoadEnd: () => {
|
|
195
|
+
if (!hasLoadedOnce.current) {
|
|
196
|
+
hasLoadedOnce.current = true;
|
|
197
|
+
setIsLoading(false);
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
onShouldStartLoadWithRequest: (request) => {
|
|
201
|
+
const { url } = request;
|
|
202
|
+
if (url.startsWith("shoebox://") || url.includes("pay.google.com") || url.includes("wallet.google.com") || url.includes("wallet.apple.com")) {
|
|
203
|
+
Linking.openURL(url).catch(() => {
|
|
204
|
+
});
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
return true;
|
|
208
|
+
},
|
|
209
|
+
onOpenWindow: (syntheticEvent) => {
|
|
210
|
+
const { nativeEvent } = syntheticEvent;
|
|
211
|
+
const { targetUrl } = nativeEvent;
|
|
212
|
+
Linking.openURL(targetUrl).catch(() => {
|
|
213
|
+
});
|
|
214
|
+
},
|
|
215
|
+
javaScriptEnabled: true,
|
|
216
|
+
domStorageEnabled: true,
|
|
217
|
+
allowsInlineMediaPlayback: true,
|
|
218
|
+
bounces: false,
|
|
219
|
+
allowsBackForwardNavigationGestures: true,
|
|
220
|
+
mixedContentMode: "always"
|
|
221
|
+
}
|
|
222
|
+
), isLoading && /* @__PURE__ */ React.createElement(View, { style: styles.loadingOverlay }, /* @__PURE__ */ React.createElement(ActivityIndicator, { size: "large", color: "#007AFF" })));
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
var styles = StyleSheet.create({
|
|
226
|
+
container: { flex: 1 },
|
|
227
|
+
webview: { flex: 1 },
|
|
228
|
+
loadingOverlay: {
|
|
229
|
+
...StyleSheet.absoluteFillObject,
|
|
230
|
+
backgroundColor: "rgba(255, 255, 255, 0.9)",
|
|
231
|
+
justifyContent: "center",
|
|
232
|
+
alignItems: "center"
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
export { WidgetSDK };
|
|
237
|
+
//# sourceMappingURL=index.mjs.map
|
|
238
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../core/src/types/messages.ts","../../core/src/config/environment.ts","../src/utils/walletUtils.ts","../src/components/WidgetSDK.tsx"],"names":["Platform","Linking"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { TransactionRequest } from './transactions';\n\n/**\n * Internal: Messages from widget\n */\nexport type WidgetMessage =\n | { type: \"widget:ready\" }\n | { type: \"widget:open-wallet\" }\n | { type: \"widget:close\" }\n | { type: \"widget:transaction-requested\"; payload: TransactionRequest };\n\n/**\n * Internal: Wallet URLs\n */\nexport const WALLET_URLS = {\n ios: {\n passkit: \"shoebox://\",\n fallback: \"https://wallet.apple.com\",\n },\n android: {\n googlePay: \"https://pay.google.com/gp/w/home/wallet\",\n fallback: \"https://wallet.google.com\",\n },\n} as const;\n","/**\n * Widget SDK Configuration\n * Environment detection based on access token prefix\n */\n\n/**\n * Token prefixes that determine the environment\n */\nconst TOKEN_PREFIX = {\n SANDBOX: \"sbx_\",\n PRODUCTION: \"prod_\",\n} as const;\n\n/**\n * Widget URLs by environment\n */\nexport const WIDGET_URLS = {\n sandbox: \"https://oobit-widget-dev.web.app\",\n production: \"https://oobit-widget.web.app\",\n} as const;\n\n/**\n * Get the widget URL based on the access token prefix\n * @param accessToken - The access token (prefixed with sbx_ or prod_)\n * @returns The widget URL for the corresponding environment\n */\nexport function getWidgetUrl(accessToken: string): string {\n if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {\n return WIDGET_URLS.sandbox;\n }\n if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {\n return WIDGET_URLS.production;\n }\n // Default to production for backwards compatibility\n return WIDGET_URLS.production;\n}\n\n/**\n * Strip the environment prefix from an access token\n * @param accessToken - The access token (prefixed with sbx_ or prod_)\n * @returns The token without the environment prefix\n */\nexport function stripTokenPrefix(accessToken: string): string {\n if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {\n return accessToken.slice(TOKEN_PREFIX.SANDBOX.length);\n }\n if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {\n return accessToken.slice(TOKEN_PREFIX.PRODUCTION.length);\n }\n return accessToken;\n}\n","import { Alert, Linking, Platform } from \"react-native\";\nimport { WALLET_URLS } from \"@oobit/core\";\n\nconst GOOGLE_WALLET_PACKAGE = \"com.google.android.apps.walletnfcrel\";\n\nexport async function openNativeWallet(): Promise<boolean> {\n try {\n if (Platform.OS === \"ios\") {\n return await openAppleWallet();\n }\n if (Platform.OS === \"android\") {\n return await openGoogleWallet();\n }\n return false;\n } catch {\n Alert.alert(\n \"Error\",\n \"Could not open wallet. Please check if the app is installed.\"\n );\n return false;\n }\n}\n\nasync function openAppleWallet(): Promise<boolean> {\n const { passkit, fallback } = WALLET_URLS.ios;\n\n // Try wallet:// scheme first (documented for recent iOS)\n if (await tryOpenUrl(\"wallet://\")) {\n return true;\n }\n\n // Try shoebox:// scheme (unofficial but widely used)\n if (await tryOpenUrl(passkit)) {\n return true;\n }\n\n // Fallback to wallet.apple.com (opens in browser)\n if (await tryOpenUrl(fallback)) {\n return true;\n }\n\n throw new Error(\"Apple Wallet not available\");\n}\n\nasync function openGoogleWallet(): Promise<boolean> {\n const launchUrl = `market://launch?id=${GOOGLE_WALLET_PACKAGE}`;\n\n // Try launching the app directly\n if (await tryOpenUrl(launchUrl)) {\n return true;\n }\n\n // App not installed - open Play Store\n const playStoreUrl = `market://details?id=${GOOGLE_WALLET_PACKAGE}`;\n const webPlayStoreUrl = `https://play.google.com/store/apps/details?id=${GOOGLE_WALLET_PACKAGE}`;\n\n const opened = await tryOpenUrl(playStoreUrl) || await tryOpenUrl(webPlayStoreUrl);\n\n if (opened) {\n Alert.alert(\n \"Google Wallet\",\n \"Google Wallet is not installed. Opening Play Store to install the app.\"\n );\n }\n\n return false;\n}\n\nasync function tryOpenUrl(url: string): Promise<boolean> {\n try {\n const canOpen = await Linking.canOpenURL(url);\n if (canOpen) {\n await Linking.openURL(url);\n return true;\n }\n } catch {\n // URL scheme not available\n }\n return false;\n}\n\nexport async function isWalletAvailable(): Promise<boolean> {\n try {\n if (Platform.OS === \"ios\") {\n return await Linking.canOpenURL(WALLET_URLS.ios.passkit);\n }\n if (Platform.OS === \"android\") {\n return await Linking.canOpenURL(WALLET_URLS.android.googlePay);\n }\n return false;\n } catch {\n return false;\n }\n}\n","import React, {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n ActivityIndicator,\n BackHandler,\n Linking,\n Platform,\n StyleSheet,\n View,\n} from \"react-native\";\nimport { WebView, WebViewMessageEvent } from \"react-native-webview\";\nimport type { WidgetMessage, WidgetSDKConfig } from \"@oobit/core\";\nimport { getWidgetUrl, stripTokenPrefix } from \"@oobit/core\";\nimport { isWalletAvailable, openNativeWallet } from \"../utils/walletUtils\";\n\nexport interface WidgetSDKRef {\n navigateBack: () => void;\n reload: () => void;\n}\n\nexport const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(\n (\n { accessToken, userWalletAddress, onClose, onTransactionRequested },\n ref\n ) => {\n const webViewRef = useRef<WebView>(null);\n const [walletAvailable, setWalletAvailable] = useState(false);\n const [isLoading, setIsLoading] = useState(true);\n const hasLoadedOnce = useRef(false);\n\n useEffect(() => {\n isWalletAvailable().then(setWalletAvailable);\n }, []);\n\n useEffect(() => {\n const backHandler = BackHandler.addEventListener(\n \"hardwareBackPress\",\n () => {\n sendMessageToWidget({ type: \"native:back-pressed\" });\n return true;\n }\n );\n return () => backHandler.remove();\n }, []);\n\n useImperativeHandle(ref, () => ({\n navigateBack: () => sendMessageToWidget({ type: \"native:navigate-back\" }),\n reload: () => {\n setIsLoading(true);\n hasLoadedOnce.current = false;\n webViewRef.current?.reload();\n },\n }));\n\n const handleMessage = (event: WebViewMessageEvent) => {\n try {\n const message: WidgetMessage = JSON.parse(event.nativeEvent.data);\n\n switch (message.type) {\n case \"widget:ready\":\n sendMessageToWidget({\n type: \"native:platform-info\",\n payload: { platform: Platform.OS, walletAvailable },\n });\n break;\n\n case \"widget:open-wallet\":\n openNativeWallet().then((success) => {\n sendMessageToWidget({\n type: \"native:wallet-opened\",\n payload: { success },\n });\n });\n break;\n\n case \"widget:close\":\n onClose?.();\n break;\n\n case \"widget:transaction-requested\":\n onTransactionRequested(message.payload);\n break;\n }\n } catch {\n // Invalid JSON from WebView - ignore\n }\n };\n\n const sendMessageToWidget = (message: unknown) => {\n webViewRef.current?.injectJavaScript(\n `window.postMessage(${JSON.stringify(message)}, '*'); true;`\n );\n };\n\n const finalWidgetUrl = useMemo(() => {\n const baseUrl = getWidgetUrl(accessToken);\n const params = new URLSearchParams({\n token: stripTokenPrefix(accessToken),\n platform: Platform.OS,\n userWalletAddress,\n sdk: 'react-native',\n });\n return `${baseUrl}?${params.toString()}`;\n }, [accessToken, userWalletAddress]);\n\n return (\n <View style={styles.container}>\n <WebView\n ref={webViewRef}\n style={styles.webview}\n source={{ uri: finalWidgetUrl }}\n onMessage={handleMessage}\n onLoadEnd={() => {\n if (!hasLoadedOnce.current) {\n hasLoadedOnce.current = true;\n setIsLoading(false);\n }\n }}\n onShouldStartLoadWithRequest={(request) => {\n const { url } = request;\n if (\n url.startsWith(\"shoebox://\") ||\n url.includes(\"pay.google.com\") ||\n url.includes(\"wallet.google.com\") ||\n url.includes(\"wallet.apple.com\")\n ) {\n Linking.openURL(url).catch(() => {});\n return false;\n }\n return true;\n }}\n onOpenWindow={(syntheticEvent) => {\n const { nativeEvent } = syntheticEvent;\n const { targetUrl } = nativeEvent;\n Linking.openURL(targetUrl).catch(() => {});\n }}\n javaScriptEnabled={true}\n domStorageEnabled={true}\n allowsInlineMediaPlayback={true}\n bounces={false}\n allowsBackForwardNavigationGestures={true}\n mixedContentMode=\"always\"\n />\n {isLoading && (\n <View style={styles.loadingOverlay}>\n <ActivityIndicator size=\"large\" color=\"#007AFF\" />\n </View>\n )}\n </View>\n );\n }\n);\n\nconst styles = StyleSheet.create({\n container: { flex: 1 },\n webview: { flex: 1 },\n loadingOverlay: {\n ...StyleSheet.absoluteFillObject,\n backgroundColor: \"rgba(255, 255, 255, 0.9)\",\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n});\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oobit/react-native-sdk",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "React Native SDK for integrating Oobit crypto payments into wallet apps",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
},
|
|
36
36
|
"repository": {
|
|
37
37
|
"type": "git",
|
|
38
|
-
"url": "git+https://github.com/oobit-tech/react-native-SDK.git"
|
|
38
|
+
"url": "git+https://github.com/oobit-tech/react-native-SDK.git",
|
|
39
|
+
"directory": "packages/react-native"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
41
42
|
"react": ">=18.0.0",
|
|
@@ -46,26 +47,35 @@
|
|
|
46
47
|
"node-forge": "^1.3.1"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
50
|
+
"@testing-library/react-native": "^12.4.0",
|
|
49
51
|
"@types/node-forge": "^1.3.11",
|
|
50
52
|
"@types/react": "~19.1.0",
|
|
51
|
-
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
|
52
|
-
"@typescript-eslint/parser": "^8.47.0",
|
|
53
|
-
"eslint": "^9.39.1",
|
|
54
53
|
"react": "19.1.0",
|
|
55
54
|
"react-native": "0.81.5",
|
|
56
55
|
"react-native-webview": "13.15.0",
|
|
57
|
-
"
|
|
56
|
+
"tsup": "^8.0.1",
|
|
57
|
+
"typescript": "~5.9.2",
|
|
58
|
+
"vitest": "^1.2.0",
|
|
59
|
+
"@oobit/core": "3.1.0",
|
|
60
|
+
"@oobit/eslint-config": "1.0.0",
|
|
61
|
+
"@oobit/typescript-config": "1.0.0"
|
|
58
62
|
},
|
|
59
63
|
"files": [
|
|
60
64
|
"dist",
|
|
61
65
|
"src",
|
|
62
66
|
"README.md",
|
|
67
|
+
"SDK_ARCHITECTURE.md",
|
|
68
|
+
"SDK_QUICKSTART.md",
|
|
69
|
+
"WIDGET_INTEGRATION.md",
|
|
63
70
|
"LICENSE"
|
|
64
71
|
],
|
|
65
72
|
"scripts": {
|
|
66
|
-
"build": "
|
|
67
|
-
"
|
|
73
|
+
"build": "tsup",
|
|
74
|
+
"dev": "tsup --watch",
|
|
68
75
|
"typecheck": "tsc --noEmit",
|
|
69
|
-
"clean": "rm -rf dist"
|
|
76
|
+
"clean": "rm -rf dist",
|
|
77
|
+
"lint": "eslint src/",
|
|
78
|
+
"test": "vitest run",
|
|
79
|
+
"test:watch": "vitest"
|
|
70
80
|
}
|
|
71
|
-
}
|
|
81
|
+
}
|