@mobana/react-native-sdk 0.2.10
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/LICENSE +21 -0
- package/README.md +249 -0
- package/android/build.gradle +50 -0
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/ai/mobana/sdk/MobanaModule.kt +67 -0
- package/android/src/main/java/ai/mobana/sdk/MobanaPackage.kt +19 -0
- package/app.plugin.js +274 -0
- package/ios/Mobana.h +11 -0
- package/ios/Mobana.m +20 -0
- package/lib/commonjs/Mobana.js +676 -0
- package/lib/commonjs/Mobana.js.map +1 -0
- package/lib/commonjs/NativeMobana.js +53 -0
- package/lib/commonjs/NativeMobana.js.map +1 -0
- package/lib/commonjs/api.js +201 -0
- package/lib/commonjs/api.js.map +1 -0
- package/lib/commonjs/bridge/index.js +19 -0
- package/lib/commonjs/bridge/index.js.map +1 -0
- package/lib/commonjs/bridge/injectBridge.js +528 -0
- package/lib/commonjs/bridge/injectBridge.js.map +1 -0
- package/lib/commonjs/components/FlowWebView.js +676 -0
- package/lib/commonjs/components/FlowWebView.js.map +1 -0
- package/lib/commonjs/components/MobanaProvider.js +275 -0
- package/lib/commonjs/components/MobanaProvider.js.map +1 -0
- package/lib/commonjs/components/index.js +20 -0
- package/lib/commonjs/components/index.js.map +1 -0
- package/lib/commonjs/device.js +49 -0
- package/lib/commonjs/device.js.map +1 -0
- package/lib/commonjs/index.js +20 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/storage.js +277 -0
- package/lib/commonjs/storage.js.map +1 -0
- package/lib/commonjs/types.js +2 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/module/Mobana.js +673 -0
- package/lib/module/Mobana.js.map +1 -0
- package/lib/module/NativeMobana.js +49 -0
- package/lib/module/NativeMobana.js.map +1 -0
- package/lib/module/api.js +194 -0
- package/lib/module/api.js.map +1 -0
- package/lib/module/bridge/index.js +4 -0
- package/lib/module/bridge/index.js.map +1 -0
- package/lib/module/bridge/injectBridge.js +523 -0
- package/lib/module/bridge/injectBridge.js.map +1 -0
- package/lib/module/components/FlowWebView.js +672 -0
- package/lib/module/components/FlowWebView.js.map +1 -0
- package/lib/module/components/MobanaProvider.js +270 -0
- package/lib/module/components/MobanaProvider.js.map +1 -0
- package/lib/module/components/index.js +5 -0
- package/lib/module/components/index.js.map +1 -0
- package/lib/module/device.js +45 -0
- package/lib/module/device.js.map +1 -0
- package/lib/module/index.js +53 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/storage.js +257 -0
- package/lib/module/storage.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/Mobana.d.ts +209 -0
- package/lib/typescript/Mobana.d.ts.map +1 -0
- package/lib/typescript/NativeMobana.d.ts +11 -0
- package/lib/typescript/NativeMobana.d.ts.map +1 -0
- package/lib/typescript/api.d.ts +34 -0
- package/lib/typescript/api.d.ts.map +1 -0
- package/lib/typescript/bridge/index.d.ts +3 -0
- package/lib/typescript/bridge/index.d.ts.map +1 -0
- package/lib/typescript/bridge/injectBridge.d.ts +23 -0
- package/lib/typescript/bridge/injectBridge.d.ts.map +1 -0
- package/lib/typescript/components/FlowWebView.d.ts +38 -0
- package/lib/typescript/components/FlowWebView.d.ts.map +1 -0
- package/lib/typescript/components/MobanaProvider.d.ts +65 -0
- package/lib/typescript/components/MobanaProvider.d.ts.map +1 -0
- package/lib/typescript/components/index.d.ts +5 -0
- package/lib/typescript/components/index.d.ts.map +1 -0
- package/lib/typescript/device.d.ts +6 -0
- package/lib/typescript/device.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +46 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/storage.d.ts +68 -0
- package/lib/typescript/storage.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +298 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/mobana.podspec +19 -0
- package/package.json +131 -0
- package/src/Mobana.ts +742 -0
- package/src/NativeMobana.ts +61 -0
- package/src/api.ts +259 -0
- package/src/bridge/index.ts +2 -0
- package/src/bridge/injectBridge.ts +542 -0
- package/src/components/FlowWebView.tsx +826 -0
- package/src/components/MobanaProvider.tsx +393 -0
- package/src/components/index.ts +4 -0
- package/src/device.ts +42 -0
- package/src/index.ts +66 -0
- package/src/storage.ts +262 -0
- package/src/types.ts +362 -0
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bridge context passed from native to WebView
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generate JavaScript code to inject into WebView
|
|
9
|
+
* Creates the window.Mobana bridge object
|
|
10
|
+
*/
|
|
11
|
+
export function generateBridgeScript(context) {
|
|
12
|
+
const contextJson = JSON.stringify(context);
|
|
13
|
+
|
|
14
|
+
// This JavaScript runs inside the WebView
|
|
15
|
+
return `
|
|
16
|
+
(function() {
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
// Bridge context from native
|
|
20
|
+
var __context = ${contextJson};
|
|
21
|
+
var __localData = __context.localData || {};
|
|
22
|
+
|
|
23
|
+
// Pending async requests (requestId -> { resolve, reject })
|
|
24
|
+
var __pendingRequests = {};
|
|
25
|
+
var __requestId = 0;
|
|
26
|
+
|
|
27
|
+
// Send message to native
|
|
28
|
+
function postMessage(type, payload, requestId) {
|
|
29
|
+
var message = {
|
|
30
|
+
type: type,
|
|
31
|
+
payload: payload,
|
|
32
|
+
requestId: requestId
|
|
33
|
+
};
|
|
34
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(message));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Make an async request to native and wait for response
|
|
38
|
+
function asyncRequest(type, payload) {
|
|
39
|
+
return new Promise(function(resolve, reject) {
|
|
40
|
+
var id = ++__requestId;
|
|
41
|
+
__pendingRequests[id] = { resolve: resolve, reject: reject };
|
|
42
|
+
postMessage(type, payload, id);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Handle response from native (called via injectJavaScript)
|
|
47
|
+
window.__mobanaBridgeResponse = function(requestId, success, result) {
|
|
48
|
+
var pending = __pendingRequests[requestId];
|
|
49
|
+
if (pending) {
|
|
50
|
+
delete __pendingRequests[requestId];
|
|
51
|
+
if (success) {
|
|
52
|
+
pending.resolve(result);
|
|
53
|
+
} else {
|
|
54
|
+
pending.reject(new Error(result || 'Request failed'));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Mobana bridge object
|
|
60
|
+
window.Mobana = {
|
|
61
|
+
// ============================================
|
|
62
|
+
// Data Access
|
|
63
|
+
// ============================================
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get attribution data for this install
|
|
67
|
+
* @returns {Object|null} Attribution object or null if not matched
|
|
68
|
+
*/
|
|
69
|
+
getAttribution: function() {
|
|
70
|
+
return __context.attribution;
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get custom parameters passed to startFlow()
|
|
75
|
+
* @returns {Object} Parameters object
|
|
76
|
+
*/
|
|
77
|
+
getParams: function() {
|
|
78
|
+
return __context.params || {};
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get the install ID
|
|
83
|
+
* @returns {string} Unique install identifier
|
|
84
|
+
*/
|
|
85
|
+
getInstallId: function() {
|
|
86
|
+
return __context.installId;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get the current platform
|
|
91
|
+
* @returns {string} 'ios' or 'android'
|
|
92
|
+
*/
|
|
93
|
+
getPlatform: function() {
|
|
94
|
+
return __context.platform;
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get safe area insets for the device screen
|
|
99
|
+
* @returns {Object} { top, bottom, left, right, width, height }
|
|
100
|
+
*/
|
|
101
|
+
getSafeArea: function() {
|
|
102
|
+
return __context.safeArea;
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get the device color scheme (light/dark mode)
|
|
107
|
+
* @returns {string} 'light' or 'dark'
|
|
108
|
+
*/
|
|
109
|
+
getColorScheme: function() {
|
|
110
|
+
return __context.colorScheme;
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Store data locally on device (persists across app sessions)
|
|
115
|
+
* @param {string} key - Data key
|
|
116
|
+
* @param {*} value - Data value
|
|
117
|
+
*/
|
|
118
|
+
setLocalData: function(key, value) {
|
|
119
|
+
__localData[key] = value;
|
|
120
|
+
postMessage('setLocalData', { key: key, value: value });
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Retrieve locally stored data
|
|
125
|
+
* @param {string} key - Data key
|
|
126
|
+
* @returns {*} Data value or undefined
|
|
127
|
+
*/
|
|
128
|
+
getLocalData: function(key) {
|
|
129
|
+
return __localData[key];
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// ============================================
|
|
133
|
+
// Flow Control
|
|
134
|
+
// ============================================
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Complete the flow with optional data
|
|
138
|
+
* @param {Object} data - Optional data to return to the app
|
|
139
|
+
*/
|
|
140
|
+
complete: function(data) {
|
|
141
|
+
postMessage('complete', { data: data });
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Dismiss the flow
|
|
146
|
+
*/
|
|
147
|
+
dismiss: function() {
|
|
148
|
+
postMessage('dismiss', {});
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Track a custom event
|
|
153
|
+
* @param {string} name - Event name (snake_case, e.g., 'welcome_viewed')
|
|
154
|
+
*/
|
|
155
|
+
trackEvent: function(name) {
|
|
156
|
+
postMessage('trackEvent', { name: name });
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Request the app to perform an async action and return a result.
|
|
161
|
+
* The flow stays open while the app processes the request.
|
|
162
|
+
* Requires onCallback to be provided when starting the flow.
|
|
163
|
+
*
|
|
164
|
+
* @param {Object} data - Arbitrary data to send to the app's onCallback handler
|
|
165
|
+
* @param {Object} options - Optional configuration
|
|
166
|
+
* @param {number} options.timeout - Timeout in seconds (default: 300)
|
|
167
|
+
* @returns {Promise<Object>} Result returned by the app's onCallback handler
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* // Request a purchase
|
|
171
|
+
* try {
|
|
172
|
+
* var result = await Mobana.requestCallback(
|
|
173
|
+
* { action: 'purchase', planId: 'premium' },
|
|
174
|
+
* { timeout: 120 }
|
|
175
|
+
* );
|
|
176
|
+
* if (result.success) {
|
|
177
|
+
* Mobana.complete({ purchased: true });
|
|
178
|
+
* }
|
|
179
|
+
* } catch (error) {
|
|
180
|
+
* // Timeout, no handler, or handler threw an error
|
|
181
|
+
* }
|
|
182
|
+
*/
|
|
183
|
+
requestCallback: function(data, options) {
|
|
184
|
+
var opts = options || {};
|
|
185
|
+
var timeout = typeof opts.timeout === 'number' ? opts.timeout : 300;
|
|
186
|
+
|
|
187
|
+
var promise = asyncRequest('requestCallback', { data: data || {} });
|
|
188
|
+
|
|
189
|
+
// Wrap with timeout
|
|
190
|
+
var timeoutMs = timeout * 1000;
|
|
191
|
+
var timer;
|
|
192
|
+
var timeoutPromise = new Promise(function(_, reject) {
|
|
193
|
+
timer = setTimeout(function() {
|
|
194
|
+
reject(new Error('requestCallback timed out after ' + timeout + 's'));
|
|
195
|
+
}, timeoutMs);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return Promise.race([promise, timeoutPromise]).then(
|
|
199
|
+
function(result) { clearTimeout(timer); return result; },
|
|
200
|
+
function(error) { clearTimeout(timer); throw error; }
|
|
201
|
+
);
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
// ============================================
|
|
205
|
+
// Permissions
|
|
206
|
+
// ============================================
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Request notification permission
|
|
210
|
+
* @returns {Promise<boolean>} True if granted
|
|
211
|
+
*/
|
|
212
|
+
requestNotificationPermission: function() {
|
|
213
|
+
return asyncRequest('requestNotificationPermission', {});
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Check notification permission status without requesting
|
|
218
|
+
* @returns {Promise<Object>} { status: string, granted: boolean, settings?: Object }
|
|
219
|
+
*/
|
|
220
|
+
checkNotificationPermission: function() {
|
|
221
|
+
return asyncRequest('checkNotificationPermission', {});
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Request App Tracking Transparency permission (iOS only)
|
|
226
|
+
* @returns {Promise<string>} 'authorized', 'denied', 'not-determined', or 'restricted'
|
|
227
|
+
*/
|
|
228
|
+
requestATTPermission: function() {
|
|
229
|
+
return asyncRequest('requestATTPermission', {});
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Check App Tracking Transparency status without requesting (iOS only)
|
|
234
|
+
* @returns {Promise<string>} 'authorized', 'denied', 'not-determined', or 'restricted'
|
|
235
|
+
*/
|
|
236
|
+
checkATTPermission: function() {
|
|
237
|
+
return asyncRequest('checkATTPermission', {});
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Request location permission
|
|
242
|
+
* @param {Object} options - Optional configuration
|
|
243
|
+
* @param {string} options.precision - 'precise' (default) or 'coarse'. On Android, this determines
|
|
244
|
+
* whether to request ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION. On iOS, precision is
|
|
245
|
+
* controlled by the user in the permission dialog.
|
|
246
|
+
* @returns {Promise<string>} Permission result ('granted', 'denied', 'blocked', 'unavailable')
|
|
247
|
+
*/
|
|
248
|
+
requestLocationPermission: function(options) {
|
|
249
|
+
var opts = options || {};
|
|
250
|
+
return asyncRequest('requestLocationPermission', { precision: opts.precision || 'precise' });
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Request background location permission
|
|
255
|
+
* @returns {Promise<string>} Permission result ('granted', 'denied', 'blocked', 'unavailable')
|
|
256
|
+
*/
|
|
257
|
+
requestBackgroundLocationPermission: function() {
|
|
258
|
+
return asyncRequest('requestBackgroundLocationPermission', {});
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Get current location permission status
|
|
263
|
+
* @returns {Promise<Object>} Location permission status object:
|
|
264
|
+
* - foreground: 'granted' | 'denied' | 'blocked' | 'not_requested'
|
|
265
|
+
* - background: 'granted' | 'denied' | 'blocked' | 'not_requested'
|
|
266
|
+
* - precision: 'precise' | 'coarse' | 'unknown'
|
|
267
|
+
*/
|
|
268
|
+
getLocationPermissionStatus: function() {
|
|
269
|
+
return asyncRequest('getLocationPermissionStatus', {});
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Get current location
|
|
274
|
+
* @returns {Promise<Object>} Location coordinates
|
|
275
|
+
*/
|
|
276
|
+
getCurrentLocation: function() {
|
|
277
|
+
return asyncRequest('getCurrentLocation', {});
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
// ============================================
|
|
281
|
+
// Native Utilities
|
|
282
|
+
// ============================================
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Request app store review
|
|
286
|
+
* Note: This will complete the flow and show the review dialog after the flow closes.
|
|
287
|
+
* Due to iOS StoreKit limitations, reviews cannot be shown while a modal is visible.
|
|
288
|
+
* Use this as the final action in your flow.
|
|
289
|
+
*/
|
|
290
|
+
requestAppReview: function() {
|
|
291
|
+
postMessage('requestAppReview', {});
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Trigger haptic feedback
|
|
296
|
+
* @param {string} style - 'light', 'medium', 'heavy', 'success', 'warning', 'error', 'selection'
|
|
297
|
+
*/
|
|
298
|
+
haptic: function(style) {
|
|
299
|
+
postMessage('haptic', { style: style || 'medium' });
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Open a URL in the browser
|
|
304
|
+
* @param {string} url - URL to open
|
|
305
|
+
*/
|
|
306
|
+
openURL: function(url) {
|
|
307
|
+
postMessage('openURL', { url: url });
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Open app settings
|
|
312
|
+
*/
|
|
313
|
+
openSettings: function() {
|
|
314
|
+
postMessage('openSettings', {});
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Play a sound from a URL (external or base64 data URL)
|
|
319
|
+
* @param {string} url - Sound URL (https:// or data:audio/...)
|
|
320
|
+
* @param {Object} options - Optional playback options
|
|
321
|
+
* @param {number} options.volume - Volume level (0.0 - 1.0, default 1.0)
|
|
322
|
+
* @param {boolean} options.loop - Whether to loop the sound (default false)
|
|
323
|
+
* @param {function} options.onEnd - Callback when sound finishes playing
|
|
324
|
+
* @param {function} options.onError - Callback when an error occurs
|
|
325
|
+
* @returns {Object} Controller with { isPlaying, stop() }
|
|
326
|
+
*/
|
|
327
|
+
playSound: function(url, options) {
|
|
328
|
+
var opts = options || {};
|
|
329
|
+
var volume = typeof opts.volume === 'number' ? Math.max(0, Math.min(1, opts.volume)) : 1.0;
|
|
330
|
+
var loop = opts.loop === true;
|
|
331
|
+
var onEnd = typeof opts.onEnd === 'function' ? opts.onEnd : null;
|
|
332
|
+
var onError = typeof opts.onError === 'function' ? opts.onError : null;
|
|
333
|
+
|
|
334
|
+
var controller = {
|
|
335
|
+
isPlaying: false,
|
|
336
|
+
stop: function() {}
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
var audio = new Audio(url);
|
|
341
|
+
audio.volume = volume;
|
|
342
|
+
audio.loop = loop;
|
|
343
|
+
|
|
344
|
+
audio.onplay = function() {
|
|
345
|
+
controller.isPlaying = true;
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
audio.onended = function() {
|
|
349
|
+
controller.isPlaying = false;
|
|
350
|
+
if (onEnd) {
|
|
351
|
+
try { onEnd(); } catch (e) { console.warn('playSound onEnd error:', e); }
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
audio.onerror = function(e) {
|
|
356
|
+
controller.isPlaying = false;
|
|
357
|
+
console.warn('playSound error: Failed to load or play sound');
|
|
358
|
+
if (onError) {
|
|
359
|
+
try { onError(e); } catch (err) { console.warn('playSound onError callback error:', err); }
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
audio.onpause = function() {
|
|
364
|
+
if (!audio.ended) {
|
|
365
|
+
controller.isPlaying = false;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
controller.stop = function() {
|
|
370
|
+
try {
|
|
371
|
+
audio.pause();
|
|
372
|
+
audio.currentTime = 0;
|
|
373
|
+
controller.isPlaying = false;
|
|
374
|
+
} catch (e) {
|
|
375
|
+
// Audio may have been garbage collected
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
audio.play().catch(function(e) {
|
|
380
|
+
controller.isPlaying = false;
|
|
381
|
+
console.warn('playSound error: ' + e.message);
|
|
382
|
+
if (onError) {
|
|
383
|
+
try { onError(e); } catch (err) { console.warn('playSound onError callback error:', err); }
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
controller.isPlaying = true;
|
|
388
|
+
} catch (e) {
|
|
389
|
+
console.warn('playSound error: ' + e.message);
|
|
390
|
+
if (onError) {
|
|
391
|
+
try { onError(e); } catch (err) { console.warn('playSound onError callback error:', err); }
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return controller;
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// Mark bridge as ready
|
|
400
|
+
window.__mobanaBridgeReady = true;
|
|
401
|
+
|
|
402
|
+
// Dispatch ready event for flows that want to wait
|
|
403
|
+
if (typeof document !== 'undefined') {
|
|
404
|
+
document.dispatchEvent(new Event('mobana:ready'));
|
|
405
|
+
}
|
|
406
|
+
})();
|
|
407
|
+
`;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Build complete HTML with injected bridge, styles, and safe area CSS variables
|
|
412
|
+
*/
|
|
413
|
+
export function buildFlowHtml(html, css, js, bridgeScript, safeArea, colorScheme) {
|
|
414
|
+
let fullHtml = html;
|
|
415
|
+
|
|
416
|
+
// 0. Ensure viewport meta tag has viewport-fit=cover (required for edge-to-edge rendering on iOS)
|
|
417
|
+
const viewportMetaRegex = /<meta\s+[^>]*name=["']viewport["'][^>]*>/i;
|
|
418
|
+
const viewportMatch = fullHtml.match(viewportMetaRegex);
|
|
419
|
+
if (viewportMatch) {
|
|
420
|
+
const existingTag = viewportMatch[0];
|
|
421
|
+
if (!existingTag.includes('viewport-fit=cover')) {
|
|
422
|
+
// Append viewport-fit=cover to existing content attribute
|
|
423
|
+
const updatedTag = existingTag.replace(/content=["']([^"']*)["']/i, (match, content) => `content="${content}, viewport-fit=cover"`);
|
|
424
|
+
fullHtml = fullHtml.replace(existingTag, updatedTag);
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
// No viewport meta tag — inject a sensible default
|
|
428
|
+
const defaultViewport = '<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">';
|
|
429
|
+
if (fullHtml.includes('</head>')) {
|
|
430
|
+
fullHtml = fullHtml.replace(/<head([^>]*)>/i, `<head$1>${defaultViewport}`);
|
|
431
|
+
} else if (fullHtml.includes('<body')) {
|
|
432
|
+
fullHtml = fullHtml.replace('<body', `<head>${defaultViewport}</head><body`);
|
|
433
|
+
} else {
|
|
434
|
+
fullHtml = `<head>${defaultViewport}</head>` + fullHtml;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// 1. Inject SDK base resets FIRST (before user CSS, so flows can override)
|
|
439
|
+
const resetStyle = `<style data-mobana="reset">
|
|
440
|
+
/* Mobana SDK base resets — flows can override any of these */
|
|
441
|
+
*, *::before, *::after {
|
|
442
|
+
margin: 0;
|
|
443
|
+
padding: 0;
|
|
444
|
+
box-sizing: border-box;
|
|
445
|
+
-webkit-tap-highlight-color: transparent;
|
|
446
|
+
}
|
|
447
|
+
body {
|
|
448
|
+
-webkit-font-smoothing: antialiased;
|
|
449
|
+
-webkit-user-select: none;
|
|
450
|
+
user-select: none;
|
|
451
|
+
-webkit-touch-callout: none;
|
|
452
|
+
overflow: hidden;
|
|
453
|
+
}
|
|
454
|
+
</style>`;
|
|
455
|
+
if (fullHtml.includes('</head>')) {
|
|
456
|
+
// Insert at the START of <head> so it comes before any flow styles
|
|
457
|
+
fullHtml = fullHtml.replace(/<head([^>]*)>/i, `<head$1>${resetStyle}`);
|
|
458
|
+
} else if (fullHtml.includes('<body')) {
|
|
459
|
+
fullHtml = fullHtml.replace('<body', `<head>${resetStyle}</head><body`);
|
|
460
|
+
} else {
|
|
461
|
+
fullHtml = resetStyle + fullHtml;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// 2. Inject user CSS (if separate) — after resets, before env vars
|
|
465
|
+
if (css) {
|
|
466
|
+
const styleTag = `<style>${css}</style>`;
|
|
467
|
+
if (fullHtml.includes('</head>')) {
|
|
468
|
+
fullHtml = fullHtml.replace('</head>', `${styleTag}</head>`);
|
|
469
|
+
} else if (fullHtml.includes('<body')) {
|
|
470
|
+
fullHtml = fullHtml.replace('<body', `<head>${styleTag}</head><body`);
|
|
471
|
+
} else {
|
|
472
|
+
fullHtml = styleTag + fullHtml;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// 3. Inject CSS environment variables AFTER user CSS (SDK values take precedence)
|
|
477
|
+
// Note: We use values from react-native-safe-area-context (not CSS env()) for reliable
|
|
478
|
+
// cross-platform insets. Step 0 ensures viewport-fit=cover for edge-to-edge rendering.
|
|
479
|
+
const envVarsStyle = `<style data-mobana="env">
|
|
480
|
+
:root {
|
|
481
|
+
/* Color scheme - enables light-dark() CSS function */
|
|
482
|
+
color-scheme: ${colorScheme || 'light'};
|
|
483
|
+
--color-scheme: ${colorScheme || 'light'};
|
|
484
|
+
/* Safe area insets - from react-native-safe-area-context */
|
|
485
|
+
--safe-area-top: ${safeArea?.top ?? 0}px;
|
|
486
|
+
--safe-area-right: ${safeArea?.right ?? 0}px;
|
|
487
|
+
--safe-area-bottom: ${safeArea?.bottom ?? 0}px;
|
|
488
|
+
--safe-area-left: ${safeArea?.left ?? 0}px;
|
|
489
|
+
/* Screen dimensions */
|
|
490
|
+
--screen-width: ${safeArea?.width ?? 0}px;
|
|
491
|
+
--screen-height: ${safeArea?.height ?? 0}px;
|
|
492
|
+
}
|
|
493
|
+
</style>`;
|
|
494
|
+
if (fullHtml.includes('</head>')) {
|
|
495
|
+
fullHtml = fullHtml.replace('</head>', `${envVarsStyle}</head>`);
|
|
496
|
+
} else if (fullHtml.includes('<body')) {
|
|
497
|
+
fullHtml = fullHtml.replace('<body', `<head>${envVarsStyle}</head><body`);
|
|
498
|
+
} else {
|
|
499
|
+
fullHtml = envVarsStyle + fullHtml;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Inject JS if separate
|
|
503
|
+
if (js) {
|
|
504
|
+
const scriptTag = `<script>${js}</script>`;
|
|
505
|
+
if (fullHtml.includes('</body>')) {
|
|
506
|
+
fullHtml = fullHtml.replace('</body>', `${scriptTag}</body>`);
|
|
507
|
+
} else {
|
|
508
|
+
fullHtml = fullHtml + scriptTag;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Inject bridge script (must come before user JS)
|
|
513
|
+
const bridgeTag = `<script>${bridgeScript}</script>`;
|
|
514
|
+
if (fullHtml.includes('</head>')) {
|
|
515
|
+
fullHtml = fullHtml.replace('</head>', `${bridgeTag}</head>`);
|
|
516
|
+
} else if (fullHtml.includes('<body')) {
|
|
517
|
+
fullHtml = fullHtml.replace('<body', `<head>${bridgeTag}</head><body`);
|
|
518
|
+
} else {
|
|
519
|
+
fullHtml = bridgeTag + fullHtml;
|
|
520
|
+
}
|
|
521
|
+
return fullHtml;
|
|
522
|
+
}
|
|
523
|
+
//# sourceMappingURL=injectBridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["generateBridgeScript","context","contextJson","JSON","stringify","buildFlowHtml","html","css","js","bridgeScript","safeArea","colorScheme","fullHtml","viewportMetaRegex","viewportMatch","match","existingTag","includes","updatedTag","replace","content","defaultViewport","resetStyle","styleTag","envVarsStyle","top","right","bottom","left","width","height","scriptTag","bridgeTag"],"sourceRoot":"../../../src","sources":["bridge/injectBridge.ts"],"mappings":";;AAEA;AACA;AACA;;AAWA;AACA;AACA;AACA;AACA,OAAO,SAASA,oBAAoBA,CAACC,OAAsB,EAAU;EACnE,MAAMC,WAAW,GAAGC,IAAI,CAACC,SAAS,CAACH,OAAO,CAAC;;EAE3C;EACA,OAAO;AACT;AACA;AACA;AACA;AACA,oBAAoBC,WAAW;AAC/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD;;AAEA;AACA;AACA;AACA,OAAO,SAASG,aAAaA,CAC3BC,IAAY,EACZC,GAAuB,EACvBC,EAAsB,EACtBC,YAAoB,EACpBC,QAAmB,EACnBC,WAAyB,EACjB;EACR,IAAIC,QAAQ,GAAGN,IAAI;;EAEnB;EACA,MAAMO,iBAAiB,GAAG,2CAA2C;EACrE,MAAMC,aAAa,GAAGF,QAAQ,CAACG,KAAK,CAACF,iBAAiB,CAAC;EACvD,IAAIC,aAAa,EAAE;IA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","ignoreList":[]}
|