@glomopay/react-native-sdk 1.3.5 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/lib/config/base.d.ts +14 -5
- package/lib/config/base.d.ts.map +1 -1
- package/lib/config/base.js +22 -6
- package/lib/glomo-lrs-checkout.d.ts.map +1 -1
- package/lib/glomo-lrs-checkout.js +137 -11
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/injections/index.d.ts +1 -0
- package/lib/injections/index.d.ts.map +1 -1
- package/lib/injections/index.js +2 -0
- package/lib/injections/webview-education-carousel.injection.d.ts +3 -0
- package/lib/injections/webview-education-carousel.injection.d.ts.map +1 -0
- package/lib/injections/webview-education-carousel.injection.js +157 -0
- package/lib/injections/webview-flow.injection.d.ts.map +1 -1
- package/lib/injections/webview-flow.injection.js +3 -5
- package/lib/injections/webview-main.injection.d.ts.map +1 -1
- package/lib/injections/webview-main.injection.js +3 -5
- package/lib/use-lrs-checkout.d.ts +13 -1
- package/lib/use-lrs-checkout.d.ts.map +1 -1
- package/lib/use-lrs-checkout.js +114 -8
- package/lib/utils/analytics.d.ts +11 -0
- package/lib/utils/analytics.d.ts.map +1 -1
- package/lib/utils/analytics.js +22 -0
- package/package.json +1 -1
- package/src/config/base.ts +35 -5
- package/src/glomo-lrs-checkout.tsx +186 -12
- package/src/index.ts +1 -0
- package/src/injections/index.ts +2 -0
- package/src/injections/webview-education-carousel.injection.ts +154 -0
- package/src/injections/webview-flow.injection.ts +3 -5
- package/src/injections/webview-main.injection.ts +3 -5
- package/src/use-lrs-checkout.tsx +149 -8
- package/src/utils/analytics.ts +29 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** The GlomoPay LRS Checkout Component */
|
|
2
2
|
|
|
3
3
|
import React, { useMemo, useImperativeHandle, forwardRef } from "react";
|
|
4
|
-
import { StyleSheet, View, Modal, Platform, SafeAreaView,
|
|
5
|
-
import { WebView, type WebViewMessageEvent } from "react-native-webview";
|
|
4
|
+
import { StyleSheet, View, Modal, Platform, SafeAreaView, TouchableOpacity, Text } from "react-native";
|
|
5
|
+
import { WebView, type WebViewMessageEvent, type WebViewProps } from "react-native-webview";
|
|
6
6
|
|
|
7
7
|
import { useLrsCheckout, type UseLrsCheckoutOptions, type LrsCheckoutStatus } from "./use-lrs-checkout";
|
|
8
8
|
import { type SdkError } from "./utils/analytics";
|
|
@@ -21,7 +21,7 @@ export interface GlomoLrsCheckoutRef {
|
|
|
21
21
|
/**
|
|
22
22
|
* The Main GlomoPay LRS Checkout Component
|
|
23
23
|
*
|
|
24
|
-
* @param server - Optional. The
|
|
24
|
+
* @param server - Optional. The SDK server environment. For development builds ONLY.
|
|
25
25
|
* @param publicKey - The public key for the merchant. If it starts with "test_", mock mode will be enabled automatically. Otherwise, live mode is used.
|
|
26
26
|
* @param orderId - The order ID. Always starts with "order_"
|
|
27
27
|
*
|
|
@@ -54,10 +54,13 @@ function GlomoLrsCheckoutComponent(
|
|
|
54
54
|
) {
|
|
55
55
|
// Computing mockMode based on the publicKey prefix
|
|
56
56
|
const mockMode = useMemo(() => {
|
|
57
|
+
const resolvedMockMode = publicKey?.toLowerCase().startsWith("test_") ?? false;
|
|
57
58
|
if (devMode) {
|
|
58
|
-
console.log(
|
|
59
|
+
console.log(
|
|
60
|
+
`[GlomoPay RN SDK] ${resolvedMockMode ? "Using" : "Not using"} Mock mode for publicKey: ${publicKey}`
|
|
61
|
+
);
|
|
59
62
|
}
|
|
60
|
-
return
|
|
63
|
+
return resolvedMockMode;
|
|
61
64
|
}, [publicKey, devMode]);
|
|
62
65
|
|
|
63
66
|
const {
|
|
@@ -66,11 +69,15 @@ function GlomoLrsCheckoutComponent(
|
|
|
66
69
|
showCheckout,
|
|
67
70
|
flowWebViewUrl,
|
|
68
71
|
showFlowWebView,
|
|
72
|
+
educationCarouselUrl: resolvedEducationCarouselUrl,
|
|
73
|
+
educationCarouselState,
|
|
69
74
|
checkoutUrl,
|
|
70
75
|
mainWebViewRef,
|
|
71
76
|
flowWebViewRef,
|
|
77
|
+
educationCarouselWebViewRef,
|
|
72
78
|
handleMainWebViewMessage,
|
|
73
79
|
handleFlowWebViewMessage,
|
|
80
|
+
handleEducationCarouselWebViewMessage,
|
|
74
81
|
handleError,
|
|
75
82
|
handleHttpError,
|
|
76
83
|
handleNavigationStateChange,
|
|
@@ -78,6 +85,7 @@ function GlomoLrsCheckoutComponent(
|
|
|
78
85
|
handleFlowBack,
|
|
79
86
|
injectedMain,
|
|
80
87
|
injectedFlow,
|
|
88
|
+
injectedEducationCarousel,
|
|
81
89
|
} = useLrsCheckout(
|
|
82
90
|
{
|
|
83
91
|
server,
|
|
@@ -99,14 +107,73 @@ function GlomoLrsCheckoutComponent(
|
|
|
99
107
|
getStatus: () => status,
|
|
100
108
|
}));
|
|
101
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Injecting a viewport meta tag and CSS text-size-adjust guard into the carousel WebView on load.
|
|
112
|
+
* This prevents the OS from auto-scaling text (especially on Android), which can break the carousel layout.
|
|
113
|
+
*/
|
|
114
|
+
const carouselViewportGuardScript = useMemo(
|
|
115
|
+
() => `
|
|
116
|
+
(function() {
|
|
117
|
+
try {
|
|
118
|
+
var viewportContent = "width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover";
|
|
119
|
+
var viewportTag = document.querySelector("meta[name='viewport']");
|
|
120
|
+
|
|
121
|
+
if (!viewportTag) {
|
|
122
|
+
viewportTag = document.createElement("meta");
|
|
123
|
+
viewportTag.setAttribute("name", "viewport");
|
|
124
|
+
var headOrRoot = document.head || document.documentElement;
|
|
125
|
+
if (headOrRoot) {
|
|
126
|
+
headOrRoot.appendChild(viewportTag);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (viewportTag) {
|
|
131
|
+
viewportTag.setAttribute("content", viewportContent);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
var styleTagId = "__glomo_rn_webview_scale_guard__";
|
|
135
|
+
var existingStyleTag = document.getElementById(styleTagId);
|
|
136
|
+
if (!existingStyleTag) {
|
|
137
|
+
var styleTag = document.createElement("style");
|
|
138
|
+
styleTag.id = styleTagId;
|
|
139
|
+
styleTag.textContent = "html, body, * { -webkit-text-size-adjust: 100% !important; text-size-adjust: 100% !important; }";
|
|
140
|
+
var styleHost = document.head || document.documentElement;
|
|
141
|
+
if (styleHost) {
|
|
142
|
+
styleHost.appendChild(styleTag);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {}
|
|
146
|
+
true;
|
|
147
|
+
})();
|
|
148
|
+
`,
|
|
149
|
+
[]
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Locking down zoom and scaling behaviour for the carousel WebView.
|
|
154
|
+
* setSupportZoom/scalesPageToFit prevent user-initiated and system-initiated zoom;
|
|
155
|
+
* textZoom=100 on Android normalises the system font-size setting so the carousel isn't affected.
|
|
156
|
+
*/
|
|
157
|
+
const carouselWebViewProps = useMemo<Partial<WebViewProps>>(
|
|
158
|
+
() => ({
|
|
159
|
+
setSupportZoom: false,
|
|
160
|
+
scalesPageToFit: false,
|
|
161
|
+
overScrollMode: "never",
|
|
162
|
+
textZoom: Platform.OS === "android" ? 100 : undefined,
|
|
163
|
+
}),
|
|
164
|
+
[]
|
|
165
|
+
);
|
|
166
|
+
|
|
102
167
|
// Making the WebView component
|
|
103
168
|
const makeWebView = React.useCallback(
|
|
104
169
|
(
|
|
105
170
|
ref: React.RefObject<WebView>,
|
|
106
171
|
url: string,
|
|
107
172
|
messageHandler: (event: WebViewMessageEvent) => void,
|
|
108
|
-
identifier: "main" | "flow",
|
|
109
|
-
injectedJavaScript?: string
|
|
173
|
+
identifier: "main" | "flow" | "educationCarousel",
|
|
174
|
+
injectedJavaScript?: string,
|
|
175
|
+
injectedJavaScriptBeforeContentLoaded?: string,
|
|
176
|
+
additionalProps?: Partial<WebViewProps>
|
|
110
177
|
) => {
|
|
111
178
|
// Preventing a render if no injection script is provided
|
|
112
179
|
if (!injectedJavaScript) {
|
|
@@ -146,6 +213,26 @@ function GlomoLrsCheckoutComponent(
|
|
|
146
213
|
onError={handleError}
|
|
147
214
|
onHttpError={handleHttpError}
|
|
148
215
|
onNavigationStateChange={handleNavigationStateChange}
|
|
216
|
+
onLoadStart={() => {
|
|
217
|
+
if (devMode) {
|
|
218
|
+
console.log(`[GlomoPay RN SDK] [${identifier}] WebView load started for ${url}`);
|
|
219
|
+
}
|
|
220
|
+
}}
|
|
221
|
+
onLoadEnd={() => {
|
|
222
|
+
if (identifier === "educationCarousel" && ref.current && carouselViewportGuardScript) {
|
|
223
|
+
ref.current.injectJavaScript(carouselViewportGuardScript);
|
|
224
|
+
}
|
|
225
|
+
if (devMode) {
|
|
226
|
+
console.log(`[GlomoPay RN SDK] [${identifier}] WebView load ended for ${url}`);
|
|
227
|
+
}
|
|
228
|
+
}}
|
|
229
|
+
onLoadProgress={({ nativeEvent }) => {
|
|
230
|
+
if (devMode && identifier === "educationCarousel") {
|
|
231
|
+
console.log(
|
|
232
|
+
`[GlomoPay RN SDK] [educationCarousel] load progress=${nativeEvent.progress} url=${nativeEvent.url}`
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}}
|
|
149
236
|
source={{ uri: url }}
|
|
150
237
|
javaScriptCanOpenWindowsAutomatically={true}
|
|
151
238
|
setSupportMultipleWindows={true}
|
|
@@ -156,6 +243,7 @@ function GlomoLrsCheckoutComponent(
|
|
|
156
243
|
limitsNavigationsToAppBoundDomains={false}
|
|
157
244
|
menuItems={[]}
|
|
158
245
|
injectedJavaScript={injectedJavaScript}
|
|
246
|
+
injectedJavaScriptBeforeContentLoaded={injectedJavaScriptBeforeContentLoaded}
|
|
159
247
|
style={styles.webview}
|
|
160
248
|
showsHorizontalScrollIndicator={false}
|
|
161
249
|
showsVerticalScrollIndicator={false}
|
|
@@ -164,10 +252,11 @@ function GlomoLrsCheckoutComponent(
|
|
|
164
252
|
sharedCookiesEnabled={true}
|
|
165
253
|
allowsInlineMediaPlayback={true}
|
|
166
254
|
mediaPlaybackRequiresUserAction={false}
|
|
255
|
+
{...additionalProps}
|
|
167
256
|
/>
|
|
168
257
|
);
|
|
169
258
|
},
|
|
170
|
-
[handleError, handleHttpError, handleNavigationStateChange, devMode]
|
|
259
|
+
[handleError, handleHttpError, handleNavigationStateChange, devMode, carouselViewportGuardScript]
|
|
171
260
|
);
|
|
172
261
|
|
|
173
262
|
const MainWebViewComponent = useMemo(
|
|
@@ -183,6 +272,29 @@ function GlomoLrsCheckoutComponent(
|
|
|
183
272
|
[makeWebView, flowWebViewUrl, handleFlowWebViewMessage, injectedFlow, flowWebViewRef]
|
|
184
273
|
);
|
|
185
274
|
|
|
275
|
+
const EducationCarouselWebViewComponent = useMemo(
|
|
276
|
+
() =>
|
|
277
|
+
resolvedEducationCarouselUrl
|
|
278
|
+
? makeWebView(
|
|
279
|
+
educationCarouselWebViewRef,
|
|
280
|
+
resolvedEducationCarouselUrl,
|
|
281
|
+
handleEducationCarouselWebViewMessage,
|
|
282
|
+
"educationCarousel",
|
|
283
|
+
injectedEducationCarousel,
|
|
284
|
+
undefined,
|
|
285
|
+
carouselWebViewProps
|
|
286
|
+
)
|
|
287
|
+
: null,
|
|
288
|
+
[
|
|
289
|
+
makeWebView,
|
|
290
|
+
resolvedEducationCarouselUrl,
|
|
291
|
+
educationCarouselWebViewRef,
|
|
292
|
+
handleEducationCarouselWebViewMessage,
|
|
293
|
+
injectedEducationCarousel,
|
|
294
|
+
carouselWebViewProps,
|
|
295
|
+
]
|
|
296
|
+
);
|
|
297
|
+
|
|
186
298
|
// Using the controlled visible if provided, otherwise using the internal state
|
|
187
299
|
const isVisible = useMemo(() => controlledVisible ?? showCheckout, [controlledVisible, showCheckout]);
|
|
188
300
|
|
|
@@ -201,6 +313,22 @@ function GlomoLrsCheckoutComponent(
|
|
|
201
313
|
// Alias to ensure explicit component reference for React Native
|
|
202
314
|
const ModalComponent = Modal;
|
|
203
315
|
|
|
316
|
+
/**
|
|
317
|
+
* Deriving render gates from state:
|
|
318
|
+
* showCarousel: carousel WebView is visible only after it signals it has content
|
|
319
|
+
* shouldRenderFlowOverlay: flow overlay is mounted only when there is an active flow URL
|
|
320
|
+
* shouldRenderEducationCarousel: carousel is rendered inside the flow overlay (depends on shouldRenderFlowOverlay)
|
|
321
|
+
*/
|
|
322
|
+
const showCarousel = educationCarouselState === "hasContent";
|
|
323
|
+
const shouldRenderFlowOverlay = Boolean(showFlowWebView && flowWebViewUrl && FlowWebViewComponent);
|
|
324
|
+
const shouldRenderEducationCarousel = Boolean(shouldRenderFlowOverlay && EducationCarouselWebViewComponent);
|
|
325
|
+
|
|
326
|
+
if (devMode) {
|
|
327
|
+
console.log(
|
|
328
|
+
`[GlomoPay RN SDK] Render gates: modal=${shouldShowModal}, flowOverlay=${shouldRenderFlowOverlay}, educationCarouselWebView=${shouldRenderEducationCarousel}, educationCarouselVisible=${showCarousel}, flowWebViewUrl=${flowWebViewUrl || "<empty>"}, educationCarouselUrl=${resolvedEducationCarouselUrl || "<empty>"}`
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
204
332
|
return (
|
|
205
333
|
<ModalComponent
|
|
206
334
|
visible={shouldShowModal}
|
|
@@ -211,14 +339,36 @@ function GlomoLrsCheckoutComponent(
|
|
|
211
339
|
<View style={styles.modalContainer}>
|
|
212
340
|
<View style={styles.mainWebViewContainer}>{MainWebViewComponent}</View>
|
|
213
341
|
|
|
214
|
-
{
|
|
342
|
+
{shouldRenderFlowOverlay && (
|
|
215
343
|
<View style={styles.flowWebViewContainer}>
|
|
216
344
|
<SafeAreaView style={styles.flowHeader}>
|
|
217
345
|
<TouchableOpacity onPress={handleFlowBack} style={styles.flowBackButton}>
|
|
218
346
|
<Text style={styles.flowBackChevron}>‹</Text>
|
|
219
347
|
</TouchableOpacity>
|
|
220
348
|
</SafeAreaView>
|
|
221
|
-
<View style={styles.
|
|
349
|
+
<View style={styles.overlayContentContainer}>
|
|
350
|
+
{shouldRenderEducationCarousel && (
|
|
351
|
+
<View
|
|
352
|
+
pointerEvents={showCarousel ? "auto" : "none"}
|
|
353
|
+
style={[
|
|
354
|
+
styles.educationCarouselContainer,
|
|
355
|
+
showCarousel
|
|
356
|
+
? styles.educationCarouselContainerVisible
|
|
357
|
+
: styles.educationCarouselContainerHidden,
|
|
358
|
+
]}
|
|
359
|
+
>
|
|
360
|
+
{EducationCarouselWebViewComponent}
|
|
361
|
+
</View>
|
|
362
|
+
)}
|
|
363
|
+
<View
|
|
364
|
+
style={[
|
|
365
|
+
styles.flowWebViewContent,
|
|
366
|
+
showCarousel ? styles.flowWebViewContentWithCarousel : null,
|
|
367
|
+
]}
|
|
368
|
+
>
|
|
369
|
+
{FlowWebViewComponent}
|
|
370
|
+
</View>
|
|
371
|
+
</View>
|
|
222
372
|
</View>
|
|
223
373
|
)}
|
|
224
374
|
</View>
|
|
@@ -257,10 +407,9 @@ const styles = StyleSheet.create({
|
|
|
257
407
|
backgroundColor: "#fff",
|
|
258
408
|
borderBottomWidth: 1,
|
|
259
409
|
borderBottomColor: "#e0e0e0",
|
|
260
|
-
paddingTop: Platform.OS === "android" ? StatusBar.currentHeight : 0,
|
|
261
410
|
},
|
|
262
411
|
flowBackButton: {
|
|
263
|
-
paddingVertical:
|
|
412
|
+
paddingVertical: 0,
|
|
264
413
|
paddingLeft: 20,
|
|
265
414
|
},
|
|
266
415
|
flowBackChevron: {
|
|
@@ -271,6 +420,31 @@ const styles = StyleSheet.create({
|
|
|
271
420
|
flowWebViewContent: {
|
|
272
421
|
flex: 1,
|
|
273
422
|
},
|
|
423
|
+
flowWebViewContentWithCarousel: {
|
|
424
|
+
flex: 8.5,
|
|
425
|
+
},
|
|
426
|
+
overlayContentContainer: {
|
|
427
|
+
flex: 1,
|
|
428
|
+
},
|
|
429
|
+
educationCarouselContainer: {
|
|
430
|
+
flex: 1.5,
|
|
431
|
+
},
|
|
432
|
+
educationCarouselContainerVisible: {
|
|
433
|
+
opacity: 1,
|
|
434
|
+
zIndex: 2,
|
|
435
|
+
elevation: 2,
|
|
436
|
+
},
|
|
437
|
+
educationCarouselContainerHidden: {
|
|
438
|
+
position: "absolute",
|
|
439
|
+
top: 0,
|
|
440
|
+
left: 0,
|
|
441
|
+
width: 1,
|
|
442
|
+
height: 1,
|
|
443
|
+
opacity: 0,
|
|
444
|
+
zIndex: -1,
|
|
445
|
+
elevation: 0,
|
|
446
|
+
overflow: "hidden",
|
|
447
|
+
},
|
|
274
448
|
errorContainer: {
|
|
275
449
|
flex: 1,
|
|
276
450
|
justifyContent: "center",
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// Payload, status, and error types from the LRS Checkout flow
|
|
4
4
|
export { type GlomoLrsCheckoutPayload, type LrsCheckoutStatus } from "./use-lrs-checkout";
|
|
5
5
|
export { type SdkError } from "./utils/analytics";
|
|
6
|
+
export { type GlomoLrsServer } from "./config/base";
|
|
6
7
|
|
|
7
8
|
// Main UI Component for the LRS Checkout flow
|
|
8
9
|
export { GlomoLrsCheckout, type GlomoLrsCheckoutProps, type GlomoLrsCheckoutRef } from "./glomo-lrs-checkout";
|
package/src/injections/index.ts
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { injectedScript as mainScript } from "./webview-main.injection";
|
|
5
5
|
import { injectedScript as flowScript } from "./webview-flow.injection";
|
|
6
|
+
import { injectedScript as educationCarouselScript } from "./webview-education-carousel.injection";
|
|
6
7
|
|
|
7
8
|
export const InjectionScripts = {
|
|
8
9
|
main: mainScript,
|
|
9
10
|
flow: flowScript,
|
|
11
|
+
educationCarousel: educationCarouselScript,
|
|
10
12
|
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/** Injection JavaScript for the Education Carousel WebView */
|
|
2
|
+
export const injectedScript: string = `
|
|
3
|
+
(function() {
|
|
4
|
+
// Handling the carousel
|
|
5
|
+
const LOG_PREFIX = 'EDUCATION_CAROUSEL_WEBVIEW: ';
|
|
6
|
+
|
|
7
|
+
function formatMessage(args) {
|
|
8
|
+
return args.map(function(arg) {
|
|
9
|
+
if (typeof arg === 'object') {
|
|
10
|
+
try {
|
|
11
|
+
return LOG_PREFIX + JSON.stringify(arg, null, 2);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
return LOG_PREFIX + '[unserializable object]';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return LOG_PREFIX + String(arg);
|
|
18
|
+
}).join(' ');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function parseMessageData(rawData) {
|
|
22
|
+
if (typeof rawData === 'string') {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(rawData);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return rawData;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return rawData;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function forwardEducationStepsMessage(data, source) {
|
|
34
|
+
const parsedData = parseMessageData(data);
|
|
35
|
+
|
|
36
|
+
if (!parsedData || typeof parsedData !== 'object') {
|
|
37
|
+
safePostMessage({
|
|
38
|
+
type: 'console',
|
|
39
|
+
level: 'debug',
|
|
40
|
+
message: formatMessage(['Ignored non-object carousel message from ' + source, parsedData])
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (parsedData.type !== 'lrs.has_education_steps') {
|
|
46
|
+
safePostMessage({
|
|
47
|
+
type: 'console',
|
|
48
|
+
level: 'debug',
|
|
49
|
+
message: formatMessage(['Ignored unrelated carousel message from ' + source, parsedData])
|
|
50
|
+
});
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
safePostMessage({
|
|
55
|
+
type: 'lrs.has_education_steps',
|
|
56
|
+
value: Boolean(parsedData.value),
|
|
57
|
+
source: source,
|
|
58
|
+
payload: parsedData
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
safePostMessage({
|
|
62
|
+
type: 'console',
|
|
63
|
+
level: 'info',
|
|
64
|
+
message: formatMessage(['Forwarded lrs.has_education_steps from ' + source, parsedData])
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function safePostMessage(payload) {
|
|
69
|
+
try {
|
|
70
|
+
if (window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === 'function') {
|
|
71
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(payload));
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
originalError && originalError.call(console, LOG_PREFIX + 'Failed to post message to ReactNativeWebView', error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Overriding console methods
|
|
79
|
+
const originalLog = console.log;
|
|
80
|
+
const originalWarn = console.warn;
|
|
81
|
+
const originalError = console.error;
|
|
82
|
+
const originalInfo = console.info;
|
|
83
|
+
|
|
84
|
+
console.log = function(...args) {
|
|
85
|
+
originalLog.apply(console, args);
|
|
86
|
+
safePostMessage({
|
|
87
|
+
type: 'console',
|
|
88
|
+
level: 'log',
|
|
89
|
+
message: formatMessage(args)
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
console.warn = function(...args) {
|
|
94
|
+
originalWarn.apply(console, args);
|
|
95
|
+
safePostMessage({
|
|
96
|
+
type: 'console',
|
|
97
|
+
level: 'warn',
|
|
98
|
+
message: formatMessage(args)
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
console.error = function(...args) {
|
|
103
|
+
originalError.apply(console, args);
|
|
104
|
+
safePostMessage({
|
|
105
|
+
type: 'console',
|
|
106
|
+
level: 'error',
|
|
107
|
+
message: formatMessage(args)
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
console.info = function(...args) {
|
|
112
|
+
originalInfo.apply(console, args);
|
|
113
|
+
safePostMessage({
|
|
114
|
+
type: 'console',
|
|
115
|
+
level: 'info',
|
|
116
|
+
message: formatMessage(args)
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Listening for unhandled errors
|
|
121
|
+
window.addEventListener('error', function(event) {
|
|
122
|
+
safePostMessage({
|
|
123
|
+
type: 'console',
|
|
124
|
+
level: 'error',
|
|
125
|
+
message: LOG_PREFIX + 'Unhandled Error: ' + event.message + ' at ' + event.filename + ':' + event.lineno
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Listening for unhandled promise rejections
|
|
130
|
+
window.addEventListener('unhandledrejection', function(event) {
|
|
131
|
+
safePostMessage({
|
|
132
|
+
type: 'console',
|
|
133
|
+
level: 'error',
|
|
134
|
+
message: LOG_PREFIX + 'Unhandled Promise Rejection: ' + (event.reason?.toString() || 'Unknown')
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Listening for messages from the WebView
|
|
139
|
+
window.addEventListener('message', function(event) {
|
|
140
|
+
console.log(LOG_PREFIX + 'Message received from window: ' + event.data);
|
|
141
|
+
forwardEducationStepsMessage(event.data, 'window.message');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
document.addEventListener('message', function(event) {
|
|
145
|
+
console.log(LOG_PREFIX + 'Message received from document: ' + event.data);
|
|
146
|
+
forwardEducationStepsMessage(event.data, 'document.message');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Logging that injection is complete
|
|
150
|
+
console.log(LOG_PREFIX + 'GlomoPay WebView JavaScript injection complete and waiting for lrs.has_education_steps');
|
|
151
|
+
|
|
152
|
+
true; // Required for injected JavaScript
|
|
153
|
+
})();
|
|
154
|
+
`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Injection JavaScript for the Secondary WebView */
|
|
2
2
|
export const injectedScript: string = `
|
|
3
3
|
(function() {
|
|
4
|
-
//
|
|
4
|
+
// Overriding console methods
|
|
5
5
|
const originalLog = console.log;
|
|
6
6
|
const originalWarn = console.warn;
|
|
7
7
|
const originalError = console.error;
|
|
@@ -58,8 +58,7 @@ export const injectedScript: string = `
|
|
|
58
58
|
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
59
59
|
type: 'console',
|
|
60
60
|
level: 'network',
|
|
61
|
-
message: "FLOW_WEBVIEW: " + 'FETCH RESPONSE: ' + url + ' - Status: ' + response.status
|
|
62
|
-
extraData: { data: response.responseText }
|
|
61
|
+
message: "FLOW_WEBVIEW: " + 'FETCH RESPONSE: ' + url + ' - Status: ' + response.status
|
|
63
62
|
}));
|
|
64
63
|
return response;
|
|
65
64
|
})
|
|
@@ -94,8 +93,7 @@ export const injectedScript: string = `
|
|
|
94
93
|
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
95
94
|
type: 'console',
|
|
96
95
|
level: 'network',
|
|
97
|
-
message: "FLOW_WEBVIEW: " + 'XHR RESPONSE: ' + this._url + ' - Status: ' + this.status
|
|
98
|
-
extraData: { data: this.responseText }
|
|
96
|
+
message: "FLOW_WEBVIEW: " + 'XHR RESPONSE: ' + this._url + ' - Status: ' + this.status
|
|
99
97
|
}));
|
|
100
98
|
});
|
|
101
99
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Injection JavaScript for the Primary WebView */
|
|
2
2
|
export const injectedScript: string = `
|
|
3
3
|
(function() {
|
|
4
|
-
//
|
|
4
|
+
// Overriding console methods
|
|
5
5
|
const originalLog = console.log;
|
|
6
6
|
const originalWarn = console.warn;
|
|
7
7
|
const originalError = console.error;
|
|
@@ -94,15 +94,13 @@ export const injectedScript: string = `
|
|
|
94
94
|
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
95
95
|
type: 'console',
|
|
96
96
|
level: 'network',
|
|
97
|
-
message: 'XHR RESPONSE LOAD: ' + this._url + ' - Status: ' + this.status
|
|
98
|
-
extraData: { data: this.responseText }
|
|
97
|
+
message: 'XHR RESPONSE LOAD: ' + this._url + ' - Status: ' + this.status
|
|
99
98
|
}));
|
|
100
99
|
} catch (e) {
|
|
101
100
|
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
102
101
|
type: 'console',
|
|
103
102
|
level: 'network',
|
|
104
|
-
message: 'XHR RESPONSE ERROR: ' + this._url + ' - Status: ' + this.status
|
|
105
|
-
extraData: { data: this.responseText }
|
|
103
|
+
message: 'XHR RESPONSE ERROR: ' + this._url + ' - Status: ' + this.status
|
|
106
104
|
}));
|
|
107
105
|
}
|
|
108
106
|
});
|