@savers_app/react-native-sandbox-sdk 1.2.8 → 1.4.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/lib/module/@types/react-native-svg.d.js +2 -0
- package/lib/module/@types/react-native-svg.d.js.map +1 -0
- package/lib/module/components/TravelPortalBackIcon.js +24 -0
- package/lib/module/components/TravelPortalBackIcon.js.map +1 -0
- package/lib/module/components/TravelPortalHeader.js +97 -0
- package/lib/module/components/TravelPortalHeader.js.map +1 -0
- package/lib/module/services/webview/DualWebViewBridgeController.js +42 -86
- package/lib/module/services/webview/DualWebViewBridgeController.js.map +1 -1
- package/lib/module/services/webview/dualWebViewBridge.types.js.map +1 -1
- package/lib/typescript/src/components/TravelPortalBackIcon.d.ts +10 -0
- package/lib/typescript/src/components/TravelPortalBackIcon.d.ts.map +1 -0
- package/lib/typescript/src/components/TravelPortalHeader.d.ts +19 -0
- package/lib/typescript/src/components/TravelPortalHeader.d.ts.map +1 -0
- package/lib/typescript/src/services/webview/DualWebViewBridgeController.d.ts +4 -0
- package/lib/typescript/src/services/webview/DualWebViewBridgeController.d.ts.map +1 -1
- package/lib/typescript/src/services/webview/dualWebViewBridge.types.d.ts +2 -0
- package/lib/typescript/src/services/webview/dualWebViewBridge.types.d.ts.map +1 -1
- package/package.json +3 -1
- package/src/@types/react-native-svg.d.ts +19 -0
- package/src/components/TravelPortalBackIcon.tsx +26 -0
- package/src/components/TravelPortalHeader.tsx +143 -0
- package/src/services/webview/DualWebViewBridgeController.tsx +61 -87
- package/src/services/webview/dualWebViewBridge.types.ts +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../../src","sources":["@types/react-native-svg.d.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Path, Svg } from 'react-native-svg';
|
|
5
|
+
|
|
6
|
+
/** Matches `m.saversapp.com/assets/svg/BackIcon.tsx` (17×16). */
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
const DEFAULT_COLOR = '#66CC99';
|
|
9
|
+
export const TravelPortalBackIcon = ({
|
|
10
|
+
height = 16,
|
|
11
|
+
width = 17,
|
|
12
|
+
color = DEFAULT_COLOR
|
|
13
|
+
}) => /*#__PURE__*/_jsx(Svg, {
|
|
14
|
+
width: width,
|
|
15
|
+
height: height,
|
|
16
|
+
viewBox: "0 0 17 16",
|
|
17
|
+
fill: "none",
|
|
18
|
+
children: /*#__PURE__*/_jsx(Path, {
|
|
19
|
+
d: "M.293 7.293a1 1 0 000 1.414l6.364 6.364a1 1 0 001.414-1.414L2.414 8l5.657-5.657A1 1 0 006.657.93L.293 7.293zM1 9h16V7H1v2z",
|
|
20
|
+
fill: color
|
|
21
|
+
})
|
|
22
|
+
});
|
|
23
|
+
export default TravelPortalBackIcon;
|
|
24
|
+
//# sourceMappingURL=TravelPortalBackIcon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","Path","Svg","jsx","_jsx","DEFAULT_COLOR","TravelPortalBackIcon","height","width","color","viewBox","fill","children","d"],"sourceRoot":"../../../src","sources":["components/TravelPortalBackIcon.tsx"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,IAAI,EAAEC,GAAG,QAAQ,kBAAkB;;AAE5C;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAOA,MAAMC,aAAa,GAAG,SAAS;AAE/B,OAAO,MAAMC,oBAAyD,GAAGA,CAAC;EACxEC,MAAM,GAAG,EAAE;EACXC,KAAK,GAAG,EAAE;EACVC,KAAK,GAAGJ;AACV,CAAC,kBACCD,IAAA,CAACF,GAAG;EAACM,KAAK,EAAEA,KAAM;EAACD,MAAM,EAAEA,MAAO;EAACG,OAAO,EAAC,WAAW;EAACC,IAAI,EAAC,MAAM;EAAAC,QAAA,eAChER,IAAA,CAACH,IAAI;IACHY,CAAC,EAAC,4HAA4H;IAC9HF,IAAI,EAAEF;EAAM,CACb;AAAC,CACC,CACN;AAED,eAAeH,oBAAoB","ignoreList":[]}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React, { useEffect, useState } from 'react';
|
|
4
|
+
import { Image, StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
5
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
6
|
+
import { TravelPortalBackIcon } from "./TravelPortalBackIcon.js";
|
|
7
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
+
/** Horizontal padding: Tailwind `px-3.5` in m.saversapp Header. */
|
|
9
|
+
const HEADER_PADDING_X = 14;
|
|
10
|
+
/** Vertical padding: Tailwind `py-4` in m.saversapp Header. */
|
|
11
|
+
const HEADER_PADDING_Y = 16;
|
|
12
|
+
/** Row min height: Tailwind `min-h-[60px]` in m.saversapp Header. */
|
|
13
|
+
const HEADER_ROW_MIN_HEIGHT = 60;
|
|
14
|
+
const LOGO_MAX_WIDTH = 160;
|
|
15
|
+
const LOGO_MAX_HEIGHT = 70;
|
|
16
|
+
const LOGO_MIN_HEIGHT = 40;
|
|
17
|
+
export const TravelPortalHeader = ({
|
|
18
|
+
onBack,
|
|
19
|
+
partnerLogo,
|
|
20
|
+
backIconColor,
|
|
21
|
+
renderBackIcon,
|
|
22
|
+
topInset = 'none',
|
|
23
|
+
style
|
|
24
|
+
}) => {
|
|
25
|
+
const insets = useSafeAreaInsets();
|
|
26
|
+
const paddingTop = topInset === 'safe-area' ? insets.top + HEADER_PADDING_Y : HEADER_PADDING_Y;
|
|
27
|
+
const [logoWidth, setLogoWidth] = useState(LOGO_MAX_WIDTH);
|
|
28
|
+
const [logoHeight, setLogoHeight] = useState(LOGO_MAX_HEIGHT);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!partnerLogo) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
Image.getSize(partnerLogo, (srcWidth, srcHeight) => {
|
|
34
|
+
setLogoWidth(srcWidth > LOGO_MAX_WIDTH ? LOGO_MAX_WIDTH : srcWidth);
|
|
35
|
+
setLogoHeight(srcHeight > LOGO_MAX_HEIGHT ? LOGO_MAX_HEIGHT : srcHeight);
|
|
36
|
+
}, () => {
|
|
37
|
+
setLogoWidth(LOGO_MAX_WIDTH);
|
|
38
|
+
setLogoHeight(LOGO_MAX_HEIGHT);
|
|
39
|
+
});
|
|
40
|
+
}, [partnerLogo]);
|
|
41
|
+
return /*#__PURE__*/_jsx(View, {
|
|
42
|
+
style: [styles.container, {
|
|
43
|
+
paddingTop,
|
|
44
|
+
paddingBottom: HEADER_PADDING_Y
|
|
45
|
+
}, style],
|
|
46
|
+
children: /*#__PURE__*/_jsxs(View, {
|
|
47
|
+
style: styles.row,
|
|
48
|
+
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
|
|
49
|
+
onPress: onBack,
|
|
50
|
+
hitSlop: 16,
|
|
51
|
+
accessibilityRole: "button",
|
|
52
|
+
accessibilityLabel: "Back",
|
|
53
|
+
style: styles.backButton,
|
|
54
|
+
children: renderBackIcon ? renderBackIcon() : /*#__PURE__*/_jsx(TravelPortalBackIcon, {
|
|
55
|
+
color: backIconColor
|
|
56
|
+
})
|
|
57
|
+
}), partnerLogo ? /*#__PURE__*/_jsx(Image, {
|
|
58
|
+
source: {
|
|
59
|
+
uri: partnerLogo
|
|
60
|
+
},
|
|
61
|
+
resizeMode: "contain",
|
|
62
|
+
style: [styles.logo, {
|
|
63
|
+
width: logoWidth,
|
|
64
|
+
height: logoHeight
|
|
65
|
+
}]
|
|
66
|
+
}) : /*#__PURE__*/_jsx(View, {
|
|
67
|
+
style: styles.logoPlaceholder
|
|
68
|
+
})]
|
|
69
|
+
})
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
const styles = StyleSheet.create({
|
|
73
|
+
container: {
|
|
74
|
+
backgroundColor: '#fff',
|
|
75
|
+
paddingHorizontal: HEADER_PADDING_X
|
|
76
|
+
},
|
|
77
|
+
row: {
|
|
78
|
+
flexDirection: 'row',
|
|
79
|
+
alignItems: 'center',
|
|
80
|
+
justifyContent: 'space-between',
|
|
81
|
+
minHeight: HEADER_ROW_MIN_HEIGHT
|
|
82
|
+
},
|
|
83
|
+
backButton: {
|
|
84
|
+
paddingVertical: 8,
|
|
85
|
+
justifyContent: 'center'
|
|
86
|
+
},
|
|
87
|
+
logo: {
|
|
88
|
+
minHeight: LOGO_MIN_HEIGHT,
|
|
89
|
+
alignSelf: 'flex-end'
|
|
90
|
+
},
|
|
91
|
+
logoPlaceholder: {
|
|
92
|
+
width: LOGO_MAX_WIDTH,
|
|
93
|
+
minHeight: LOGO_MIN_HEIGHT
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
export default TravelPortalHeader;
|
|
97
|
+
//# sourceMappingURL=TravelPortalHeader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","useEffect","useState","Image","StyleSheet","TouchableOpacity","View","useSafeAreaInsets","TravelPortalBackIcon","jsx","_jsx","jsxs","_jsxs","HEADER_PADDING_X","HEADER_PADDING_Y","HEADER_ROW_MIN_HEIGHT","LOGO_MAX_WIDTH","LOGO_MAX_HEIGHT","LOGO_MIN_HEIGHT","TravelPortalHeader","onBack","partnerLogo","backIconColor","renderBackIcon","topInset","style","insets","paddingTop","top","logoWidth","setLogoWidth","logoHeight","setLogoHeight","getSize","srcWidth","srcHeight","styles","container","paddingBottom","children","row","onPress","hitSlop","accessibilityRole","accessibilityLabel","backButton","color","source","uri","resizeMode","logo","width","height","logoPlaceholder","create","backgroundColor","paddingHorizontal","flexDirection","alignItems","justifyContent","minHeight","paddingVertical","alignSelf"],"sourceRoot":"../../../src","sources":["components/TravelPortalHeader.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
4
|
-
import {
|
|
3
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { StyleSheet, View } from 'react-native';
|
|
5
5
|
import WebView from 'react-native-webview';
|
|
6
|
+
import { TravelPortalHeader } from "../../components/TravelPortalHeader.js";
|
|
6
7
|
import { parseDualWebViewEnvelope } from "./dualWebViewBridge.types.js";
|
|
7
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
9
|
const DEFAULT_TRAVEL_PORTAL_URL = 'https://sandbox.travelercashback.com';
|
|
@@ -13,6 +14,25 @@ const WEBVIEW_UA_MARKER = 'CloAppWebView';
|
|
|
13
14
|
/** Legacy / Hub: open travel from first WebView without JSON envelope. */
|
|
14
15
|
const TRAVEL_DEEP_LINK = 'saversapp://travel';
|
|
15
16
|
const LEGACY_OPEN_TRAVEL_ACTIONS = ['OPEN_TRAVEL_PORTAL', 'OPEN_TRAVEL'];
|
|
17
|
+
function resolveTravelPortalUrlFromPayload(payload) {
|
|
18
|
+
if (typeof payload === 'string' && payload.trim()) {
|
|
19
|
+
return payload.trim();
|
|
20
|
+
}
|
|
21
|
+
if (payload && typeof payload === 'object' && 'url' in payload) {
|
|
22
|
+
const url = payload.url;
|
|
23
|
+
if (typeof url === 'string' && url.trim()) {
|
|
24
|
+
return url.trim();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
function resolveTravelPortalUrlFromLegacyMessage(data) {
|
|
30
|
+
const topLevel = data?.url;
|
|
31
|
+
if (typeof topLevel === 'string' && topLevel.trim()) {
|
|
32
|
+
return topLevel.trim();
|
|
33
|
+
}
|
|
34
|
+
return resolveTravelPortalUrlFromPayload(data?.payload);
|
|
35
|
+
}
|
|
16
36
|
function buildInjectNativeBridgeScript(detail) {
|
|
17
37
|
return `
|
|
18
38
|
(function(){
|
|
@@ -26,6 +46,8 @@ export const DualWebViewBridgeController = ({
|
|
|
26
46
|
saversAppUrl,
|
|
27
47
|
travelPortalUrl = DEFAULT_TRAVEL_PORTAL_URL,
|
|
28
48
|
partnerLogo,
|
|
49
|
+
travelBackIconColor,
|
|
50
|
+
travelHeaderTopInset = 'none',
|
|
29
51
|
renderTravelBackIcon,
|
|
30
52
|
initialSurface = 'savers',
|
|
31
53
|
onSaversSdkMessage,
|
|
@@ -37,7 +59,14 @@ export const DualWebViewBridgeController = ({
|
|
|
37
59
|
const [surface, setSurface] = useState(initialSurface);
|
|
38
60
|
const [saversKey, setSaversKey] = useState(0);
|
|
39
61
|
const [travelKey, setTravelKey] = useState(0);
|
|
40
|
-
const
|
|
62
|
+
const [activeTravelPortalUrl, setActiveTravelPortalUrl] = useState(travelPortalUrl);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
setActiveTravelPortalUrl(travelPortalUrl);
|
|
65
|
+
}, [travelPortalUrl]);
|
|
66
|
+
const openTravel = useCallback(url => {
|
|
67
|
+
if (url) {
|
|
68
|
+
setActiveTravelPortalUrl(url);
|
|
69
|
+
}
|
|
41
70
|
setSurface('travel');
|
|
42
71
|
setTravelKey(k => k + 1);
|
|
43
72
|
}, []);
|
|
@@ -58,7 +87,7 @@ export const DualWebViewBridgeController = ({
|
|
|
58
87
|
const handleBridgeEnvelope = useCallback(env => {
|
|
59
88
|
switch (env.action) {
|
|
60
89
|
case 'open_travel':
|
|
61
|
-
openTravel();
|
|
90
|
+
openTravel(resolveTravelPortalUrlFromPayload(env.payload));
|
|
62
91
|
return true;
|
|
63
92
|
case 'close_travel':
|
|
64
93
|
backToSavers();
|
|
@@ -84,7 +113,7 @@ export const DualWebViewBridgeController = ({
|
|
|
84
113
|
try {
|
|
85
114
|
const data = JSON.parse(raw);
|
|
86
115
|
if (data?.action === 'OPEN_TRAVEL_PORTAL' || data?.type === 'OPEN_TRAVEL' || data?.openTravel === true) {
|
|
87
|
-
openTravel();
|
|
116
|
+
openTravel(resolveTravelPortalUrlFromLegacyMessage(data));
|
|
88
117
|
return true;
|
|
89
118
|
}
|
|
90
119
|
} catch {
|
|
@@ -137,8 +166,8 @@ export const DualWebViewBridgeController = ({
|
|
|
137
166
|
return true;
|
|
138
167
|
}, [openTravel]);
|
|
139
168
|
const travelSource = useMemo(() => ({
|
|
140
|
-
uri:
|
|
141
|
-
}), [
|
|
169
|
+
uri: activeTravelPortalUrl
|
|
170
|
+
}), [activeTravelPortalUrl]);
|
|
142
171
|
const saversSource = useMemo(() => ({
|
|
143
172
|
uri: saversAppUrl
|
|
144
173
|
}), [saversAppUrl]);
|
|
@@ -158,33 +187,12 @@ export const DualWebViewBridgeController = ({
|
|
|
158
187
|
webviewDebuggingEnabled: __DEV__
|
|
159
188
|
}, saversKey) : null, surface === 'travel' ? /*#__PURE__*/_jsxs(View, {
|
|
160
189
|
style: styles.travelColumn,
|
|
161
|
-
children: [/*#__PURE__*/
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
accessibilityLabel: "Back",
|
|
168
|
-
style: styles.backButton,
|
|
169
|
-
children: renderTravelBackIcon ? renderTravelBackIcon() : /*#__PURE__*/_jsxs(View, {
|
|
170
|
-
style: styles.backIconWrapper,
|
|
171
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
172
|
-
style: styles.backIconShaft
|
|
173
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
174
|
-
style: styles.backIconHead
|
|
175
|
-
})]
|
|
176
|
-
})
|
|
177
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
178
|
-
style: styles.headerSpacer
|
|
179
|
-
}), partnerLogo ? /*#__PURE__*/_jsx(Image, {
|
|
180
|
-
source: {
|
|
181
|
-
uri: partnerLogo
|
|
182
|
-
},
|
|
183
|
-
resizeMode: "contain",
|
|
184
|
-
style: styles.logo
|
|
185
|
-
}) : /*#__PURE__*/_jsx(View, {
|
|
186
|
-
style: styles.logoPlaceholder
|
|
187
|
-
})]
|
|
190
|
+
children: [/*#__PURE__*/_jsx(TravelPortalHeader, {
|
|
191
|
+
onBack: backToSavers,
|
|
192
|
+
partnerLogo: partnerLogo,
|
|
193
|
+
backIconColor: travelBackIconColor,
|
|
194
|
+
topInset: travelHeaderTopInset,
|
|
195
|
+
renderBackIcon: renderTravelBackIcon
|
|
188
196
|
}), /*#__PURE__*/_jsx(WebView, {
|
|
189
197
|
ref: travelRef,
|
|
190
198
|
source: travelSource,
|
|
@@ -209,58 +217,6 @@ const styles = StyleSheet.create({
|
|
|
209
217
|
},
|
|
210
218
|
webview: {
|
|
211
219
|
flex: 1
|
|
212
|
-
},
|
|
213
|
-
header: {
|
|
214
|
-
flexDirection: 'row',
|
|
215
|
-
alignItems: 'center',
|
|
216
|
-
backgroundColor: '#fff',
|
|
217
|
-
paddingHorizontal: 16,
|
|
218
|
-
paddingTop: 12,
|
|
219
|
-
paddingBottom: 12,
|
|
220
|
-
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
221
|
-
borderBottomColor: '#E5E5E5'
|
|
222
|
-
},
|
|
223
|
-
backButton: {
|
|
224
|
-
width: 32,
|
|
225
|
-
height: 32,
|
|
226
|
-
justifyContent: 'center',
|
|
227
|
-
alignItems: 'center'
|
|
228
|
-
},
|
|
229
|
-
backIconWrapper: {
|
|
230
|
-
width: 20,
|
|
231
|
-
height: 16,
|
|
232
|
-
justifyContent: 'center',
|
|
233
|
-
position: 'relative'
|
|
234
|
-
},
|
|
235
|
-
backIconShaft: {
|
|
236
|
-
position: 'absolute',
|
|
237
|
-
left: 6,
|
|
238
|
-
right: 0,
|
|
239
|
-
height: 2,
|
|
240
|
-
backgroundColor: '#111827',
|
|
241
|
-
borderRadius: 1
|
|
242
|
-
},
|
|
243
|
-
backIconHead: {
|
|
244
|
-
width: 10,
|
|
245
|
-
height: 10,
|
|
246
|
-
borderLeftWidth: 2,
|
|
247
|
-
borderBottomWidth: 2,
|
|
248
|
-
borderColor: '#111827',
|
|
249
|
-
transform: [{
|
|
250
|
-
rotate: '45deg'
|
|
251
|
-
}],
|
|
252
|
-
marginLeft: 1
|
|
253
|
-
},
|
|
254
|
-
headerSpacer: {
|
|
255
|
-
flex: 1
|
|
256
|
-
},
|
|
257
|
-
logo: {
|
|
258
|
-
width: 120,
|
|
259
|
-
height: 32
|
|
260
|
-
},
|
|
261
|
-
logoPlaceholder: {
|
|
262
|
-
width: 120,
|
|
263
|
-
height: 32
|
|
264
220
|
}
|
|
265
221
|
});
|
|
266
222
|
export default DualWebViewBridgeController;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","useCallback","useMemo","useRef","useState","
|
|
1
|
+
{"version":3,"names":["React","useCallback","useEffect","useMemo","useRef","useState","StyleSheet","View","WebView","TravelPortalHeader","parseDualWebViewEnvelope","jsx","_jsx","jsxs","_jsxs","DEFAULT_TRAVEL_PORTAL_URL","WEBVIEW_UA_MARKER","TRAVEL_DEEP_LINK","LEGACY_OPEN_TRAVEL_ACTIONS","resolveTravelPortalUrlFromPayload","payload","trim","url","undefined","resolveTravelPortalUrlFromLegacyMessage","data","topLevel","buildInjectNativeBridgeScript","detail","JSON","stringify","DualWebViewBridgeController","saversAppUrl","travelPortalUrl","partnerLogo","travelBackIconColor","travelHeaderTopInset","renderTravelBackIcon","initialSurface","onSaversSdkMessage","onLoadEndSavers","onLoadEndTravel","saversRef","travelRef","surface","setSurface","saversKey","setSaversKey","travelKey","setTravelKey","activeTravelPortalUrl","setActiveTravelPortalUrl","openTravel","k","backToSavers","relayTo","target","from","bridge","action","ref","current","injectJavaScript","handleBridgeEnvelope","env","to","tryLegacyOpenTravel","raw","parse","type","some","s","includes","onMessageSavers","e","nativeEvent","postBack","onMessageTravel","onShouldStartLoadSavers","request","startsWith","travelSource","uri","saversSource","style","styles","root","children","source","originWhitelist","applicationNameForUserAgent","onMessage","onShouldStartLoadWithRequest","onLoadEnd","webview","sharedCookiesEnabled","incognito","webviewDebuggingEnabled","__DEV__","travelColumn","onBack","backIconColor","topInset","renderBackIcon","create","flex"],"sourceRoot":"../../../../src","sources":["services/webview/DualWebViewBridgeController.tsx"],"mappings":";;AAAA,OAAOA,KAAK,IACVC,WAAW,EACXC,SAAS,EACTC,OAAO,EACPC,MAAM,EACNC,QAAQ,QACH,OAAO;AACd,SAASC,UAAU,EAAEC,IAAI,QAAQ,cAAc;AAC/C,OAAOC,OAAO,MAAM,sBAAsB;AAC1C,SAASC,kBAAkB,QAAQ,wCAAqC;AACxE,SAEEC,wBAAwB,QACnB,8BAA2B;AAAC,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAEnC,MAAMC,yBAAyB,GAAG,sCAAsC;;AAExE;AACA,MAAMC,iBAAiB,GAAG,eAAe;;AAEzC;AACA,MAAMC,gBAAgB,GAAG,oBAAoB;AAC7C,MAAMC,0BAA0B,GAAG,CAAC,oBAAoB,EAAE,aAAa,CAAC;AAExE,SAASC,iCAAiCA,CACxCC,OAAgB,EACI;EACpB,IAAI,OAAOA,OAAO,KAAK,QAAQ,IAAIA,OAAO,CAACC,IAAI,CAAC,CAAC,EAAE;IACjD,OAAOD,OAAO,CAACC,IAAI,CAAC,CAAC;EACvB;EACA,IAAID,OAAO,IAAI,OAAOA,OAAO,KAAK,QAAQ,IAAI,KAAK,IAAIA,OAAO,EAAE;IAC9D,MAAME,GAAG,GAAIF,OAAO,CAAuBE,GAAG;IAC9C,IAAI,OAAOA,GAAG,KAAK,QAAQ,IAAIA,GAAG,CAACD,IAAI,CAAC,CAAC,EAAE;MACzC,OAAOC,GAAG,CAACD,IAAI,CAAC,CAAC;IACnB;EACF;EACA,OAAOE,SAAS;AAClB;AAEA,SAASC,uCAAuCA,CAC9CC,IAA6B,EACT;EACpB,MAAMC,QAAQ,GAAGD,IAAI,EAAEH,GAAG;EAC1B,IAAI,OAAOI,QAAQ,KAAK,QAAQ,IAAIA,QAAQ,CAACL,IAAI,CAAC,CAAC,EAAE;IACnD,OAAOK,QAAQ,CAACL,IAAI,CAAC,CAAC;EACxB;EACA,OAAOF,iCAAiC,CAACM,IAAI,EAAEL,OAAO,CAAC;AACzD;AAmBA,SAASO,6BAA6BA,CACpCC,MAA+B,EACvB;EACR,OAAO;AACT;AACA,qBAAqBC,IAAI,CAACC,SAAS,CAACF,MAAM,CAAC;AAC3C;AACA;AACA;AACA,GAAG;AACH;AAEA,OAAO,MAAMG,2BAEZ,GAAGA,CAAC;EACHC,YAAY;EACZC,eAAe,GAAGlB,yBAAyB;EAC3CmB,WAAW;EACXC,mBAAmB;EACnBC,oBAAoB,GAAG,MAAM;EAC7BC,oBAAoB;EACpBC,cAAc,GAAG,QAAQ;EACzBC,kBAAkB;EAClBC,eAAe;EACfC;AACF,CAAC,KAAK;EACJ,MAAMC,SAAS,GAAGtC,MAAM,CAAU,IAAI,CAAC;EACvC,MAAMuC,SAAS,GAAGvC,MAAM,CAAU,IAAI,CAAC;EAEvC,MAAM,CAACwC,OAAO,EAAEC,UAAU,CAAC,GAAGxC,QAAQ,CAAUiC,cAAc,CAAC;EAC/D,MAAM,CAACQ,SAAS,EAAEC,YAAY,CAAC,GAAG1C,QAAQ,CAAC,CAAC,CAAC;EAC7C,MAAM,CAAC2C,SAAS,EAAEC,YAAY,CAAC,GAAG5C,QAAQ,CAAC,CAAC,CAAC;EAC7C,MAAM,CAAC6C,qBAAqB,EAAEC,wBAAwB,CAAC,GACrD9C,QAAQ,CAAC4B,eAAe,CAAC;EAE3B/B,SAAS,CAAC,MAAM;IACdiD,wBAAwB,CAAClB,eAAe,CAAC;EAC3C,CAAC,EAAE,CAACA,eAAe,CAAC,CAAC;EAErB,MAAMmB,UAAU,GAAGnD,WAAW,CAAEqB,GAAY,IAAK;IAC/C,IAAIA,GAAG,EAAE;MACP6B,wBAAwB,CAAC7B,GAAG,CAAC;IAC/B;IACAuB,UAAU,CAAC,QAAQ,CAAC;IACpBI,YAAY,CAAEI,CAAC,IAAKA,CAAC,GAAG,CAAC,CAAC;EAC5B,CAAC,EAAE,EAAE,CAAC;EAEN,MAAMC,YAAY,GAAGrD,WAAW,CAAC,MAAM;IACrC4C,UAAU,CAAC,QAAQ,CAAC;IACpBE,YAAY,CAAEM,CAAC,IAAKA,CAAC,GAAG,CAAC,CAAC;EAC5B,CAAC,EAAE,EAAE,CAAC;EAEN,MAAME,OAAO,GAAGtD,WAAW,CACzB,CACEuD,MAA2B,EAC3BpC,OAAgB,EAChBqC,IAAyB,KACtB;IACH,MAAM7B,MAAM,GAAG;MACb8B,MAAM,EAAE,cAAc;MACtBD,IAAI;MACJE,MAAM,EAAE,OAAgB;MACxBvC;IACF,CAAC;IACD,MAAMwC,GAAG,GAAGJ,MAAM,KAAK,QAAQ,GAAGd,SAAS,GAAGC,SAAS;IACvDiB,GAAG,CAACC,OAAO,EAAEC,gBAAgB,CAACnC,6BAA6B,CAACC,MAAM,CAAC,CAAC;EACtE,CAAC,EACD,EACF,CAAC;EAED,MAAMmC,oBAAoB,GAAG9D,WAAW,CACrC+D,GAA8B,IAAc;IAC3C,QAAQA,GAAG,CAACL,MAAM;MAChB,KAAK,aAAa;QAChBP,UAAU,CAACjC,iCAAiC,CAAC6C,GAAG,CAAC5C,OAAO,CAAC,CAAC;QAC1D,OAAO,IAAI;MACb,KAAK,cAAc;QACjBkC,YAAY,CAAC,CAAC;QACd,OAAO,IAAI;MACb,KAAK,OAAO;QACV,IAAIU,GAAG,CAACC,EAAE,KAAK,QAAQ,EAAE;UACvBV,OAAO,CAAC,QAAQ,EAAES,GAAG,CAAC5C,OAAO,EAAE4C,GAAG,CAACP,IAAI,CAAC;UACxC,OAAO,IAAI;QACb;QACA,IAAIO,GAAG,CAACC,EAAE,KAAK,QAAQ,EAAE;UACvBV,OAAO,CAAC,QAAQ,EAAES,GAAG,CAAC5C,OAAO,EAAE4C,GAAG,CAACP,IAAI,CAAC;UACxC,OAAO,IAAI;QACb;QACA,OAAO,KAAK;MACd;QACE,OAAO,KAAK;IAChB;EACF,CAAC,EACD,CAACH,YAAY,EAAEF,UAAU,EAAEG,OAAO,CACpC,CAAC;EAED,MAAMW,mBAAmB,GAAGjE,WAAW,CACpCkE,GAAW,IAAc;IACxB,IAAI,CAACA,GAAG,IAAI,OAAOA,GAAG,KAAK,QAAQ,EAAE;MACnC,OAAO,KAAK;IACd;IACA,IAAI;MACF,MAAM1C,IAAI,GAAGI,IAAI,CAACuC,KAAK,CAACD,GAAG,CAAC;MAC5B,IACE1C,IAAI,EAAEkC,MAAM,KAAK,oBAAoB,IACrClC,IAAI,EAAE4C,IAAI,KAAK,aAAa,IAC5B5C,IAAI,EAAE2B,UAAU,KAAK,IAAI,EACzB;QACAA,UAAU,CAAC5B,uCAAuC,CAACC,IAAI,CAAC,CAAC;QACzD,OAAO,IAAI;MACb;IACF,CAAC,CAAC,MAAM;MACN;IAAA;IAEF,IAAIP,0BAA0B,CAACoD,IAAI,CAAEC,CAAC,IAAKJ,GAAG,CAACK,QAAQ,CAACD,CAAC,CAAC,CAAC,EAAE;MAC3DnB,UAAU,CAAC,CAAC;MACZ,OAAO,IAAI;IACb;IACA,OAAO,KAAK;EACd,CAAC,EACD,CAACA,UAAU,CACb,CAAC;EAED,MAAMqB,eAAe,GAAGxE,WAAW,CAChCyE,CAAoC,IAAK;IACxC,MAAMP,GAAG,GAAGO,CAAC,EAAEC,WAAW,EAAElD,IAAI,IAAI,EAAE;IACtC,MAAMuC,GAAG,GAAGtD,wBAAwB,CAACyD,GAAG,CAAC;IAEzC,IAAIH,GAAG,IAAIA,GAAG,CAACP,IAAI,KAAK,QAAQ,IAAIM,oBAAoB,CAACC,GAAG,CAAC,EAAE;MAC7D;IACF;IACA,IAAIE,mBAAmB,CAACC,GAAG,CAAC,EAAE;MAC5B;IACF;IAEA,MAAMS,QAAQ,GAAInD,IAAa,IAAK;MAClC,MAAMG,MAAM,GAAG;QACb8B,MAAM,EAAE,cAAc;QACtBD,IAAI,EAAE,QAAQ;QACdE,MAAM,EAAE,OAAO;QACfvC,OAAO,EAAEK;MACX,CAAC;MACDiB,SAAS,CAACmB,OAAO,EAAEC,gBAAgB,CACjCnC,6BAA6B,CAACC,MAAM,CACtC,CAAC;IACH,CAAC;IACDW,kBAAkB,GAAG4B,GAAG,EAAES,QAAQ,CAAC;EACrC,CAAC,EACD,CAACb,oBAAoB,EAAExB,kBAAkB,EAAE2B,mBAAmB,CAChE,CAAC;EAED,MAAMW,eAAe,GAAG5E,WAAW,CAChCyE,CAAoC,IAAK;IACxC,MAAMP,GAAG,GAAGO,CAAC,EAAEC,WAAW,EAAElD,IAAI,IAAI,EAAE;IACtC,MAAMuC,GAAG,GAAGtD,wBAAwB,CAACyD,GAAG,CAAC;IAEzC,IAAIH,GAAG,IAAIA,GAAG,CAACP,IAAI,KAAK,QAAQ,IAAIM,oBAAoB,CAACC,GAAG,CAAC,EAAE;MAC7D;IACF;IACA,IAAIE,mBAAmB,CAACC,GAAG,CAAC,EAAE;MAC5B;IACF;EACF,CAAC,EACD,CAACJ,oBAAoB,EAAEG,mBAAmB,CAC5C,CAAC;EAED,MAAMY,uBAAuB,GAAG7E,WAAW,CACxC8E,OAAwB,IAAK;IAC5B,MAAM;MAAEzD;IAAI,CAAC,GAAGyD,OAAO;IACvB,IAAIzD,GAAG,CAAC0D,UAAU,CAAC/D,gBAAgB,CAAC,IAAIK,GAAG,KAAK,qBAAqB,EAAE;MACrE8B,UAAU,CAAC,CAAC;MACZ,OAAO,KAAK;IACd;IACA,OAAO,IAAI;EACb,CAAC,EACD,CAACA,UAAU,CACb,CAAC;EAED,MAAM6B,YAAY,GAAG9E,OAAO,CAC1B,OAAO;IAAE+E,GAAG,EAAEhC;EAAsB,CAAC,CAAC,EACtC,CAACA,qBAAqB,CACxB,CAAC;EACD,MAAMiC,YAAY,GAAGhF,OAAO,CAAC,OAAO;IAAE+E,GAAG,EAAElD;EAAa,CAAC,CAAC,EAAE,CAACA,YAAY,CAAC,CAAC;EAE3E,oBACElB,KAAA,CAACP,IAAI;IAAC6E,KAAK,EAAEC,MAAM,CAACC,IAAK;IAAAC,QAAA,GACtB3C,OAAO,KAAK,QAAQ,gBACnBhC,IAAA,CAACJ,OAAO;MAENoD,GAAG,EAAElB,SAAU;MACf8C,MAAM,EAAEL,YAAa;MACrBM,eAAe,EAAE,CAAC,GAAG,CAAE;MACvBC,2BAA2B,EAAE1E,iBAAkB;MAC/C2E,SAAS,EAAElB,eAAgB;MAC3BmB,4BAA4B,EAAEd,uBAAwB;MACtDe,SAAS,EAAErD,eAAgB;MAC3B4C,KAAK,EAAEC,MAAM,CAACS,OAAQ;MACtBC,oBAAoB;MACpBC,SAAS,EAAE,KAAM;MACjBC,uBAAuB,EAAEC;IAAQ,GAX5BpD,SAYN,CAAC,GACA,IAAI,EAEPF,OAAO,KAAK,QAAQ,gBACnB9B,KAAA,CAACP,IAAI;MAAC6E,KAAK,EAAEC,MAAM,CAACc,YAAa;MAAAZ,QAAA,gBAC/B3E,IAAA,CAACH,kBAAkB;QACjB2F,MAAM,EAAE9C,YAAa;QACrBpB,WAAW,EAAEA,WAAY;QACzBmE,aAAa,EAAElE,mBAAoB;QACnCmE,QAAQ,EAAElE,oBAAqB;QAC/BmE,cAAc,EAAElE;MAAqB,CACtC,CAAC,eACFzB,IAAA,CAACJ,OAAO;QAENoD,GAAG,EAAEjB,SAAU;QACf6C,MAAM,EAAEP,YAAa;QACrBQ,eAAe,EAAE,CAAC,GAAG,CAAE;QACvBC,2BAA2B,EAAE1E,iBAAkB;QAC/C2E,SAAS,EAAEd,eAAgB;QAC3BgB,SAAS,EAAEpD,eAAgB;QAC3B2C,KAAK,EAAEC,MAAM,CAACS,OAAQ;QACtBC,oBAAoB;QACpBC,SAAS,EAAE,KAAM;QACjBC,uBAAuB,EAAEC;MAAQ,GAV5BlD,SAWN,CAAC;IAAA,CACE,CAAC,GACL,IAAI;EAAA,CACJ,CAAC;AAEX,CAAC;AAED,MAAMqC,MAAM,GAAG/E,UAAU,CAACkG,MAAM,CAAC;EAC/BlB,IAAI,EAAE;IAAEmB,IAAI,EAAE;EAAE,CAAC;EACjBN,YAAY,EAAE;IAAEM,IAAI,EAAE;EAAE,CAAC;EACzBX,OAAO,EAAE;IAAEW,IAAI,EAAE;EAAE;AACrB,CAAC,CAAC;AAEF,eAAe1E,2BAA2B","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["parseDualWebViewEnvelope","raw","o","JSON","parse","bridge","from","action"],"sourceRoot":"../../../../src","sources":["services/webview/dualWebViewBridge.types.ts"],"mappings":";;AAAA;AACA;AACA;AACA;;
|
|
1
|
+
{"version":3,"names":["parseDualWebViewEnvelope","raw","o","JSON","parse","bridge","from","action"],"sourceRoot":"../../../../src","sources":["services/webview/dualWebViewBridge.types.ts"],"mappings":";;AAAA;AACA;AACA;AACA;;AAkBA,OAAO,SAASA,wBAAwBA,CACtCC,GAAW,EACuB;EAClC,IAAI;IACF,MAAMC,CAAC,GAAGC,IAAI,CAACC,KAAK,CAACH,GAAG,CAA8B;IACtD,IAAIC,CAAC,EAAEG,MAAM,KAAK,cAAc,IAAIH,CAAC,EAAEI,IAAI,IAAIJ,CAAC,EAAEK,MAAM,EAAE;MACxD,OAAOL,CAAC;IACV;EACF,CAAC,CAAC,MAAM;IACN;EAAA;EAEF,OAAO,IAAI;AACb","ignoreList":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/** Matches `m.saversapp.com/assets/svg/BackIcon.tsx` (17×16). */
|
|
3
|
+
export type TravelPortalBackIconProps = {
|
|
4
|
+
height?: number;
|
|
5
|
+
width?: number;
|
|
6
|
+
color?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const TravelPortalBackIcon: React.FC<TravelPortalBackIconProps>;
|
|
9
|
+
export default TravelPortalBackIcon;
|
|
10
|
+
//# sourceMappingURL=TravelPortalBackIcon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TravelPortalBackIcon.d.ts","sourceRoot":"","sources":["../../../../src/components/TravelPortalBackIcon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,iEAAiE;AACjE,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAIF,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAWpE,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
export type TravelPortalHeaderInsets = 'none' | 'safe-area';
|
|
4
|
+
export type TravelPortalHeaderProps = {
|
|
5
|
+
onBack: () => void;
|
|
6
|
+
partnerLogo?: string;
|
|
7
|
+
backIconColor?: string;
|
|
8
|
+
renderBackIcon?: () => React.ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* `none` — matches m.saversapp `Header` (`py-4` only). Use when the host
|
|
11
|
+
* already reserves top space (e.g. clo-app `MainBodyComponent` hosting bar).
|
|
12
|
+
* `safe-area` — adds device top inset (standalone / full-screen hosts).
|
|
13
|
+
*/
|
|
14
|
+
topInset?: TravelPortalHeaderInsets;
|
|
15
|
+
style?: StyleProp<ViewStyle>;
|
|
16
|
+
};
|
|
17
|
+
export declare const TravelPortalHeader: React.FC<TravelPortalHeaderProps>;
|
|
18
|
+
export default TravelPortalHeader;
|
|
19
|
+
//# sourceMappingURL=TravelPortalHeader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TravelPortalHeader.d.ts","sourceRoot":"","sources":["../../../../src/components/TravelPortalHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAItB,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,WAAW,CAAC;AAY5D,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,wBAAwB,CAAC;IACpC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CA6EhE,CAAC;AA2BF,eAAe,kBAAkB,CAAC"}
|
|
@@ -4,6 +4,10 @@ export type DualWebViewBridgeControllerProps = {
|
|
|
4
4
|
saversAppUrl: string;
|
|
5
5
|
travelPortalUrl?: string;
|
|
6
6
|
partnerLogo?: string;
|
|
7
|
+
/** Matches m.saversapp `theme.colors.quaternary` (default `#66CC99`). */
|
|
8
|
+
travelBackIconColor?: string;
|
|
9
|
+
/** @default 'none' — host apps like clo-app already reserve top space when hosting. */
|
|
10
|
+
travelHeaderTopInset?: 'none' | 'safe-area';
|
|
7
11
|
renderTravelBackIcon?: () => React.ReactNode;
|
|
8
12
|
initialSurface?: Surface;
|
|
9
13
|
onSaversSdkMessage?: (raw: string, postBack: (data: unknown) => void) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DualWebViewBridgeController.d.ts","sourceRoot":"","sources":["../../../../../src/services/webview/DualWebViewBridgeController.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"DualWebViewBridgeController.d.ts","sourceRoot":"","sources":["../../../../../src/services/webview/DualWebViewBridgeController.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AA2Cf,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE1C,MAAM,MAAM,gCAAgC,GAAG;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uFAAuF;IACvF,oBAAoB,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5C,oBAAoB,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IAC7C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,CAAC;IAC9E,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B,CAAC;AAcF,eAAO,MAAM,2BAA2B,EAAE,KAAK,CAAC,EAAE,CAChD,gCAAgC,CAwNjC,CAAC;AAQF,eAAe,2BAA2B,CAAC"}
|
|
@@ -9,11 +9,13 @@ export type DualWebViewBridgeEnvelope = {
|
|
|
9
9
|
/**
|
|
10
10
|
* relay — forward payload to the other WebView via injectJavaScript + CustomEvent.
|
|
11
11
|
* open_travel — show travel surface (reloads travel WebView).
|
|
12
|
+
* payload may be `{ url: string }` (SSO initialize URL from hosted m.saversapp.com).
|
|
12
13
|
* close_travel — same as native back (reloads Savers WebView).
|
|
13
14
|
*/
|
|
14
15
|
action: 'relay' | 'open_travel' | 'close_travel';
|
|
15
16
|
/** For relay: which surface should receive the event. */
|
|
16
17
|
to?: 'savers' | 'travel';
|
|
18
|
+
/** open_travel: `{ url: string }` — travel portal URL to load. */
|
|
17
19
|
payload?: unknown;
|
|
18
20
|
};
|
|
19
21
|
export declare function parseDualWebViewEnvelope(raw: string): DualWebViewBridgeEnvelope | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dualWebViewBridge.types.d.ts","sourceRoot":"","sources":["../../../../../src/services/webview/dualWebViewBridge.types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,cAAc,CAAC;IACvB,+CAA+C;IAC/C,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B
|
|
1
|
+
{"version":3,"file":"dualWebViewBridge.types.d.ts","sourceRoot":"","sources":["../../../../../src/services/webview/dualWebViewBridge.types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,cAAc,CAAC;IACvB,+CAA+C;IAC/C,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B;;;;;OAKG;IACH,MAAM,EAAE,OAAO,GAAG,aAAa,GAAG,cAAc,CAAC;IACjD,yDAAyD;IACzD,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACzB,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,GACV,yBAAyB,GAAG,IAAI,CAUlC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savers_app/react-native-sandbox-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Cross-platform React Native SDK exposing native features (maps, dial pad, browser), device ID/location and session utilities, a URL generator, and a WebView message bridge to trigger actions from web content.",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"react-native": "./src/index.tsx",
|
|
@@ -94,6 +94,7 @@
|
|
|
94
94
|
"react-native": "0.81.5",
|
|
95
95
|
"react-native-builder-bob": "^0.40.13",
|
|
96
96
|
"react-native-safe-area-context": "^5.5.2",
|
|
97
|
+
"react-native-svg": "^15.15.1",
|
|
97
98
|
"react-native-webview": "^13.16.0",
|
|
98
99
|
"release-it": "^19.0.4",
|
|
99
100
|
"typescript": "^5.9.2"
|
|
@@ -108,6 +109,7 @@
|
|
|
108
109
|
"react-native-aes-gcm-crypto": "*",
|
|
109
110
|
"react-native-device-info": "*",
|
|
110
111
|
"react-native-safe-area-context": "*",
|
|
112
|
+
"react-native-svg": "*",
|
|
111
113
|
"react-native-webview": "*"
|
|
112
114
|
},
|
|
113
115
|
"workspaces": [
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare module 'react-native-svg' {
|
|
2
|
+
import type * as React from 'react';
|
|
3
|
+
import type { ViewProps } from 'react-native';
|
|
4
|
+
|
|
5
|
+
export interface SvgProps extends ViewProps {
|
|
6
|
+
width?: number | string;
|
|
7
|
+
height?: number | string;
|
|
8
|
+
viewBox?: string;
|
|
9
|
+
fill?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface PathProps {
|
|
13
|
+
d?: string;
|
|
14
|
+
fill?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Svg: React.FC<SvgProps>;
|
|
18
|
+
export const Path: React.FC<PathProps>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Path, Svg } from 'react-native-svg';
|
|
3
|
+
|
|
4
|
+
/** Matches `m.saversapp.com/assets/svg/BackIcon.tsx` (17×16). */
|
|
5
|
+
export type TravelPortalBackIconProps = {
|
|
6
|
+
height?: number;
|
|
7
|
+
width?: number;
|
|
8
|
+
color?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DEFAULT_COLOR = '#66CC99';
|
|
12
|
+
|
|
13
|
+
export const TravelPortalBackIcon: React.FC<TravelPortalBackIconProps> = ({
|
|
14
|
+
height = 16,
|
|
15
|
+
width = 17,
|
|
16
|
+
color = DEFAULT_COLOR,
|
|
17
|
+
}) => (
|
|
18
|
+
<Svg width={width} height={height} viewBox="0 0 17 16" fill="none">
|
|
19
|
+
<Path
|
|
20
|
+
d="M.293 7.293a1 1 0 000 1.414l6.364 6.364a1 1 0 001.414-1.414L2.414 8l5.657-5.657A1 1 0 006.657.93L.293 7.293zM1 9h16V7H1v2z"
|
|
21
|
+
fill={color}
|
|
22
|
+
/>
|
|
23
|
+
</Svg>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export default TravelPortalBackIcon;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Image,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
TouchableOpacity,
|
|
6
|
+
View,
|
|
7
|
+
type StyleProp,
|
|
8
|
+
type ViewStyle,
|
|
9
|
+
} from 'react-native';
|
|
10
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
11
|
+
import { TravelPortalBackIcon } from './TravelPortalBackIcon';
|
|
12
|
+
|
|
13
|
+
export type TravelPortalHeaderInsets = 'none' | 'safe-area';
|
|
14
|
+
|
|
15
|
+
/** Horizontal padding: Tailwind `px-3.5` in m.saversapp Header. */
|
|
16
|
+
const HEADER_PADDING_X = 14;
|
|
17
|
+
/** Vertical padding: Tailwind `py-4` in m.saversapp Header. */
|
|
18
|
+
const HEADER_PADDING_Y = 16;
|
|
19
|
+
/** Row min height: Tailwind `min-h-[60px]` in m.saversapp Header. */
|
|
20
|
+
const HEADER_ROW_MIN_HEIGHT = 60;
|
|
21
|
+
const LOGO_MAX_WIDTH = 160;
|
|
22
|
+
const LOGO_MAX_HEIGHT = 70;
|
|
23
|
+
const LOGO_MIN_HEIGHT = 40;
|
|
24
|
+
|
|
25
|
+
export type TravelPortalHeaderProps = {
|
|
26
|
+
onBack: () => void;
|
|
27
|
+
partnerLogo?: string;
|
|
28
|
+
backIconColor?: string;
|
|
29
|
+
renderBackIcon?: () => React.ReactNode;
|
|
30
|
+
/**
|
|
31
|
+
* `none` — matches m.saversapp `Header` (`py-4` only). Use when the host
|
|
32
|
+
* already reserves top space (e.g. clo-app `MainBodyComponent` hosting bar).
|
|
33
|
+
* `safe-area` — adds device top inset (standalone / full-screen hosts).
|
|
34
|
+
*/
|
|
35
|
+
topInset?: TravelPortalHeaderInsets;
|
|
36
|
+
style?: StyleProp<ViewStyle>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const TravelPortalHeader: React.FC<TravelPortalHeaderProps> = ({
|
|
40
|
+
onBack,
|
|
41
|
+
partnerLogo,
|
|
42
|
+
backIconColor,
|
|
43
|
+
renderBackIcon,
|
|
44
|
+
topInset = 'none',
|
|
45
|
+
style,
|
|
46
|
+
}) => {
|
|
47
|
+
const insets = useSafeAreaInsets();
|
|
48
|
+
const paddingTop =
|
|
49
|
+
topInset === 'safe-area' ? insets.top + HEADER_PADDING_Y : HEADER_PADDING_Y;
|
|
50
|
+
const [logoWidth, setLogoWidth] = useState(LOGO_MAX_WIDTH);
|
|
51
|
+
const [logoHeight, setLogoHeight] = useState(LOGO_MAX_HEIGHT);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (!partnerLogo) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
Image.getSize(
|
|
58
|
+
partnerLogo,
|
|
59
|
+
(srcWidth, srcHeight) => {
|
|
60
|
+
setLogoWidth(srcWidth > LOGO_MAX_WIDTH ? LOGO_MAX_WIDTH : srcWidth);
|
|
61
|
+
setLogoHeight(
|
|
62
|
+
srcHeight > LOGO_MAX_HEIGHT ? LOGO_MAX_HEIGHT : srcHeight
|
|
63
|
+
);
|
|
64
|
+
},
|
|
65
|
+
() => {
|
|
66
|
+
setLogoWidth(LOGO_MAX_WIDTH);
|
|
67
|
+
setLogoHeight(LOGO_MAX_HEIGHT);
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
}, [partnerLogo]);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<View
|
|
74
|
+
style={[
|
|
75
|
+
styles.container,
|
|
76
|
+
{
|
|
77
|
+
paddingTop,
|
|
78
|
+
paddingBottom: HEADER_PADDING_Y,
|
|
79
|
+
},
|
|
80
|
+
style,
|
|
81
|
+
]}
|
|
82
|
+
>
|
|
83
|
+
<View style={styles.row}>
|
|
84
|
+
<TouchableOpacity
|
|
85
|
+
onPress={onBack}
|
|
86
|
+
hitSlop={16}
|
|
87
|
+
accessibilityRole="button"
|
|
88
|
+
accessibilityLabel="Back"
|
|
89
|
+
style={styles.backButton}
|
|
90
|
+
>
|
|
91
|
+
{renderBackIcon ? (
|
|
92
|
+
renderBackIcon()
|
|
93
|
+
) : (
|
|
94
|
+
<TravelPortalBackIcon color={backIconColor} />
|
|
95
|
+
)}
|
|
96
|
+
</TouchableOpacity>
|
|
97
|
+
|
|
98
|
+
{partnerLogo ? (
|
|
99
|
+
<Image
|
|
100
|
+
source={{ uri: partnerLogo }}
|
|
101
|
+
resizeMode="contain"
|
|
102
|
+
style={[
|
|
103
|
+
styles.logo,
|
|
104
|
+
{
|
|
105
|
+
width: logoWidth,
|
|
106
|
+
height: logoHeight,
|
|
107
|
+
},
|
|
108
|
+
]}
|
|
109
|
+
/>
|
|
110
|
+
) : (
|
|
111
|
+
<View style={styles.logoPlaceholder} />
|
|
112
|
+
)}
|
|
113
|
+
</View>
|
|
114
|
+
</View>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const styles = StyleSheet.create({
|
|
119
|
+
container: {
|
|
120
|
+
backgroundColor: '#fff',
|
|
121
|
+
paddingHorizontal: HEADER_PADDING_X,
|
|
122
|
+
},
|
|
123
|
+
row: {
|
|
124
|
+
flexDirection: 'row',
|
|
125
|
+
alignItems: 'center',
|
|
126
|
+
justifyContent: 'space-between',
|
|
127
|
+
minHeight: HEADER_ROW_MIN_HEIGHT,
|
|
128
|
+
},
|
|
129
|
+
backButton: {
|
|
130
|
+
paddingVertical: 8,
|
|
131
|
+
justifyContent: 'center',
|
|
132
|
+
},
|
|
133
|
+
logo: {
|
|
134
|
+
minHeight: LOGO_MIN_HEIGHT,
|
|
135
|
+
alignSelf: 'flex-end',
|
|
136
|
+
},
|
|
137
|
+
logoPlaceholder: {
|
|
138
|
+
width: LOGO_MAX_WIDTH,
|
|
139
|
+
minHeight: LOGO_MIN_HEIGHT,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
export default TravelPortalHeader;
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import { StyleSheet, View } from 'react-native';
|
|
3
9
|
import WebView from 'react-native-webview';
|
|
10
|
+
import { TravelPortalHeader } from '../../components/TravelPortalHeader';
|
|
4
11
|
import {
|
|
5
12
|
type DualWebViewBridgeEnvelope,
|
|
6
13
|
parseDualWebViewEnvelope,
|
|
@@ -15,12 +22,41 @@ const WEBVIEW_UA_MARKER = 'CloAppWebView';
|
|
|
15
22
|
const TRAVEL_DEEP_LINK = 'saversapp://travel';
|
|
16
23
|
const LEGACY_OPEN_TRAVEL_ACTIONS = ['OPEN_TRAVEL_PORTAL', 'OPEN_TRAVEL'];
|
|
17
24
|
|
|
25
|
+
function resolveTravelPortalUrlFromPayload(
|
|
26
|
+
payload: unknown
|
|
27
|
+
): string | undefined {
|
|
28
|
+
if (typeof payload === 'string' && payload.trim()) {
|
|
29
|
+
return payload.trim();
|
|
30
|
+
}
|
|
31
|
+
if (payload && typeof payload === 'object' && 'url' in payload) {
|
|
32
|
+
const url = (payload as { url?: unknown }).url;
|
|
33
|
+
if (typeof url === 'string' && url.trim()) {
|
|
34
|
+
return url.trim();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resolveTravelPortalUrlFromLegacyMessage(
|
|
41
|
+
data: Record<string, unknown>
|
|
42
|
+
): string | undefined {
|
|
43
|
+
const topLevel = data?.url;
|
|
44
|
+
if (typeof topLevel === 'string' && topLevel.trim()) {
|
|
45
|
+
return topLevel.trim();
|
|
46
|
+
}
|
|
47
|
+
return resolveTravelPortalUrlFromPayload(data?.payload);
|
|
48
|
+
}
|
|
49
|
+
|
|
18
50
|
export type Surface = 'savers' | 'travel';
|
|
19
51
|
|
|
20
52
|
export type DualWebViewBridgeControllerProps = {
|
|
21
53
|
saversAppUrl: string;
|
|
22
54
|
travelPortalUrl?: string;
|
|
23
55
|
partnerLogo?: string;
|
|
56
|
+
/** Matches m.saversapp `theme.colors.quaternary` (default `#66CC99`). */
|
|
57
|
+
travelBackIconColor?: string;
|
|
58
|
+
/** @default 'none' — host apps like clo-app already reserve top space when hosting. */
|
|
59
|
+
travelHeaderTopInset?: 'none' | 'safe-area';
|
|
24
60
|
renderTravelBackIcon?: () => React.ReactNode;
|
|
25
61
|
initialSurface?: Surface;
|
|
26
62
|
onSaversSdkMessage?: (raw: string, postBack: (data: unknown) => void) => void;
|
|
@@ -46,6 +82,8 @@ export const DualWebViewBridgeController: React.FC<
|
|
|
46
82
|
saversAppUrl,
|
|
47
83
|
travelPortalUrl = DEFAULT_TRAVEL_PORTAL_URL,
|
|
48
84
|
partnerLogo,
|
|
85
|
+
travelBackIconColor,
|
|
86
|
+
travelHeaderTopInset = 'none',
|
|
49
87
|
renderTravelBackIcon,
|
|
50
88
|
initialSurface = 'savers',
|
|
51
89
|
onSaversSdkMessage,
|
|
@@ -58,8 +96,17 @@ export const DualWebViewBridgeController: React.FC<
|
|
|
58
96
|
const [surface, setSurface] = useState<Surface>(initialSurface);
|
|
59
97
|
const [saversKey, setSaversKey] = useState(0);
|
|
60
98
|
const [travelKey, setTravelKey] = useState(0);
|
|
99
|
+
const [activeTravelPortalUrl, setActiveTravelPortalUrl] =
|
|
100
|
+
useState(travelPortalUrl);
|
|
61
101
|
|
|
62
|
-
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
setActiveTravelPortalUrl(travelPortalUrl);
|
|
104
|
+
}, [travelPortalUrl]);
|
|
105
|
+
|
|
106
|
+
const openTravel = useCallback((url?: string) => {
|
|
107
|
+
if (url) {
|
|
108
|
+
setActiveTravelPortalUrl(url);
|
|
109
|
+
}
|
|
63
110
|
setSurface('travel');
|
|
64
111
|
setTravelKey((k) => k + 1);
|
|
65
112
|
}, []);
|
|
@@ -91,7 +138,7 @@ export const DualWebViewBridgeController: React.FC<
|
|
|
91
138
|
(env: DualWebViewBridgeEnvelope): boolean => {
|
|
92
139
|
switch (env.action) {
|
|
93
140
|
case 'open_travel':
|
|
94
|
-
openTravel();
|
|
141
|
+
openTravel(resolveTravelPortalUrlFromPayload(env.payload));
|
|
95
142
|
return true;
|
|
96
143
|
case 'close_travel':
|
|
97
144
|
backToSavers();
|
|
@@ -125,7 +172,7 @@ export const DualWebViewBridgeController: React.FC<
|
|
|
125
172
|
data?.type === 'OPEN_TRAVEL' ||
|
|
126
173
|
data?.openTravel === true
|
|
127
174
|
) {
|
|
128
|
-
openTravel();
|
|
175
|
+
openTravel(resolveTravelPortalUrlFromLegacyMessage(data));
|
|
129
176
|
return true;
|
|
130
177
|
}
|
|
131
178
|
} catch {
|
|
@@ -196,8 +243,8 @@ export const DualWebViewBridgeController: React.FC<
|
|
|
196
243
|
);
|
|
197
244
|
|
|
198
245
|
const travelSource = useMemo(
|
|
199
|
-
() => ({ uri:
|
|
200
|
-
[
|
|
246
|
+
() => ({ uri: activeTravelPortalUrl }),
|
|
247
|
+
[activeTravelPortalUrl]
|
|
201
248
|
);
|
|
202
249
|
const saversSource = useMemo(() => ({ uri: saversAppUrl }), [saversAppUrl]);
|
|
203
250
|
|
|
@@ -222,36 +269,13 @@ export const DualWebViewBridgeController: React.FC<
|
|
|
222
269
|
|
|
223
270
|
{surface === 'travel' ? (
|
|
224
271
|
<View style={styles.travelColumn}>
|
|
225
|
-
<
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
>
|
|
233
|
-
{renderTravelBackIcon ? (
|
|
234
|
-
renderTravelBackIcon()
|
|
235
|
-
) : (
|
|
236
|
-
<View style={styles.backIconWrapper}>
|
|
237
|
-
<View style={styles.backIconShaft} />
|
|
238
|
-
<View style={styles.backIconHead} />
|
|
239
|
-
</View>
|
|
240
|
-
)}
|
|
241
|
-
</TouchableOpacity>
|
|
242
|
-
|
|
243
|
-
<View style={styles.headerSpacer} />
|
|
244
|
-
|
|
245
|
-
{partnerLogo ? (
|
|
246
|
-
<Image
|
|
247
|
-
source={{ uri: partnerLogo }}
|
|
248
|
-
resizeMode="contain"
|
|
249
|
-
style={styles.logo}
|
|
250
|
-
/>
|
|
251
|
-
) : (
|
|
252
|
-
<View style={styles.logoPlaceholder} />
|
|
253
|
-
)}
|
|
254
|
-
</View>
|
|
272
|
+
<TravelPortalHeader
|
|
273
|
+
onBack={backToSavers}
|
|
274
|
+
partnerLogo={partnerLogo}
|
|
275
|
+
backIconColor={travelBackIconColor}
|
|
276
|
+
topInset={travelHeaderTopInset}
|
|
277
|
+
renderBackIcon={renderTravelBackIcon}
|
|
278
|
+
/>
|
|
255
279
|
<WebView
|
|
256
280
|
key={travelKey}
|
|
257
281
|
ref={travelRef}
|
|
@@ -275,56 +299,6 @@ const styles = StyleSheet.create({
|
|
|
275
299
|
root: { flex: 1 },
|
|
276
300
|
travelColumn: { flex: 1 },
|
|
277
301
|
webview: { flex: 1 },
|
|
278
|
-
header: {
|
|
279
|
-
flexDirection: 'row',
|
|
280
|
-
alignItems: 'center',
|
|
281
|
-
backgroundColor: '#fff',
|
|
282
|
-
paddingHorizontal: 16,
|
|
283
|
-
paddingTop: 12,
|
|
284
|
-
paddingBottom: 12,
|
|
285
|
-
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
286
|
-
borderBottomColor: '#E5E5E5',
|
|
287
|
-
},
|
|
288
|
-
backButton: {
|
|
289
|
-
width: 32,
|
|
290
|
-
height: 32,
|
|
291
|
-
justifyContent: 'center',
|
|
292
|
-
alignItems: 'center',
|
|
293
|
-
},
|
|
294
|
-
backIconWrapper: {
|
|
295
|
-
width: 20,
|
|
296
|
-
height: 16,
|
|
297
|
-
justifyContent: 'center',
|
|
298
|
-
position: 'relative',
|
|
299
|
-
},
|
|
300
|
-
backIconShaft: {
|
|
301
|
-
position: 'absolute',
|
|
302
|
-
left: 6,
|
|
303
|
-
right: 0,
|
|
304
|
-
height: 2,
|
|
305
|
-
backgroundColor: '#111827',
|
|
306
|
-
borderRadius: 1,
|
|
307
|
-
},
|
|
308
|
-
backIconHead: {
|
|
309
|
-
width: 10,
|
|
310
|
-
height: 10,
|
|
311
|
-
borderLeftWidth: 2,
|
|
312
|
-
borderBottomWidth: 2,
|
|
313
|
-
borderColor: '#111827',
|
|
314
|
-
transform: [{ rotate: '45deg' }],
|
|
315
|
-
marginLeft: 1,
|
|
316
|
-
},
|
|
317
|
-
headerSpacer: {
|
|
318
|
-
flex: 1,
|
|
319
|
-
},
|
|
320
|
-
logo: {
|
|
321
|
-
width: 120,
|
|
322
|
-
height: 32,
|
|
323
|
-
},
|
|
324
|
-
logoPlaceholder: {
|
|
325
|
-
width: 120,
|
|
326
|
-
height: 32,
|
|
327
|
-
},
|
|
328
302
|
});
|
|
329
303
|
|
|
330
304
|
export default DualWebViewBridgeController;
|
|
@@ -9,11 +9,13 @@ export type DualWebViewBridgeEnvelope = {
|
|
|
9
9
|
/**
|
|
10
10
|
* relay — forward payload to the other WebView via injectJavaScript + CustomEvent.
|
|
11
11
|
* open_travel — show travel surface (reloads travel WebView).
|
|
12
|
+
* payload may be `{ url: string }` (SSO initialize URL from hosted m.saversapp.com).
|
|
12
13
|
* close_travel — same as native back (reloads Savers WebView).
|
|
13
14
|
*/
|
|
14
15
|
action: 'relay' | 'open_travel' | 'close_travel';
|
|
15
16
|
/** For relay: which surface should receive the event. */
|
|
16
17
|
to?: 'savers' | 'travel';
|
|
18
|
+
/** open_travel: `{ url: string }` — travel portal URL to load. */
|
|
17
19
|
payload?: unknown;
|
|
18
20
|
};
|
|
19
21
|
|