@savers_app/react-native-sandbox-sdk 1.3.0 → 1.5.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.
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=react-native-svg.d.js.map
@@ -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":";;AAAA,OAAOA,KAAK,IAAIC,SAAS,EAAEC,QAAQ,QAAQ,OAAO;AAClD,SACEC,KAAK,EACLC,UAAU,EACVC,gBAAgB,EAChBC,IAAI,QAGC,cAAc;AACrB,SAASC,iBAAiB,QAAQ,gCAAgC;AAClE,SAASC,oBAAoB,QAAQ,2BAAwB;AAAC,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAI9D;AACA,MAAMC,gBAAgB,GAAG,EAAE;AAC3B;AACA,MAAMC,gBAAgB,GAAG,EAAE;AAC3B;AACA,MAAMC,qBAAqB,GAAG,EAAE;AAChC,MAAMC,cAAc,GAAG,GAAG;AAC1B,MAAMC,eAAe,GAAG,EAAE;AAC1B,MAAMC,eAAe,GAAG,EAAE;AAgB1B,OAAO,MAAMC,kBAAqD,GAAGA,CAAC;EACpEC,MAAM;EACNC,WAAW;EACXC,aAAa;EACbC,cAAc;EACdC,QAAQ,GAAG,MAAM;EACjBC;AACF,CAAC,KAAK;EACJ,MAAMC,MAAM,GAAGnB,iBAAiB,CAAC,CAAC;EAClC,MAAMoB,UAAU,GACdH,QAAQ,KAAK,WAAW,GAAGE,MAAM,CAACE,GAAG,GAAGd,gBAAgB,GAAGA,gBAAgB;EAC7E,MAAM,CAACe,SAAS,EAAEC,YAAY,CAAC,GAAG5B,QAAQ,CAACc,cAAc,CAAC;EAC1D,MAAM,CAACe,UAAU,EAAEC,aAAa,CAAC,GAAG9B,QAAQ,CAACe,eAAe,CAAC;EAE7DhB,SAAS,CAAC,MAAM;IACd,IAAI,CAACoB,WAAW,EAAE;MAChB;IACF;IACAlB,KAAK,CAAC8B,OAAO,CACXZ,WAAW,EACX,CAACa,QAAQ,EAAEC,SAAS,KAAK;MACvBL,YAAY,CAACI,QAAQ,GAAGlB,cAAc,GAAGA,cAAc,GAAGkB,QAAQ,CAAC;MACnEF,aAAa,CACXG,SAAS,GAAGlB,eAAe,GAAGA,eAAe,GAAGkB,SAClD,CAAC;IACH,CAAC,EACD,MAAM;MACJL,YAAY,CAACd,cAAc,CAAC;MAC5BgB,aAAa,CAACf,eAAe,CAAC;IAChC,CACF,CAAC;EACH,CAAC,EAAE,CAACI,WAAW,CAAC,CAAC;EAEjB,oBACEX,IAAA,CAACJ,IAAI;IACHmB,KAAK,EAAE,CACLW,MAAM,CAACC,SAAS,EAChB;MACEV,UAAU;MACVW,aAAa,EAAExB;IACjB,CAAC,EACDW,KAAK,CACL;IAAAc,QAAA,eAEF3B,KAAA,CAACN,IAAI;MAACmB,KAAK,EAAEW,MAAM,CAACI,GAAI;MAAAD,QAAA,gBACtB7B,IAAA,CAACL,gBAAgB;QACfoC,OAAO,EAAErB,MAAO;QAChBsB,OAAO,EAAE,EAAG;QACZC,iBAAiB,EAAC,QAAQ;QAC1BC,kBAAkB,EAAC,MAAM;QACzBnB,KAAK,EAAEW,MAAM,CAACS,UAAW;QAAAN,QAAA,EAExBhB,cAAc,GACbA,cAAc,CAAC,CAAC,gBAEhBb,IAAA,CAACF,oBAAoB;UAACsC,KAAK,EAAExB;QAAc,CAAE;MAC9C,CACe,CAAC,EAElBD,WAAW,gBACVX,IAAA,CAACP,KAAK;QACJ4C,MAAM,EAAE;UAAEC,GAAG,EAAE3B;QAAY,CAAE;QAC7B4B,UAAU,EAAC,SAAS;QACpBxB,KAAK,EAAE,CACLW,MAAM,CAACc,IAAI,EACX;UACEC,KAAK,EAAEtB,SAAS;UAChBuB,MAAM,EAAErB;QACV,CAAC;MACD,CACH,CAAC,gBAEFrB,IAAA,CAACJ,IAAI;QAACmB,KAAK,EAAEW,MAAM,CAACiB;MAAgB,CAAE,CACvC;IAAA,CACG;EAAC,CACH,CAAC;AAEX,CAAC;AAED,MAAMjB,MAAM,GAAGhC,UAAU,CAACkD,MAAM,CAAC;EAC/BjB,SAAS,EAAE;IACTkB,eAAe,EAAE,MAAM;IACvBC,iBAAiB,EAAE3C;EACrB,CAAC;EACD2B,GAAG,EAAE;IACHiB,aAAa,EAAE,KAAK;IACpBC,UAAU,EAAE,QAAQ;IACpBC,cAAc,EAAE,eAAe;IAC/BC,SAAS,EAAE7C;EACb,CAAC;EACD8B,UAAU,EAAE;IACVgB,eAAe,EAAE,CAAC;IAClBF,cAAc,EAAE;EAClB,CAAC;EACDT,IAAI,EAAE;IACJU,SAAS,EAAE1C,eAAe;IAC1B4C,SAAS,EAAE;EACb,CAAC;EACDT,eAAe,EAAE;IACfF,KAAK,EAAEnC,cAAc;IACrB4C,SAAS,EAAE1C;EACb;AACF,CAAC,CAAC;AAEF,eAAeC,kBAAkB","ignoreList":[]}
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
 
3
3
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
- import { Image, StyleSheet, TouchableOpacity, View } from 'react-native';
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';
@@ -45,6 +46,8 @@ export const DualWebViewBridgeController = ({
45
46
  saversAppUrl,
46
47
  travelPortalUrl = DEFAULT_TRAVEL_PORTAL_URL,
47
48
  partnerLogo,
49
+ travelBackIconColor,
50
+ travelHeaderTopInset = 'none',
48
51
  renderTravelBackIcon,
49
52
  initialSurface = 'savers',
50
53
  onSaversSdkMessage,
@@ -53,6 +56,7 @@ export const DualWebViewBridgeController = ({
53
56
  }) => {
54
57
  const saversRef = useRef(null);
55
58
  const travelRef = useRef(null);
59
+ const pendingNavigateHubAfterSaversReloadRef = useRef(false);
56
60
  const [surface, setSurface] = useState(initialSurface);
57
61
  const [saversKey, setSaversKey] = useState(0);
58
62
  const [travelKey, setTravelKey] = useState(0);
@@ -68,9 +72,26 @@ export const DualWebViewBridgeController = ({
68
72
  setTravelKey(k => k + 1);
69
73
  }, []);
70
74
  const backToSavers = useCallback(() => {
75
+ pendingNavigateHubAfterSaversReloadRef.current = true;
71
76
  setSurface('savers');
72
77
  setSaversKey(k => k + 1);
73
78
  }, []);
79
+ const onLoadEndSaversWithSync = useCallback(() => {
80
+ // If we just returned from Travel, we intentionally reload Savers and then force it to show Hub.
81
+ if (pendingNavigateHubAfterSaversReloadRef.current) {
82
+ pendingNavigateHubAfterSaversReloadRef.current = false;
83
+ saversRef.current?.injectJavaScript(buildInjectNativeBridgeScript({
84
+ bridge: 'dual-webview',
85
+ from: 'native',
86
+ action: 'relay',
87
+ payload: {
88
+ type: 'NAVIGATE',
89
+ route: 'Hub'
90
+ }
91
+ }));
92
+ }
93
+ onLoadEndSavers?.();
94
+ }, [onLoadEndSavers]);
74
95
  const relayTo = useCallback((target, payload, from) => {
75
96
  const detail = {
76
97
  bridge: 'dual-webview',
@@ -177,40 +198,19 @@ export const DualWebViewBridgeController = ({
177
198
  applicationNameForUserAgent: WEBVIEW_UA_MARKER,
178
199
  onMessage: onMessageSavers,
179
200
  onShouldStartLoadWithRequest: onShouldStartLoadSavers,
180
- onLoadEnd: onLoadEndSavers,
201
+ onLoadEnd: onLoadEndSaversWithSync,
181
202
  style: styles.webview,
182
203
  sharedCookiesEnabled: true,
183
204
  incognito: false,
184
205
  webviewDebuggingEnabled: __DEV__
185
206
  }, saversKey) : null, surface === 'travel' ? /*#__PURE__*/_jsxs(View, {
186
207
  style: styles.travelColumn,
187
- children: [/*#__PURE__*/_jsxs(View, {
188
- style: styles.header,
189
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
190
- onPress: backToSavers,
191
- hitSlop: 16,
192
- accessibilityRole: "button",
193
- accessibilityLabel: "Back",
194
- style: styles.backButton,
195
- children: renderTravelBackIcon ? renderTravelBackIcon() : /*#__PURE__*/_jsxs(View, {
196
- style: styles.backIconWrapper,
197
- children: [/*#__PURE__*/_jsx(View, {
198
- style: styles.backIconShaft
199
- }), /*#__PURE__*/_jsx(View, {
200
- style: styles.backIconHead
201
- })]
202
- })
203
- }), /*#__PURE__*/_jsx(View, {
204
- style: styles.headerSpacer
205
- }), partnerLogo ? /*#__PURE__*/_jsx(Image, {
206
- source: {
207
- uri: partnerLogo
208
- },
209
- resizeMode: "contain",
210
- style: styles.logo
211
- }) : /*#__PURE__*/_jsx(View, {
212
- style: styles.logoPlaceholder
213
- })]
208
+ children: [/*#__PURE__*/_jsx(TravelPortalHeader, {
209
+ onBack: backToSavers,
210
+ partnerLogo: partnerLogo,
211
+ backIconColor: travelBackIconColor,
212
+ topInset: travelHeaderTopInset,
213
+ renderBackIcon: renderTravelBackIcon
214
214
  }), /*#__PURE__*/_jsx(WebView, {
215
215
  ref: travelRef,
216
216
  source: travelSource,
@@ -235,58 +235,6 @@ const styles = StyleSheet.create({
235
235
  },
236
236
  webview: {
237
237
  flex: 1
238
- },
239
- header: {
240
- flexDirection: 'row',
241
- alignItems: 'center',
242
- backgroundColor: '#fff',
243
- paddingHorizontal: 16,
244
- paddingTop: 12,
245
- paddingBottom: 12,
246
- borderBottomWidth: StyleSheet.hairlineWidth,
247
- borderBottomColor: '#E5E5E5'
248
- },
249
- backButton: {
250
- width: 32,
251
- height: 32,
252
- justifyContent: 'center',
253
- alignItems: 'center'
254
- },
255
- backIconWrapper: {
256
- width: 20,
257
- height: 16,
258
- justifyContent: 'center',
259
- position: 'relative'
260
- },
261
- backIconShaft: {
262
- position: 'absolute',
263
- left: 6,
264
- right: 0,
265
- height: 2,
266
- backgroundColor: '#111827',
267
- borderRadius: 1
268
- },
269
- backIconHead: {
270
- width: 10,
271
- height: 10,
272
- borderLeftWidth: 2,
273
- borderBottomWidth: 2,
274
- borderColor: '#111827',
275
- transform: [{
276
- rotate: '45deg'
277
- }],
278
- marginLeft: 1
279
- },
280
- headerSpacer: {
281
- flex: 1
282
- },
283
- logo: {
284
- width: 120,
285
- height: 32
286
- },
287
- logoPlaceholder: {
288
- width: 120,
289
- height: 32
290
238
  }
291
239
  });
292
240
  export default DualWebViewBridgeController;
@@ -1 +1 @@
1
- {"version":3,"names":["React","useCallback","useEffect","useMemo","useRef","useState","Image","StyleSheet","TouchableOpacity","View","WebView","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","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","header","onPress","hitSlop","accessibilityRole","accessibilityLabel","backButton","backIconWrapper","backIconShaft","backIconHead","headerSpacer","resizeMode","logo","logoPlaceholder","create","flex","flexDirection","alignItems","backgroundColor","paddingHorizontal","paddingTop","paddingBottom","borderBottomWidth","hairlineWidth","borderBottomColor","width","height","justifyContent","position","left","right","borderRadius","borderLeftWidth","borderColor","transform","rotate","marginLeft"],"sourceRoot":"../../../../src","sources":["services/webview/DualWebViewBridgeController.tsx"],"mappings":";;AAAA,OAAOA,KAAK,IAAIC,WAAW,EAAEC,SAAS,EAAEC,OAAO,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,OAAO;AAChF,SAASC,KAAK,EAAEC,UAAU,EAAEC,gBAAgB,EAAEC,IAAI,QAAQ,cAAc;AACxE,OAAOC,OAAO,MAAM,sBAAsB;AAC1C,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,CAACC,OAAgB,EAAsB;EAC/E,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,CAACC,IAA6B,EAAsB;EAClG,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;AAeA,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,oBAAoB;EACpBC,cAAc,GAAG,QAAQ;EACzBC,kBAAkB;EAClBC,eAAe;EACfC;AACF,CAAC,KAAK;EACJ,MAAMC,SAAS,GAAGrC,MAAM,CAAU,IAAI,CAAC;EACvC,MAAMsC,SAAS,GAAGtC,MAAM,CAAU,IAAI,CAAC;EAEvC,MAAM,CAACuC,OAAO,EAAEC,UAAU,CAAC,GAAGvC,QAAQ,CAAUgC,cAAc,CAAC;EAC/D,MAAM,CAACQ,SAAS,EAAEC,YAAY,CAAC,GAAGzC,QAAQ,CAAC,CAAC,CAAC;EAC7C,MAAM,CAAC0C,SAAS,EAAEC,YAAY,CAAC,GAAG3C,QAAQ,CAAC,CAAC,CAAC;EAC7C,MAAM,CAAC4C,qBAAqB,EAAEC,wBAAwB,CAAC,GACrD7C,QAAQ,CAAC6B,eAAe,CAAC;EAE3BhC,SAAS,CAAC,MAAM;IACdgD,wBAAwB,CAAChB,eAAe,CAAC;EAC3C,CAAC,EAAE,CAACA,eAAe,CAAC,CAAC;EAErB,MAAMiB,UAAU,GAAGlD,WAAW,CAAEsB,GAAY,IAAK;IAC/C,IAAIA,GAAG,EAAE;MACP2B,wBAAwB,CAAC3B,GAAG,CAAC;IAC/B;IACAqB,UAAU,CAAC,QAAQ,CAAC;IACpBI,YAAY,CAAEI,CAAC,IAAKA,CAAC,GAAG,CAAC,CAAC;EAC5B,CAAC,EAAE,EAAE,CAAC;EAEN,MAAMC,YAAY,GAAGpD,WAAW,CAAC,MAAM;IACrC2C,UAAU,CAAC,QAAQ,CAAC;IACpBE,YAAY,CAAEM,CAAC,IAAKA,CAAC,GAAG,CAAC,CAAC;EAC5B,CAAC,EAAE,EAAE,CAAC;EAEN,MAAME,OAAO,GAAGrD,WAAW,CACzB,CACEsD,MAA2B,EAC3BlC,OAAgB,EAChBmC,IAAyB,KACtB;IACH,MAAM3B,MAAM,GAAG;MACb4B,MAAM,EAAE,cAAc;MACtBD,IAAI;MACJE,MAAM,EAAE,OAAgB;MACxBrC;IACF,CAAC;IACD,MAAMsC,GAAG,GAAGJ,MAAM,KAAK,QAAQ,GAAGd,SAAS,GAAGC,SAAS;IACvDiB,GAAG,CAACC,OAAO,EAAEC,gBAAgB,CAACjC,6BAA6B,CAACC,MAAM,CAAC,CAAC;EACtE,CAAC,EACD,EACF,CAAC;EAED,MAAMiC,oBAAoB,GAAG7D,WAAW,CACrC8D,GAA8B,IAAc;IAC3C,QAAQA,GAAG,CAACL,MAAM;MAChB,KAAK,aAAa;QAChBP,UAAU,CAAC/B,iCAAiC,CAAC2C,GAAG,CAAC1C,OAAO,CAAC,CAAC;QAC1D,OAAO,IAAI;MACb,KAAK,cAAc;QACjBgC,YAAY,CAAC,CAAC;QACd,OAAO,IAAI;MACb,KAAK,OAAO;QACV,IAAIU,GAAG,CAACC,EAAE,KAAK,QAAQ,EAAE;UACvBV,OAAO,CAAC,QAAQ,EAAES,GAAG,CAAC1C,OAAO,EAAE0C,GAAG,CAACP,IAAI,CAAC;UACxC,OAAO,IAAI;QACb;QACA,IAAIO,GAAG,CAACC,EAAE,KAAK,QAAQ,EAAE;UACvBV,OAAO,CAAC,QAAQ,EAAES,GAAG,CAAC1C,OAAO,EAAE0C,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,GAAGhE,WAAW,CACpCiE,GAAW,IAAc;IACxB,IAAI,CAACA,GAAG,IAAI,OAAOA,GAAG,KAAK,QAAQ,EAAE;MACnC,OAAO,KAAK;IACd;IACA,IAAI;MACF,MAAMxC,IAAI,GAAGI,IAAI,CAACqC,KAAK,CAACD,GAAG,CAAC;MAC5B,IACExC,IAAI,EAAEgC,MAAM,KAAK,oBAAoB,IACrChC,IAAI,EAAE0C,IAAI,KAAK,aAAa,IAC5B1C,IAAI,EAAEyB,UAAU,KAAK,IAAI,EACzB;QACAA,UAAU,CAAC1B,uCAAuC,CAACC,IAAI,CAAC,CAAC;QACzD,OAAO,IAAI;MACb;IACF,CAAC,CAAC,MAAM;MACN;IAAA;IAEF,IAAIP,0BAA0B,CAACkD,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,GAAGvE,WAAW,CAChCwE,CAAoC,IAAK;IACxC,MAAMP,GAAG,GAAGO,CAAC,EAAEC,WAAW,EAAEhD,IAAI,IAAI,EAAE;IACtC,MAAMqC,GAAG,GAAGpD,wBAAwB,CAACuD,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,GAAIjD,IAAa,IAAK;MAClC,MAAMG,MAAM,GAAG;QACb4B,MAAM,EAAE,cAAc;QACtBD,IAAI,EAAE,QAAQ;QACdE,MAAM,EAAE,OAAO;QACfrC,OAAO,EAAEK;MACX,CAAC;MACDe,SAAS,CAACmB,OAAO,EAAEC,gBAAgB,CACjCjC,6BAA6B,CAACC,MAAM,CACtC,CAAC;IACH,CAAC;IACDS,kBAAkB,GAAG4B,GAAG,EAAES,QAAQ,CAAC;EACrC,CAAC,EACD,CAACb,oBAAoB,EAAExB,kBAAkB,EAAE2B,mBAAmB,CAChE,CAAC;EAED,MAAMW,eAAe,GAAG3E,WAAW,CAChCwE,CAAoC,IAAK;IACxC,MAAMP,GAAG,GAAGO,CAAC,EAAEC,WAAW,EAAEhD,IAAI,IAAI,EAAE;IACtC,MAAMqC,GAAG,GAAGpD,wBAAwB,CAACuD,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,GAAG5E,WAAW,CACxC6E,OAAwB,IAAK;IAC5B,MAAM;MAAEvD;IAAI,CAAC,GAAGuD,OAAO;IACvB,IAAIvD,GAAG,CAACwD,UAAU,CAAC7D,gBAAgB,CAAC,IAAIK,GAAG,KAAK,qBAAqB,EAAE;MACrE4B,UAAU,CAAC,CAAC;MACZ,OAAO,KAAK;IACd;IACA,OAAO,IAAI;EACb,CAAC,EACD,CAACA,UAAU,CACb,CAAC;EAED,MAAM6B,YAAY,GAAG7E,OAAO,CAC1B,OAAO;IAAE8E,GAAG,EAAEhC;EAAsB,CAAC,CAAC,EACtC,CAACA,qBAAqB,CACxB,CAAC;EACD,MAAMiC,YAAY,GAAG/E,OAAO,CAAC,OAAO;IAAE8E,GAAG,EAAEhD;EAAa,CAAC,CAAC,EAAE,CAACA,YAAY,CAAC,CAAC;EAE3E,oBACElB,KAAA,CAACN,IAAI;IAAC0E,KAAK,EAAEC,MAAM,CAACC,IAAK;IAAAC,QAAA,GACtB3C,OAAO,KAAK,QAAQ,gBACnB9B,IAAA,CAACH,OAAO;MAENiD,GAAG,EAAElB,SAAU;MACf8C,MAAM,EAAEL,YAAa;MACrBM,eAAe,EAAE,CAAC,GAAG,CAAE;MACvBC,2BAA2B,EAAExE,iBAAkB;MAC/CyE,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,gBACnB5B,KAAA,CAACN,IAAI;MAAC0E,KAAK,EAAEC,MAAM,CAACc,YAAa;MAAAZ,QAAA,gBAC/BvE,KAAA,CAACN,IAAI;QAAC0E,KAAK,EAAEC,MAAM,CAACe,MAAO;QAAAb,QAAA,gBACzBzE,IAAA,CAACL,gBAAgB;UACf4F,OAAO,EAAE/C,YAAa;UACtBgD,OAAO,EAAE,EAAG;UACZC,iBAAiB,EAAC,QAAQ;UAC1BC,kBAAkB,EAAC,MAAM;UACzBpB,KAAK,EAAEC,MAAM,CAACoB,UAAW;UAAAlB,QAAA,EAExBlD,oBAAoB,GACnBA,oBAAoB,CAAC,CAAC,gBAEtBrB,KAAA,CAACN,IAAI;YAAC0E,KAAK,EAAEC,MAAM,CAACqB,eAAgB;YAAAnB,QAAA,gBAClCzE,IAAA,CAACJ,IAAI;cAAC0E,KAAK,EAAEC,MAAM,CAACsB;YAAc,CAAE,CAAC,eACrC7F,IAAA,CAACJ,IAAI;cAAC0E,KAAK,EAAEC,MAAM,CAACuB;YAAa,CAAE,CAAC;UAAA,CAChC;QACP,CACe,CAAC,eAEnB9F,IAAA,CAACJ,IAAI;UAAC0E,KAAK,EAAEC,MAAM,CAACwB;QAAa,CAAE,CAAC,EAEnCzE,WAAW,gBACVtB,IAAA,CAACP,KAAK;UACJiF,MAAM,EAAE;YAAEN,GAAG,EAAE9C;UAAY,CAAE;UAC7B0E,UAAU,EAAC,SAAS;UACpB1B,KAAK,EAAEC,MAAM,CAAC0B;QAAK,CACpB,CAAC,gBAEFjG,IAAA,CAACJ,IAAI;UAAC0E,KAAK,EAAEC,MAAM,CAAC2B;QAAgB,CAAE,CACvC;MAAA,CACG,CAAC,eACPlG,IAAA,CAACH,OAAO;QAENiD,GAAG,EAAEjB,SAAU;QACf6C,MAAM,EAAEP,YAAa;QACrBQ,eAAe,EAAE,CAAC,GAAG,CAAE;QACvBC,2BAA2B,EAAExE,iBAAkB;QAC/CyE,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,GAAG7E,UAAU,CAACyG,MAAM,CAAC;EAC/B3B,IAAI,EAAE;IAAE4B,IAAI,EAAE;EAAE,CAAC;EACjBf,YAAY,EAAE;IAAEe,IAAI,EAAE;EAAE,CAAC;EACzBpB,OAAO,EAAE;IAAEoB,IAAI,EAAE;EAAE,CAAC;EACpBd,MAAM,EAAE;IACNe,aAAa,EAAE,KAAK;IACpBC,UAAU,EAAE,QAAQ;IACpBC,eAAe,EAAE,MAAM;IACvBC,iBAAiB,EAAE,EAAE;IACrBC,UAAU,EAAE,EAAE;IACdC,aAAa,EAAE,EAAE;IACjBC,iBAAiB,EAAEjH,UAAU,CAACkH,aAAa;IAC3CC,iBAAiB,EAAE;EACrB,CAAC;EACDlB,UAAU,EAAE;IACVmB,KAAK,EAAE,EAAE;IACTC,MAAM,EAAE,EAAE;IACVC,cAAc,EAAE,QAAQ;IACxBV,UAAU,EAAE;EACd,CAAC;EACDV,eAAe,EAAE;IACfkB,KAAK,EAAE,EAAE;IACTC,MAAM,EAAE,EAAE;IACVC,cAAc,EAAE,QAAQ;IACxBC,QAAQ,EAAE;EACZ,CAAC;EACDpB,aAAa,EAAE;IACboB,QAAQ,EAAE,UAAU;IACpBC,IAAI,EAAE,CAAC;IACPC,KAAK,EAAE,CAAC;IACRJ,MAAM,EAAE,CAAC;IACTR,eAAe,EAAE,SAAS;IAC1Ba,YAAY,EAAE;EAChB,CAAC;EACDtB,YAAY,EAAE;IACZgB,KAAK,EAAE,EAAE;IACTC,MAAM,EAAE,EAAE;IACVM,eAAe,EAAE,CAAC;IAClBV,iBAAiB,EAAE,CAAC;IACpBW,WAAW,EAAE,SAAS;IACtBC,SAAS,EAAE,CAAC;MAAEC,MAAM,EAAE;IAAQ,CAAC,CAAC;IAChCC,UAAU,EAAE;EACd,CAAC;EACD1B,YAAY,EAAE;IACZK,IAAI,EAAE;EACR,CAAC;EACDH,IAAI,EAAE;IACJa,KAAK,EAAE,GAAG;IACVC,MAAM,EAAE;EACV,CAAC;EACDb,eAAe,EAAE;IACfY,KAAK,EAAE,GAAG;IACVC,MAAM,EAAE;EACV;AACF,CAAC,CAAC;AAEF,eAAe5F,2BAA2B","ignoreList":[]}
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","pendingNavigateHubAfterSaversReloadRef","surface","setSurface","saversKey","setSaversKey","travelKey","setTravelKey","activeTravelPortalUrl","setActiveTravelPortalUrl","openTravel","k","backToSavers","current","onLoadEndSaversWithSync","injectJavaScript","bridge","from","action","type","route","relayTo","target","ref","handleBridgeEnvelope","env","to","tryLegacyOpenTravel","raw","parse","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":"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","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,KAA4D,MAAM,OAAO,CAAC;AAsCjF,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,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,CA6OjC,CAAC;AA0DF,eAAe,2BAA2B,CAAC"}
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,CA0OjC,CAAC;AAQF,eAAe,2BAA2B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savers_app/react-native-sandbox-sdk",
3
- "version": "1.3.0",
3
+ "version": "1.5.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, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
- import { Image, StyleSheet, TouchableOpacity, View } from 'react-native';
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,7 +22,9 @@ 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
 
18
- function resolveTravelPortalUrlFromPayload(payload: unknown): string | undefined {
25
+ function resolveTravelPortalUrlFromPayload(
26
+ payload: unknown
27
+ ): string | undefined {
19
28
  if (typeof payload === 'string' && payload.trim()) {
20
29
  return payload.trim();
21
30
  }
@@ -28,7 +37,9 @@ function resolveTravelPortalUrlFromPayload(payload: unknown): string | undefined
28
37
  return undefined;
29
38
  }
30
39
 
31
- function resolveTravelPortalUrlFromLegacyMessage(data: Record<string, unknown>): string | undefined {
40
+ function resolveTravelPortalUrlFromLegacyMessage(
41
+ data: Record<string, unknown>
42
+ ): string | undefined {
32
43
  const topLevel = data?.url;
33
44
  if (typeof topLevel === 'string' && topLevel.trim()) {
34
45
  return topLevel.trim();
@@ -42,6 +53,10 @@ export type DualWebViewBridgeControllerProps = {
42
53
  saversAppUrl: string;
43
54
  travelPortalUrl?: string;
44
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';
45
60
  renderTravelBackIcon?: () => React.ReactNode;
46
61
  initialSurface?: Surface;
47
62
  onSaversSdkMessage?: (raw: string, postBack: (data: unknown) => void) => void;
@@ -67,6 +82,8 @@ export const DualWebViewBridgeController: React.FC<
67
82
  saversAppUrl,
68
83
  travelPortalUrl = DEFAULT_TRAVEL_PORTAL_URL,
69
84
  partnerLogo,
85
+ travelBackIconColor,
86
+ travelHeaderTopInset = 'none',
70
87
  renderTravelBackIcon,
71
88
  initialSurface = 'savers',
72
89
  onSaversSdkMessage,
@@ -75,6 +92,7 @@ export const DualWebViewBridgeController: React.FC<
75
92
  }) => {
76
93
  const saversRef = useRef<WebView>(null);
77
94
  const travelRef = useRef<WebView>(null);
95
+ const pendingNavigateHubAfterSaversReloadRef = useRef(false);
78
96
 
79
97
  const [surface, setSurface] = useState<Surface>(initialSurface);
80
98
  const [saversKey, setSaversKey] = useState(0);
@@ -95,10 +113,27 @@ export const DualWebViewBridgeController: React.FC<
95
113
  }, []);
96
114
 
97
115
  const backToSavers = useCallback(() => {
116
+ pendingNavigateHubAfterSaversReloadRef.current = true;
98
117
  setSurface('savers');
99
118
  setSaversKey((k) => k + 1);
100
119
  }, []);
101
120
 
121
+ const onLoadEndSaversWithSync = useCallback(() => {
122
+ // If we just returned from Travel, we intentionally reload Savers and then force it to show Hub.
123
+ if (pendingNavigateHubAfterSaversReloadRef.current) {
124
+ pendingNavigateHubAfterSaversReloadRef.current = false;
125
+ saversRef.current?.injectJavaScript(
126
+ buildInjectNativeBridgeScript({
127
+ bridge: 'dual-webview',
128
+ from: 'native',
129
+ action: 'relay',
130
+ payload: { type: 'NAVIGATE', route: 'Hub' },
131
+ })
132
+ );
133
+ }
134
+ onLoadEndSavers?.();
135
+ }, [onLoadEndSavers]);
136
+
102
137
  const relayTo = useCallback(
103
138
  (
104
139
  target: 'savers' | 'travel',
@@ -242,7 +277,7 @@ export const DualWebViewBridgeController: React.FC<
242
277
  applicationNameForUserAgent={WEBVIEW_UA_MARKER}
243
278
  onMessage={onMessageSavers}
244
279
  onShouldStartLoadWithRequest={onShouldStartLoadSavers}
245
- onLoadEnd={onLoadEndSavers}
280
+ onLoadEnd={onLoadEndSaversWithSync}
246
281
  style={styles.webview}
247
282
  sharedCookiesEnabled
248
283
  incognito={false}
@@ -252,36 +287,13 @@ export const DualWebViewBridgeController: React.FC<
252
287
 
253
288
  {surface === 'travel' ? (
254
289
  <View style={styles.travelColumn}>
255
- <View style={styles.header}>
256
- <TouchableOpacity
257
- onPress={backToSavers}
258
- hitSlop={16}
259
- accessibilityRole="button"
260
- accessibilityLabel="Back"
261
- style={styles.backButton}
262
- >
263
- {renderTravelBackIcon ? (
264
- renderTravelBackIcon()
265
- ) : (
266
- <View style={styles.backIconWrapper}>
267
- <View style={styles.backIconShaft} />
268
- <View style={styles.backIconHead} />
269
- </View>
270
- )}
271
- </TouchableOpacity>
272
-
273
- <View style={styles.headerSpacer} />
274
-
275
- {partnerLogo ? (
276
- <Image
277
- source={{ uri: partnerLogo }}
278
- resizeMode="contain"
279
- style={styles.logo}
280
- />
281
- ) : (
282
- <View style={styles.logoPlaceholder} />
283
- )}
284
- </View>
290
+ <TravelPortalHeader
291
+ onBack={backToSavers}
292
+ partnerLogo={partnerLogo}
293
+ backIconColor={travelBackIconColor}
294
+ topInset={travelHeaderTopInset}
295
+ renderBackIcon={renderTravelBackIcon}
296
+ />
285
297
  <WebView
286
298
  key={travelKey}
287
299
  ref={travelRef}
@@ -305,56 +317,6 @@ const styles = StyleSheet.create({
305
317
  root: { flex: 1 },
306
318
  travelColumn: { flex: 1 },
307
319
  webview: { flex: 1 },
308
- header: {
309
- flexDirection: 'row',
310
- alignItems: 'center',
311
- backgroundColor: '#fff',
312
- paddingHorizontal: 16,
313
- paddingTop: 12,
314
- paddingBottom: 12,
315
- borderBottomWidth: StyleSheet.hairlineWidth,
316
- borderBottomColor: '#E5E5E5',
317
- },
318
- backButton: {
319
- width: 32,
320
- height: 32,
321
- justifyContent: 'center',
322
- alignItems: 'center',
323
- },
324
- backIconWrapper: {
325
- width: 20,
326
- height: 16,
327
- justifyContent: 'center',
328
- position: 'relative',
329
- },
330
- backIconShaft: {
331
- position: 'absolute',
332
- left: 6,
333
- right: 0,
334
- height: 2,
335
- backgroundColor: '#111827',
336
- borderRadius: 1,
337
- },
338
- backIconHead: {
339
- width: 10,
340
- height: 10,
341
- borderLeftWidth: 2,
342
- borderBottomWidth: 2,
343
- borderColor: '#111827',
344
- transform: [{ rotate: '45deg' }],
345
- marginLeft: 1,
346
- },
347
- headerSpacer: {
348
- flex: 1,
349
- },
350
- logo: {
351
- width: 120,
352
- height: 32,
353
- },
354
- logoPlaceholder: {
355
- width: 120,
356
- height: 32,
357
- },
358
320
  });
359
321
 
360
322
  export default DualWebViewBridgeController;