@dynamic-labs/react-native-extension 4.20.7 → 4.20.9
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/index.cjs +149 -23
- package/index.js +150 -24
- package/package.json +5 -5
- package/src/ReactNativeExtension/ReactNativeExtension.d.ts +3 -2
- package/src/components/WebView/WebView.d.ts +19 -3
- package/src/components/WebView/constants.d.ts +2 -0
- package/src/components/WebView/useWebViewLoadingTimeout/index.d.ts +1 -0
- package/src/components/WebView/useWebViewLoadingTimeout/useWebViewLoadingTimeout.d.ts +4 -0
- package/src/components/WebView/useWebViewRecoveryTimeout/index.d.ts +1 -0
- package/src/components/WebView/useWebViewRecoveryTimeout/useWebViewRecoveryTimeout.d.ts +11 -0
- package/src/components/WebView/utils/assignEnvironmentIdToUrl/assignEnvironmentIdToUrl.d.ts +1 -0
- package/src/components/WebView/utils/assignEnvironmentIdToUrl/index.d.ts +1 -0
- package/src/components/WebView/utils/hasClearStateInUrl/hasClearStateInUrl.d.ts +1 -0
- package/src/components/WebView/utils/hasClearStateInUrl/index.d.ts +1 -0
- package/src/components/WebView/utils/increaseRetryToUrl/increaseRetryToUrl.d.ts +1 -0
- package/src/components/WebView/utils/increaseRetryToUrl/index.d.ts +1 -0
- package/src/components/WebView/utils/setClearStateToUrl/index.d.ts +1 -0
- package/src/components/WebView/utils/setClearStateToUrl/setClearStateToUrl.d.ts +1 -0
- package/src/components/WebView/useMessageTransportWebViewBridge/useIsMounted/index.d.ts +0 -1
- package/src/components/WebView/useMessageTransportWebViewBridge/useIsMounted/useIsMounted.d.ts +0 -1
package/index.cjs
CHANGED
|
@@ -14,7 +14,7 @@ var expoLinking = require('expo-linking');
|
|
|
14
14
|
var expoWebBrowser = require('expo-web-browser');
|
|
15
15
|
var expoSecureStore = require('expo-secure-store');
|
|
16
16
|
|
|
17
|
-
var version = "4.20.
|
|
17
|
+
var version = "4.20.9";
|
|
18
18
|
|
|
19
19
|
function _extends() {
|
|
20
20
|
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
@@ -28,22 +28,12 @@ function _extends() {
|
|
|
28
28
|
|
|
29
29
|
const logger = new logger$1.Logger('react-native-extension');
|
|
30
30
|
|
|
31
|
-
const useIsMounted = () => {
|
|
32
|
-
const isMountedRef = react.useRef(true);
|
|
33
|
-
react.useEffect(() => () => {
|
|
34
|
-
isMountedRef.current = false;
|
|
35
|
-
}, []);
|
|
36
|
-
return react.useCallback(() => isMountedRef.current, [isMountedRef]);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
31
|
const useMessageTransportWebViewBridge = (core, webViewRef) => {
|
|
40
|
-
const canBroadcastMessages = useIsMounted();
|
|
41
32
|
/**
|
|
42
33
|
* Receive a message from the webview and forward it to the client
|
|
43
34
|
* message transport
|
|
44
35
|
*/
|
|
45
36
|
const onMessageHandler = event => {
|
|
46
|
-
if (!canBroadcastMessages()) return;
|
|
47
37
|
let parsedData = null;
|
|
48
38
|
try {
|
|
49
39
|
parsedData = JSON.parse(event.nativeEvent.data, (_, value) => {
|
|
@@ -73,7 +63,10 @@ const useMessageTransportWebViewBridge = (core, webViewRef) => {
|
|
|
73
63
|
*/
|
|
74
64
|
react.useEffect(() => {
|
|
75
65
|
const sendMessageToWebView = message => {
|
|
76
|
-
if (!
|
|
66
|
+
if (!webViewRef.current) {
|
|
67
|
+
logger.debug('WebView ref not found');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
77
70
|
/**
|
|
78
71
|
* Only forward messages to the webview
|
|
79
72
|
* that where created by the client/host
|
|
@@ -86,7 +79,7 @@ const useMessageTransportWebViewBridge = (core, webViewRef) => {
|
|
|
86
79
|
return () => {
|
|
87
80
|
core.messageTransport.off(sendMessageToWebView);
|
|
88
81
|
};
|
|
89
|
-
}, [core.messageTransport, webViewRef
|
|
82
|
+
}, [core.messageTransport, webViewRef]);
|
|
90
83
|
return {
|
|
91
84
|
onMessageHandler
|
|
92
85
|
};
|
|
@@ -128,15 +121,123 @@ const styles = reactNative.StyleSheet.create({
|
|
|
128
121
|
}
|
|
129
122
|
});
|
|
130
123
|
|
|
124
|
+
const CLEAR_STATE_PARAM = 'clear-state';
|
|
125
|
+
const ENVIRONMENT_ID_PARAM = 'environmentId';
|
|
126
|
+
|
|
127
|
+
const setClearStateToUrl = webViewUrl => {
|
|
128
|
+
const newWebViewUrl = new URL(webViewUrl);
|
|
129
|
+
newWebViewUrl.searchParams.set(CLEAR_STATE_PARAM, 'true');
|
|
130
|
+
newWebViewUrl.searchParams.delete('retry');
|
|
131
|
+
return newWebViewUrl;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const hasClearStateInUrl = webViewUrl => Boolean(webViewUrl.searchParams.get(CLEAR_STATE_PARAM));
|
|
135
|
+
|
|
136
|
+
const useWebViewRecoveryTimeout = ({
|
|
137
|
+
core,
|
|
138
|
+
webViewUrl,
|
|
139
|
+
disableRecovery,
|
|
140
|
+
recoveryTimeout,
|
|
141
|
+
setWebViewUrl
|
|
142
|
+
}) => {
|
|
143
|
+
const timeoutRef = react.useRef(null);
|
|
144
|
+
/**
|
|
145
|
+
* Clear timeout on unmount
|
|
146
|
+
*/
|
|
147
|
+
react.useEffect(() => () => {
|
|
148
|
+
if (timeoutRef.current) {
|
|
149
|
+
clearTimeout(timeoutRef.current);
|
|
150
|
+
timeoutRef.current = null;
|
|
151
|
+
}
|
|
152
|
+
}, []);
|
|
153
|
+
return react.useCallback(() => {
|
|
154
|
+
if (disableRecovery) {
|
|
155
|
+
logger.debug('timeout recovery: is disabled');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (hasClearStateInUrl(webViewUrl)) {
|
|
159
|
+
logger.debug('timeout recovery: disabled because webview already cleared its state', webViewUrl.toString());
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
logger.debug('timeout recovery: registering timeout', webViewUrl.toString());
|
|
163
|
+
if (timeoutRef.current) {
|
|
164
|
+
clearTimeout(timeoutRef.current);
|
|
165
|
+
}
|
|
166
|
+
timeoutRef.current = setTimeout(() => {
|
|
167
|
+
if (!core.messageTransport.isBlocked()) {
|
|
168
|
+
logger.debug('timeout recovery: skip the timeout because the webview did load');
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
core.messageTransport.block();
|
|
172
|
+
// Increase the retry
|
|
173
|
+
setWebViewUrl(webViewUrl => {
|
|
174
|
+
const newWebViewUrl = setClearStateToUrl(webViewUrl);
|
|
175
|
+
logger.debug('timeout recovery: reloading webview to clear its state', newWebViewUrl.toString());
|
|
176
|
+
return newWebViewUrl;
|
|
177
|
+
});
|
|
178
|
+
}, recoveryTimeout);
|
|
179
|
+
}, [core, disableRecovery, recoveryTimeout, setWebViewUrl, webViewUrl]);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const increaseRetryToUrl = webViewUrl => {
|
|
183
|
+
var _a;
|
|
184
|
+
const currentRetry = Number((_a = webViewUrl.searchParams.get('retry')) !== null && _a !== void 0 ? _a : '0');
|
|
185
|
+
const newWebViewUrl = new URL(webViewUrl);
|
|
186
|
+
newWebViewUrl.searchParams.delete(CLEAR_STATE_PARAM);
|
|
187
|
+
newWebViewUrl.searchParams.set('retry', String(currentRetry + 1));
|
|
188
|
+
return newWebViewUrl;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const useWebViewLoadingTimeout = (timeout, onTimeout) => {
|
|
192
|
+
const timeoutRef = react.useRef(null);
|
|
193
|
+
const onTimeoutRef = react.useRef(onTimeout);
|
|
194
|
+
onTimeoutRef.current = onTimeout;
|
|
195
|
+
const onLoadStart = react.useCallback(() => {
|
|
196
|
+
if (timeoutRef.current) {
|
|
197
|
+
clearTimeout(timeoutRef.current);
|
|
198
|
+
}
|
|
199
|
+
timeoutRef.current = setTimeout(() => {
|
|
200
|
+
onTimeoutRef.current();
|
|
201
|
+
}, timeout);
|
|
202
|
+
}, [timeout]);
|
|
203
|
+
const onLoad = react.useCallback(() => {
|
|
204
|
+
if (timeoutRef.current) {
|
|
205
|
+
clearTimeout(timeoutRef.current);
|
|
206
|
+
timeoutRef.current = null;
|
|
207
|
+
}
|
|
208
|
+
}, []);
|
|
209
|
+
react.useEffect(() => () => {
|
|
210
|
+
if (timeoutRef.current) {
|
|
211
|
+
clearTimeout(timeoutRef.current);
|
|
212
|
+
timeoutRef.current = null;
|
|
213
|
+
}
|
|
214
|
+
}, []);
|
|
215
|
+
return {
|
|
216
|
+
onLoad,
|
|
217
|
+
onLoadStart
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const assignEnvironmentIdToUrl = (url, environmentId) => {
|
|
222
|
+
const _url = new URL(url);
|
|
223
|
+
_url.searchParams.set(ENVIRONMENT_ID_PARAM, environmentId);
|
|
224
|
+
return _url;
|
|
225
|
+
};
|
|
226
|
+
|
|
131
227
|
const WebView = ({
|
|
132
|
-
webviewUrl,
|
|
228
|
+
webviewUrl: initialWebViewUrl,
|
|
133
229
|
core,
|
|
134
|
-
webviewDebuggingEnabled: _webviewDebuggingEnabled = false
|
|
230
|
+
webviewDebuggingEnabled: _webviewDebuggingEnabled = false,
|
|
231
|
+
disableRecovery: _disableRecovery = false,
|
|
232
|
+
recoveryTimeout: _recoveryTimeout = 8000,
|
|
233
|
+
loadingTimeout: _loadingTimeout = 10000
|
|
135
234
|
}) => {
|
|
136
235
|
const webViewRef = react.useRef(null);
|
|
137
236
|
const {
|
|
138
237
|
visible
|
|
139
238
|
} = useWebViewVisibility(core);
|
|
239
|
+
const [webViewUrl, setWebViewUrl] = react.useState(assignEnvironmentIdToUrl(initialWebViewUrl, core.environmentId));
|
|
240
|
+
const webViewKey = react.useMemo(() => webViewUrl.toString(), [webViewUrl]);
|
|
140
241
|
const {
|
|
141
242
|
onMessageHandler
|
|
142
243
|
} = useMessageTransportWebViewBridge(core, webViewRef);
|
|
@@ -151,7 +252,6 @@ const WebView = ({
|
|
|
151
252
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- we only care about the unmount callback
|
|
152
253
|
[]);
|
|
153
254
|
const blockAndReloadWebView = react.useCallback(() => {
|
|
154
|
-
var _a;
|
|
155
255
|
/**
|
|
156
256
|
* Should not reload the webview if it is already blocked
|
|
157
257
|
* and loading a new page
|
|
@@ -168,26 +268,52 @@ const WebView = ({
|
|
|
168
268
|
* the webview is reloading
|
|
169
269
|
*/
|
|
170
270
|
core.messageTransport.block();
|
|
171
|
-
|
|
172
|
-
|
|
271
|
+
// Increase the retry
|
|
272
|
+
setWebViewUrl(webViewUrl => {
|
|
273
|
+
const newWebViewUrl = increaseRetryToUrl(webViewUrl);
|
|
274
|
+
logger.debug('Reloading webview', newWebViewUrl.toString());
|
|
275
|
+
return newWebViewUrl;
|
|
276
|
+
});
|
|
277
|
+
}, [core]);
|
|
173
278
|
react.useEffect(() => core.messageTransport.recoveryManager.onRecoveryRequested(blockAndReloadWebView), [core, blockAndReloadWebView]);
|
|
279
|
+
/**
|
|
280
|
+
* Reload the webview with a clean state when a timeout is reached
|
|
281
|
+
* and the webview did not get to the loaded state yet
|
|
282
|
+
*/
|
|
283
|
+
const startRecoveryTimeout = useWebViewRecoveryTimeout({
|
|
284
|
+
core,
|
|
285
|
+
disableRecovery: _disableRecovery,
|
|
286
|
+
recoveryTimeout: _recoveryTimeout,
|
|
287
|
+
setWebViewUrl,
|
|
288
|
+
webViewUrl
|
|
289
|
+
});
|
|
290
|
+
const setWebViewLoadError = react.useCallback(() => {
|
|
291
|
+
core.initialization.error = new Error('Could not load Dynamic WebView');
|
|
292
|
+
}, [core]);
|
|
293
|
+
const {
|
|
294
|
+
onLoad,
|
|
295
|
+
onLoadStart
|
|
296
|
+
} = useWebViewLoadingTimeout(_loadingTimeout, setWebViewLoadError);
|
|
174
297
|
return /*#__PURE__*/jsxRuntime.jsx(reactNativeWebview.WebView, {
|
|
175
298
|
ref: webViewRef,
|
|
176
299
|
source: {
|
|
177
|
-
uri:
|
|
300
|
+
uri: webViewUrl.toString()
|
|
178
301
|
},
|
|
179
302
|
containerStyle: containerStyles,
|
|
180
303
|
style: styles['webview'],
|
|
181
304
|
onMessage: onMessageHandler,
|
|
305
|
+
onLoadEnd: () => startRecoveryTimeout(),
|
|
306
|
+
onLoadStart: onLoadStart,
|
|
307
|
+
onLoad: onLoad,
|
|
182
308
|
hideKeyboardAccessoryView: true,
|
|
183
309
|
webviewDebuggingEnabled: _webviewDebuggingEnabled,
|
|
184
310
|
onContentProcessDidTerminate: blockAndReloadWebView,
|
|
185
311
|
onRenderProcessGone: blockAndReloadWebView,
|
|
186
|
-
onError: () =>
|
|
187
|
-
},
|
|
312
|
+
onError: () => setWebViewLoadError()
|
|
313
|
+
}, webViewKey);
|
|
188
314
|
};
|
|
189
|
-
const createWebView =
|
|
190
|
-
const WebViewWrapper =
|
|
315
|
+
const createWebView = internalProps => {
|
|
316
|
+
const WebViewWrapper = props => /*#__PURE__*/jsxRuntime.jsx(WebView, _extends({}, internalProps, props));
|
|
191
317
|
return WebViewWrapper;
|
|
192
318
|
};
|
|
193
319
|
const unmountWebViewWarningMessage = `WebView unmounted.
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assertPackageVersion } from '@dynamic-labs/assert-package-version';
|
|
2
2
|
import { StyleSheet, Platform } from 'react-native';
|
|
3
|
-
import { useRef,
|
|
3
|
+
import { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
|
4
4
|
import { WebView as WebView$1 } from 'react-native-webview';
|
|
5
5
|
import { Logger } from '@dynamic-labs/logger';
|
|
6
6
|
import { parseMessageTransportData, createRequestChannel } from '@dynamic-labs/message-transport';
|
|
@@ -10,7 +10,7 @@ import { createURL, openURL } from 'expo-linking';
|
|
|
10
10
|
import { openAuthSessionAsync } from 'expo-web-browser';
|
|
11
11
|
import { getItemAsync, deleteItemAsync, setItemAsync } from 'expo-secure-store';
|
|
12
12
|
|
|
13
|
-
var version = "4.20.
|
|
13
|
+
var version = "4.20.9";
|
|
14
14
|
|
|
15
15
|
function _extends() {
|
|
16
16
|
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
@@ -24,22 +24,12 @@ function _extends() {
|
|
|
24
24
|
|
|
25
25
|
const logger = new Logger('react-native-extension');
|
|
26
26
|
|
|
27
|
-
const useIsMounted = () => {
|
|
28
|
-
const isMountedRef = useRef(true);
|
|
29
|
-
useEffect(() => () => {
|
|
30
|
-
isMountedRef.current = false;
|
|
31
|
-
}, []);
|
|
32
|
-
return useCallback(() => isMountedRef.current, [isMountedRef]);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
27
|
const useMessageTransportWebViewBridge = (core, webViewRef) => {
|
|
36
|
-
const canBroadcastMessages = useIsMounted();
|
|
37
28
|
/**
|
|
38
29
|
* Receive a message from the webview and forward it to the client
|
|
39
30
|
* message transport
|
|
40
31
|
*/
|
|
41
32
|
const onMessageHandler = event => {
|
|
42
|
-
if (!canBroadcastMessages()) return;
|
|
43
33
|
let parsedData = null;
|
|
44
34
|
try {
|
|
45
35
|
parsedData = JSON.parse(event.nativeEvent.data, (_, value) => {
|
|
@@ -69,7 +59,10 @@ const useMessageTransportWebViewBridge = (core, webViewRef) => {
|
|
|
69
59
|
*/
|
|
70
60
|
useEffect(() => {
|
|
71
61
|
const sendMessageToWebView = message => {
|
|
72
|
-
if (!
|
|
62
|
+
if (!webViewRef.current) {
|
|
63
|
+
logger.debug('WebView ref not found');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
73
66
|
/**
|
|
74
67
|
* Only forward messages to the webview
|
|
75
68
|
* that where created by the client/host
|
|
@@ -82,7 +75,7 @@ const useMessageTransportWebViewBridge = (core, webViewRef) => {
|
|
|
82
75
|
return () => {
|
|
83
76
|
core.messageTransport.off(sendMessageToWebView);
|
|
84
77
|
};
|
|
85
|
-
}, [core.messageTransport, webViewRef
|
|
78
|
+
}, [core.messageTransport, webViewRef]);
|
|
86
79
|
return {
|
|
87
80
|
onMessageHandler
|
|
88
81
|
};
|
|
@@ -124,15 +117,123 @@ const styles = StyleSheet.create({
|
|
|
124
117
|
}
|
|
125
118
|
});
|
|
126
119
|
|
|
120
|
+
const CLEAR_STATE_PARAM = 'clear-state';
|
|
121
|
+
const ENVIRONMENT_ID_PARAM = 'environmentId';
|
|
122
|
+
|
|
123
|
+
const setClearStateToUrl = webViewUrl => {
|
|
124
|
+
const newWebViewUrl = new URL(webViewUrl);
|
|
125
|
+
newWebViewUrl.searchParams.set(CLEAR_STATE_PARAM, 'true');
|
|
126
|
+
newWebViewUrl.searchParams.delete('retry');
|
|
127
|
+
return newWebViewUrl;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const hasClearStateInUrl = webViewUrl => Boolean(webViewUrl.searchParams.get(CLEAR_STATE_PARAM));
|
|
131
|
+
|
|
132
|
+
const useWebViewRecoveryTimeout = ({
|
|
133
|
+
core,
|
|
134
|
+
webViewUrl,
|
|
135
|
+
disableRecovery,
|
|
136
|
+
recoveryTimeout,
|
|
137
|
+
setWebViewUrl
|
|
138
|
+
}) => {
|
|
139
|
+
const timeoutRef = useRef(null);
|
|
140
|
+
/**
|
|
141
|
+
* Clear timeout on unmount
|
|
142
|
+
*/
|
|
143
|
+
useEffect(() => () => {
|
|
144
|
+
if (timeoutRef.current) {
|
|
145
|
+
clearTimeout(timeoutRef.current);
|
|
146
|
+
timeoutRef.current = null;
|
|
147
|
+
}
|
|
148
|
+
}, []);
|
|
149
|
+
return useCallback(() => {
|
|
150
|
+
if (disableRecovery) {
|
|
151
|
+
logger.debug('timeout recovery: is disabled');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (hasClearStateInUrl(webViewUrl)) {
|
|
155
|
+
logger.debug('timeout recovery: disabled because webview already cleared its state', webViewUrl.toString());
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
logger.debug('timeout recovery: registering timeout', webViewUrl.toString());
|
|
159
|
+
if (timeoutRef.current) {
|
|
160
|
+
clearTimeout(timeoutRef.current);
|
|
161
|
+
}
|
|
162
|
+
timeoutRef.current = setTimeout(() => {
|
|
163
|
+
if (!core.messageTransport.isBlocked()) {
|
|
164
|
+
logger.debug('timeout recovery: skip the timeout because the webview did load');
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
core.messageTransport.block();
|
|
168
|
+
// Increase the retry
|
|
169
|
+
setWebViewUrl(webViewUrl => {
|
|
170
|
+
const newWebViewUrl = setClearStateToUrl(webViewUrl);
|
|
171
|
+
logger.debug('timeout recovery: reloading webview to clear its state', newWebViewUrl.toString());
|
|
172
|
+
return newWebViewUrl;
|
|
173
|
+
});
|
|
174
|
+
}, recoveryTimeout);
|
|
175
|
+
}, [core, disableRecovery, recoveryTimeout, setWebViewUrl, webViewUrl]);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const increaseRetryToUrl = webViewUrl => {
|
|
179
|
+
var _a;
|
|
180
|
+
const currentRetry = Number((_a = webViewUrl.searchParams.get('retry')) !== null && _a !== void 0 ? _a : '0');
|
|
181
|
+
const newWebViewUrl = new URL(webViewUrl);
|
|
182
|
+
newWebViewUrl.searchParams.delete(CLEAR_STATE_PARAM);
|
|
183
|
+
newWebViewUrl.searchParams.set('retry', String(currentRetry + 1));
|
|
184
|
+
return newWebViewUrl;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const useWebViewLoadingTimeout = (timeout, onTimeout) => {
|
|
188
|
+
const timeoutRef = useRef(null);
|
|
189
|
+
const onTimeoutRef = useRef(onTimeout);
|
|
190
|
+
onTimeoutRef.current = onTimeout;
|
|
191
|
+
const onLoadStart = useCallback(() => {
|
|
192
|
+
if (timeoutRef.current) {
|
|
193
|
+
clearTimeout(timeoutRef.current);
|
|
194
|
+
}
|
|
195
|
+
timeoutRef.current = setTimeout(() => {
|
|
196
|
+
onTimeoutRef.current();
|
|
197
|
+
}, timeout);
|
|
198
|
+
}, [timeout]);
|
|
199
|
+
const onLoad = useCallback(() => {
|
|
200
|
+
if (timeoutRef.current) {
|
|
201
|
+
clearTimeout(timeoutRef.current);
|
|
202
|
+
timeoutRef.current = null;
|
|
203
|
+
}
|
|
204
|
+
}, []);
|
|
205
|
+
useEffect(() => () => {
|
|
206
|
+
if (timeoutRef.current) {
|
|
207
|
+
clearTimeout(timeoutRef.current);
|
|
208
|
+
timeoutRef.current = null;
|
|
209
|
+
}
|
|
210
|
+
}, []);
|
|
211
|
+
return {
|
|
212
|
+
onLoad,
|
|
213
|
+
onLoadStart
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const assignEnvironmentIdToUrl = (url, environmentId) => {
|
|
218
|
+
const _url = new URL(url);
|
|
219
|
+
_url.searchParams.set(ENVIRONMENT_ID_PARAM, environmentId);
|
|
220
|
+
return _url;
|
|
221
|
+
};
|
|
222
|
+
|
|
127
223
|
const WebView = ({
|
|
128
|
-
webviewUrl,
|
|
224
|
+
webviewUrl: initialWebViewUrl,
|
|
129
225
|
core,
|
|
130
|
-
webviewDebuggingEnabled: _webviewDebuggingEnabled = false
|
|
226
|
+
webviewDebuggingEnabled: _webviewDebuggingEnabled = false,
|
|
227
|
+
disableRecovery: _disableRecovery = false,
|
|
228
|
+
recoveryTimeout: _recoveryTimeout = 8000,
|
|
229
|
+
loadingTimeout: _loadingTimeout = 10000
|
|
131
230
|
}) => {
|
|
132
231
|
const webViewRef = useRef(null);
|
|
133
232
|
const {
|
|
134
233
|
visible
|
|
135
234
|
} = useWebViewVisibility(core);
|
|
235
|
+
const [webViewUrl, setWebViewUrl] = useState(assignEnvironmentIdToUrl(initialWebViewUrl, core.environmentId));
|
|
236
|
+
const webViewKey = useMemo(() => webViewUrl.toString(), [webViewUrl]);
|
|
136
237
|
const {
|
|
137
238
|
onMessageHandler
|
|
138
239
|
} = useMessageTransportWebViewBridge(core, webViewRef);
|
|
@@ -147,7 +248,6 @@ const WebView = ({
|
|
|
147
248
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- we only care about the unmount callback
|
|
148
249
|
[]);
|
|
149
250
|
const blockAndReloadWebView = useCallback(() => {
|
|
150
|
-
var _a;
|
|
151
251
|
/**
|
|
152
252
|
* Should not reload the webview if it is already blocked
|
|
153
253
|
* and loading a new page
|
|
@@ -164,26 +264,52 @@ const WebView = ({
|
|
|
164
264
|
* the webview is reloading
|
|
165
265
|
*/
|
|
166
266
|
core.messageTransport.block();
|
|
167
|
-
|
|
168
|
-
|
|
267
|
+
// Increase the retry
|
|
268
|
+
setWebViewUrl(webViewUrl => {
|
|
269
|
+
const newWebViewUrl = increaseRetryToUrl(webViewUrl);
|
|
270
|
+
logger.debug('Reloading webview', newWebViewUrl.toString());
|
|
271
|
+
return newWebViewUrl;
|
|
272
|
+
});
|
|
273
|
+
}, [core]);
|
|
169
274
|
useEffect(() => core.messageTransport.recoveryManager.onRecoveryRequested(blockAndReloadWebView), [core, blockAndReloadWebView]);
|
|
275
|
+
/**
|
|
276
|
+
* Reload the webview with a clean state when a timeout is reached
|
|
277
|
+
* and the webview did not get to the loaded state yet
|
|
278
|
+
*/
|
|
279
|
+
const startRecoveryTimeout = useWebViewRecoveryTimeout({
|
|
280
|
+
core,
|
|
281
|
+
disableRecovery: _disableRecovery,
|
|
282
|
+
recoveryTimeout: _recoveryTimeout,
|
|
283
|
+
setWebViewUrl,
|
|
284
|
+
webViewUrl
|
|
285
|
+
});
|
|
286
|
+
const setWebViewLoadError = useCallback(() => {
|
|
287
|
+
core.initialization.error = new Error('Could not load Dynamic WebView');
|
|
288
|
+
}, [core]);
|
|
289
|
+
const {
|
|
290
|
+
onLoad,
|
|
291
|
+
onLoadStart
|
|
292
|
+
} = useWebViewLoadingTimeout(_loadingTimeout, setWebViewLoadError);
|
|
170
293
|
return /*#__PURE__*/jsx(WebView$1, {
|
|
171
294
|
ref: webViewRef,
|
|
172
295
|
source: {
|
|
173
|
-
uri:
|
|
296
|
+
uri: webViewUrl.toString()
|
|
174
297
|
},
|
|
175
298
|
containerStyle: containerStyles,
|
|
176
299
|
style: styles['webview'],
|
|
177
300
|
onMessage: onMessageHandler,
|
|
301
|
+
onLoadEnd: () => startRecoveryTimeout(),
|
|
302
|
+
onLoadStart: onLoadStart,
|
|
303
|
+
onLoad: onLoad,
|
|
178
304
|
hideKeyboardAccessoryView: true,
|
|
179
305
|
webviewDebuggingEnabled: _webviewDebuggingEnabled,
|
|
180
306
|
onContentProcessDidTerminate: blockAndReloadWebView,
|
|
181
307
|
onRenderProcessGone: blockAndReloadWebView,
|
|
182
|
-
onError: () =>
|
|
183
|
-
},
|
|
308
|
+
onError: () => setWebViewLoadError()
|
|
309
|
+
}, webViewKey);
|
|
184
310
|
};
|
|
185
|
-
const createWebView =
|
|
186
|
-
const WebViewWrapper =
|
|
311
|
+
const createWebView = internalProps => {
|
|
312
|
+
const WebViewWrapper = props => /*#__PURE__*/jsx(WebView, _extends({}, internalProps, props));
|
|
187
313
|
return WebViewWrapper;
|
|
188
314
|
};
|
|
189
315
|
const unmountWebViewWarningMessage = `WebView unmounted.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs/react-native-extension",
|
|
3
|
-
"version": "4.20.
|
|
3
|
+
"version": "4.20.9",
|
|
4
4
|
"main": "./index.cjs",
|
|
5
5
|
"module": "./index.js",
|
|
6
6
|
"types": "./src/index.d.ts",
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@turnkey/react-native-passkey-stamper": "0.2.15",
|
|
18
|
-
"@dynamic-labs/assert-package-version": "4.20.
|
|
19
|
-
"@dynamic-labs/client": "4.20.
|
|
20
|
-
"@dynamic-labs/logger": "4.20.
|
|
21
|
-
"@dynamic-labs/message-transport": "4.20.
|
|
18
|
+
"@dynamic-labs/assert-package-version": "4.20.9",
|
|
19
|
+
"@dynamic-labs/client": "4.20.9",
|
|
20
|
+
"@dynamic-labs/logger": "4.20.9",
|
|
21
|
+
"@dynamic-labs/message-transport": "4.20.9"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"react": ">=18.0.0 <20.0.0",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { FC } from 'react';
|
|
2
2
|
import { Extension } from '@dynamic-labs/client';
|
|
3
|
+
import { WebViewProps } from '../components/WebView';
|
|
3
4
|
export type ReactNativeExtensionProps = {
|
|
4
5
|
/**
|
|
5
6
|
* The URL to load in the WebView.
|
|
@@ -16,7 +17,7 @@ export type ReactNativeExtensionProps = {
|
|
|
16
17
|
};
|
|
17
18
|
export type IReactNativeExtension = {
|
|
18
19
|
reactNative: {
|
|
19
|
-
WebView: () =>
|
|
20
|
+
WebView: FC<WebViewProps> | (() => null);
|
|
20
21
|
};
|
|
21
22
|
};
|
|
22
23
|
export declare const ReactNativeExtension: ({ webviewUrl, webviewDebuggingEnabled, appOrigin, }?: ReactNativeExtensionProps) => Extension<IReactNativeExtension>;
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import { FC } from 'react';
|
|
2
2
|
import { Core } from '@dynamic-labs/client';
|
|
3
|
-
type WebViewProps = {
|
|
3
|
+
export type WebViewProps = {
|
|
4
|
+
/**
|
|
5
|
+
* Disable the webview recovery
|
|
6
|
+
*/
|
|
7
|
+
disableRecovery?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* The timeout between the page loaded event and the SDK
|
|
10
|
+
* has loaded event
|
|
11
|
+
*/
|
|
12
|
+
recoveryTimeout?: number;
|
|
13
|
+
/**
|
|
14
|
+
* The timeout between the webview page load start event and the
|
|
15
|
+
* page html and assets finishes loading
|
|
16
|
+
*/
|
|
17
|
+
loadingTimeout?: number;
|
|
18
|
+
};
|
|
19
|
+
type WebViewInternalProps = WebViewProps & {
|
|
4
20
|
webviewUrl: string;
|
|
5
21
|
core: Core;
|
|
6
22
|
webviewDebuggingEnabled?: boolean;
|
|
7
23
|
};
|
|
8
|
-
export declare const WebView: FC<
|
|
9
|
-
export declare const createWebView: (
|
|
24
|
+
export declare const WebView: FC<WebViewInternalProps>;
|
|
25
|
+
export declare const createWebView: (internalProps: WebViewInternalProps) => FC<WebViewProps>;
|
|
10
26
|
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useWebViewLoadingTimeout } from './useWebViewLoadingTimeout';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useWebViewRecoveryTimeout } from './useWebViewRecoveryTimeout';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
import { Core } from '@dynamic-labs/client';
|
|
3
|
+
type UseWebViewRecoveryTimeoutProps = {
|
|
4
|
+
core: Core;
|
|
5
|
+
webViewUrl: URL;
|
|
6
|
+
disableRecovery: boolean;
|
|
7
|
+
recoveryTimeout: number;
|
|
8
|
+
setWebViewUrl: Dispatch<SetStateAction<URL>>;
|
|
9
|
+
};
|
|
10
|
+
export declare const useWebViewRecoveryTimeout: ({ core, webViewUrl, disableRecovery, recoveryTimeout, setWebViewUrl, }: UseWebViewRecoveryTimeoutProps) => () => void;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const assignEnvironmentIdToUrl: (url: string, environmentId: string) => URL;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { assignEnvironmentIdToUrl } from './assignEnvironmentIdToUrl';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const hasClearStateInUrl: (webViewUrl: URL) => boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { hasClearStateInUrl } from './hasClearStateInUrl';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const increaseRetryToUrl: (webViewUrl: URL) => URL;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { increaseRetryToUrl } from './increaseRetryToUrl';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { setClearStateToUrl } from './setClearStateToUrl';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const setClearStateToUrl: (webViewUrl: URL) => URL;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useIsMounted } from './useIsMounted';
|
package/src/components/WebView/useMessageTransportWebViewBridge/useIsMounted/useIsMounted.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const useIsMounted: () => () => boolean;
|