@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.
Files changed (23) hide show
  1. package/lib/module/@types/react-native-svg.d.js +2 -0
  2. package/lib/module/@types/react-native-svg.d.js.map +1 -0
  3. package/lib/module/components/TravelPortalBackIcon.js +24 -0
  4. package/lib/module/components/TravelPortalBackIcon.js.map +1 -0
  5. package/lib/module/components/TravelPortalHeader.js +97 -0
  6. package/lib/module/components/TravelPortalHeader.js.map +1 -0
  7. package/lib/module/services/webview/DualWebViewBridgeController.js +42 -86
  8. package/lib/module/services/webview/DualWebViewBridgeController.js.map +1 -1
  9. package/lib/module/services/webview/dualWebViewBridge.types.js.map +1 -1
  10. package/lib/typescript/src/components/TravelPortalBackIcon.d.ts +10 -0
  11. package/lib/typescript/src/components/TravelPortalBackIcon.d.ts.map +1 -0
  12. package/lib/typescript/src/components/TravelPortalHeader.d.ts +19 -0
  13. package/lib/typescript/src/components/TravelPortalHeader.d.ts.map +1 -0
  14. package/lib/typescript/src/services/webview/DualWebViewBridgeController.d.ts +4 -0
  15. package/lib/typescript/src/services/webview/DualWebViewBridgeController.d.ts.map +1 -1
  16. package/lib/typescript/src/services/webview/dualWebViewBridge.types.d.ts +2 -0
  17. package/lib/typescript/src/services/webview/dualWebViewBridge.types.d.ts.map +1 -1
  18. package/package.json +3 -1
  19. package/src/@types/react-native-svg.d.ts +19 -0
  20. package/src/components/TravelPortalBackIcon.tsx +26 -0
  21. package/src/components/TravelPortalHeader.tsx +143 -0
  22. package/src/services/webview/DualWebViewBridgeController.tsx +61 -87
  23. package/src/services/webview/dualWebViewBridge.types.ts +2 -0
@@ -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":"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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 { Image, StyleSheet, TouchableOpacity, View } from 'react-native';
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 openTravel = useCallback(() => {
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: travelPortalUrl
141
- }), [travelPortalUrl]);
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__*/_jsxs(View, {
162
- style: styles.header,
163
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
164
- onPress: backToSavers,
165
- hitSlop: 16,
166
- accessibilityRole: "button",
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","Image","StyleSheet","TouchableOpacity","View","WebView","parseDualWebViewEnvelope","jsx","_jsx","jsxs","_jsxs","DEFAULT_TRAVEL_PORTAL_URL","WEBVIEW_UA_MARKER","TRAVEL_DEEP_LINK","LEGACY_OPEN_TRAVEL_ACTIONS","buildInjectNativeBridgeScript","detail","JSON","stringify","DualWebViewBridgeController","saversAppUrl","travelPortalUrl","partnerLogo","renderTravelBackIcon","initialSurface","onSaversSdkMessage","onLoadEndSavers","onLoadEndTravel","saversRef","travelRef","surface","setSurface","saversKey","setSaversKey","travelKey","setTravelKey","openTravel","k","backToSavers","relayTo","target","payload","from","bridge","action","ref","current","injectJavaScript","handleBridgeEnvelope","env","to","tryLegacyOpenTravel","raw","data","parse","type","some","s","includes","onMessageSavers","e","nativeEvent","postBack","onMessageTravel","onShouldStartLoadSavers","request","url","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":"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","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","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":"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","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;;AAgBA,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":[]}
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,KAAiD,MAAM,OAAO,CAAC;AAiBtE,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,CAoOjC,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,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;;;;OAIG;IACH,MAAM,EAAE,OAAO,GAAG,aAAa,GAAG,cAAc,CAAC;IACjD,yDAAyD;IACzD,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,GACV,yBAAyB,GAAG,IAAI,CAUlC"}
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.2.8",
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, { useCallback, 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,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
- const openTravel = useCallback(() => {
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: travelPortalUrl }),
200
- [travelPortalUrl]
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
- <View style={styles.header}>
226
- <TouchableOpacity
227
- onPress={backToSavers}
228
- hitSlop={16}
229
- accessibilityRole="button"
230
- accessibilityLabel="Back"
231
- style={styles.backButton}
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