@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.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +249 -0
  3. package/android/build.gradle +50 -0
  4. package/android/src/main/AndroidManifest.xml +6 -0
  5. package/android/src/main/java/ai/mobana/sdk/MobanaModule.kt +67 -0
  6. package/android/src/main/java/ai/mobana/sdk/MobanaPackage.kt +19 -0
  7. package/app.plugin.js +274 -0
  8. package/ios/Mobana.h +11 -0
  9. package/ios/Mobana.m +20 -0
  10. package/lib/commonjs/Mobana.js +676 -0
  11. package/lib/commonjs/Mobana.js.map +1 -0
  12. package/lib/commonjs/NativeMobana.js +53 -0
  13. package/lib/commonjs/NativeMobana.js.map +1 -0
  14. package/lib/commonjs/api.js +201 -0
  15. package/lib/commonjs/api.js.map +1 -0
  16. package/lib/commonjs/bridge/index.js +19 -0
  17. package/lib/commonjs/bridge/index.js.map +1 -0
  18. package/lib/commonjs/bridge/injectBridge.js +528 -0
  19. package/lib/commonjs/bridge/injectBridge.js.map +1 -0
  20. package/lib/commonjs/components/FlowWebView.js +676 -0
  21. package/lib/commonjs/components/FlowWebView.js.map +1 -0
  22. package/lib/commonjs/components/MobanaProvider.js +275 -0
  23. package/lib/commonjs/components/MobanaProvider.js.map +1 -0
  24. package/lib/commonjs/components/index.js +20 -0
  25. package/lib/commonjs/components/index.js.map +1 -0
  26. package/lib/commonjs/device.js +49 -0
  27. package/lib/commonjs/device.js.map +1 -0
  28. package/lib/commonjs/index.js +20 -0
  29. package/lib/commonjs/index.js.map +1 -0
  30. package/lib/commonjs/package.json +1 -0
  31. package/lib/commonjs/storage.js +277 -0
  32. package/lib/commonjs/storage.js.map +1 -0
  33. package/lib/commonjs/types.js +2 -0
  34. package/lib/commonjs/types.js.map +1 -0
  35. package/lib/module/Mobana.js +673 -0
  36. package/lib/module/Mobana.js.map +1 -0
  37. package/lib/module/NativeMobana.js +49 -0
  38. package/lib/module/NativeMobana.js.map +1 -0
  39. package/lib/module/api.js +194 -0
  40. package/lib/module/api.js.map +1 -0
  41. package/lib/module/bridge/index.js +4 -0
  42. package/lib/module/bridge/index.js.map +1 -0
  43. package/lib/module/bridge/injectBridge.js +523 -0
  44. package/lib/module/bridge/injectBridge.js.map +1 -0
  45. package/lib/module/components/FlowWebView.js +672 -0
  46. package/lib/module/components/FlowWebView.js.map +1 -0
  47. package/lib/module/components/MobanaProvider.js +270 -0
  48. package/lib/module/components/MobanaProvider.js.map +1 -0
  49. package/lib/module/components/index.js +5 -0
  50. package/lib/module/components/index.js.map +1 -0
  51. package/lib/module/device.js +45 -0
  52. package/lib/module/device.js.map +1 -0
  53. package/lib/module/index.js +53 -0
  54. package/lib/module/index.js.map +1 -0
  55. package/lib/module/storage.js +257 -0
  56. package/lib/module/storage.js.map +1 -0
  57. package/lib/module/types.js +2 -0
  58. package/lib/module/types.js.map +1 -0
  59. package/lib/typescript/Mobana.d.ts +209 -0
  60. package/lib/typescript/Mobana.d.ts.map +1 -0
  61. package/lib/typescript/NativeMobana.d.ts +11 -0
  62. package/lib/typescript/NativeMobana.d.ts.map +1 -0
  63. package/lib/typescript/api.d.ts +34 -0
  64. package/lib/typescript/api.d.ts.map +1 -0
  65. package/lib/typescript/bridge/index.d.ts +3 -0
  66. package/lib/typescript/bridge/index.d.ts.map +1 -0
  67. package/lib/typescript/bridge/injectBridge.d.ts +23 -0
  68. package/lib/typescript/bridge/injectBridge.d.ts.map +1 -0
  69. package/lib/typescript/components/FlowWebView.d.ts +38 -0
  70. package/lib/typescript/components/FlowWebView.d.ts.map +1 -0
  71. package/lib/typescript/components/MobanaProvider.d.ts +65 -0
  72. package/lib/typescript/components/MobanaProvider.d.ts.map +1 -0
  73. package/lib/typescript/components/index.d.ts +5 -0
  74. package/lib/typescript/components/index.d.ts.map +1 -0
  75. package/lib/typescript/device.d.ts +6 -0
  76. package/lib/typescript/device.d.ts.map +1 -0
  77. package/lib/typescript/index.d.ts +46 -0
  78. package/lib/typescript/index.d.ts.map +1 -0
  79. package/lib/typescript/storage.d.ts +68 -0
  80. package/lib/typescript/storage.d.ts.map +1 -0
  81. package/lib/typescript/types.d.ts +298 -0
  82. package/lib/typescript/types.d.ts.map +1 -0
  83. package/mobana.podspec +19 -0
  84. package/package.json +131 -0
  85. package/src/Mobana.ts +742 -0
  86. package/src/NativeMobana.ts +61 -0
  87. package/src/api.ts +259 -0
  88. package/src/bridge/index.ts +2 -0
  89. package/src/bridge/injectBridge.ts +542 -0
  90. package/src/components/FlowWebView.tsx +826 -0
  91. package/src/components/MobanaProvider.tsx +393 -0
  92. package/src/components/index.ts +4 -0
  93. package/src/device.ts +42 -0
  94. package/src/index.ts +66 -0
  95. package/src/storage.ts +262 -0
  96. package/src/types.ts +362 -0
@@ -0,0 +1,61 @@
1
+ import { NativeModules, Platform } from 'react-native';
2
+
3
+ /**
4
+ * Native module interface for platform-specific functionality
5
+ * Currently used for Android Install Referrer
6
+ */
7
+ interface NativeMobanaModule {
8
+ /**
9
+ * Get the Install Referrer string from Android Play Store
10
+ * Returns null on iOS or if referrer is not available
11
+ */
12
+ getInstallReferrer(): Promise<string | null>;
13
+ }
14
+
15
+ // Try to get the native module, fallback to stub if not available
16
+ const NativeMobana: NativeMobanaModule | null =
17
+ NativeModules.Mobana
18
+ ? NativeModules.Mobana
19
+ : new Proxy(
20
+ {},
21
+ {
22
+ get() {
23
+ // Return null instead of throwing to support Expo Go / web
24
+ return () => Promise.resolve(null);
25
+ },
26
+ }
27
+ ) as NativeMobanaModule;
28
+
29
+ /**
30
+ * Get Android Install Referrer
31
+ * Extracts the dacid (Mobana Click ID) from the referrer string
32
+ */
33
+ export async function getInstallReferrer(): Promise<string | null> {
34
+ if (Platform.OS !== 'android') {
35
+ return null;
36
+ }
37
+
38
+ try {
39
+ const referrer = await NativeMobana?.getInstallReferrer();
40
+
41
+ if (!referrer) {
42
+ return null;
43
+ }
44
+
45
+ // Parse referrer string to extract dacid parameter
46
+ // Format: "utm_source=...&dacid=abc123&..."
47
+ const params = new URLSearchParams(referrer);
48
+ return params.get('dacid');
49
+ } catch {
50
+ // Install Referrer not available (e.g., not installed from Play Store)
51
+ return null;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Check if native module is available
57
+ * Used to determine if we can use Install Referrer
58
+ */
59
+ export function isNativeModuleAvailable(): boolean {
60
+ return NativeModules.Mobana != null;
61
+ }
package/src/api.ts ADDED
@@ -0,0 +1,259 @@
1
+ import type { FindResponse, DeviceInfo, ConversionEvent, FlowFetchResponse } from './types';
2
+
3
+ const DEFAULT_TIMEOUT = 10000; // 10 seconds
4
+
5
+ function buildHeaders(appKey: string): Record<string, string> {
6
+ const headers: Record<string, string> = {
7
+ 'Content-Type': 'application/json',
8
+ };
9
+ if (appKey) {
10
+ headers['X-App-Key'] = appKey;
11
+ }
12
+ return headers;
13
+ }
14
+
15
+ /**
16
+ * Make a request to the attribution API
17
+ */
18
+ async function request<T>(
19
+ endpoint: string,
20
+ path: string,
21
+ body: Record<string, unknown>,
22
+ appKey: string,
23
+ timeout: number = DEFAULT_TIMEOUT,
24
+ debug: boolean = false
25
+ ): Promise<T | null> {
26
+ const url = `${endpoint}${path}`;
27
+ const controller = new AbortController();
28
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
29
+
30
+ try {
31
+ if (debug) {
32
+ console.log(`[Mobana] POST ${url}`, body);
33
+ }
34
+
35
+ const response = await fetch(url, {
36
+ method: 'POST',
37
+ headers: buildHeaders(appKey),
38
+ body: JSON.stringify(body),
39
+ signal: controller.signal,
40
+ });
41
+
42
+ clearTimeout(timeoutId);
43
+
44
+ if (!response.ok) {
45
+ if (debug) {
46
+ console.log(`[Mobana] Request failed: ${response.status}`);
47
+ }
48
+ return null;
49
+ }
50
+
51
+ const data = await response.json();
52
+
53
+ if (debug) {
54
+ console.log(`[Mobana] Response:`, data);
55
+ }
56
+
57
+ return data as T;
58
+ } catch (error) {
59
+ clearTimeout(timeoutId);
60
+
61
+ if (debug) {
62
+ if (error instanceof Error && error.name === 'AbortError') {
63
+ console.log(`[Mobana] Request timed out after ${timeout}ms`);
64
+ } else {
65
+ console.log(`[Mobana] Request error:`, error);
66
+ }
67
+ }
68
+
69
+ return null;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Call /find endpoint to get attribution
75
+ */
76
+ export async function findAttribution<T = Record<string, unknown>>(
77
+ endpoint: string,
78
+ appKey: string,
79
+ installId: string,
80
+ deviceInfo: DeviceInfo,
81
+ dacid: string | null,
82
+ timeout: number,
83
+ debug: boolean
84
+ ): Promise<FindResponse<T> | null> {
85
+ return request<FindResponse<T>>(
86
+ endpoint,
87
+ '/find',
88
+ {
89
+ installId,
90
+ platform: deviceInfo.platform,
91
+ timezone: deviceInfo.timezone,
92
+ screenWidth: deviceInfo.screenWidth,
93
+ screenHeight: deviceInfo.screenHeight,
94
+ language: deviceInfo.language,
95
+ ...(dacid && { dacid }),
96
+ },
97
+ appKey,
98
+ timeout,
99
+ debug
100
+ );
101
+ }
102
+
103
+ /**
104
+ * Call /conversion endpoint to track a conversion
105
+ */
106
+ export async function trackConversionApi(
107
+ endpoint: string,
108
+ appKey: string,
109
+ event: ConversionEvent,
110
+ debug: boolean
111
+ ): Promise<boolean> {
112
+ const result = await request<{ success: boolean }>(
113
+ endpoint,
114
+ '/conversion',
115
+ {
116
+ installId: event.installId,
117
+ name: event.name,
118
+ value: event.value,
119
+ timestamp: event.timestamp,
120
+ flowSessionId: event.flowSessionId,
121
+ },
122
+ appKey,
123
+ DEFAULT_TIMEOUT,
124
+ debug
125
+ );
126
+
127
+ return result?.success ?? false;
128
+ }
129
+
130
+ // ============================================
131
+ // Flow API
132
+ // ============================================
133
+
134
+ /**
135
+ * Fetch flow content from the server
136
+ *
137
+ * @param endpoint - API endpoint
138
+ * @param slug - Flow slug identifier
139
+ * @param installId - Install ID for tracking
140
+ * @param cachedVersionId - If provided, server will return { cached: true } if version matches
141
+ * @param debug - Enable debug logging
142
+ */
143
+ export async function fetchFlow(
144
+ endpoint: string,
145
+ appKey: string,
146
+ slug: string,
147
+ installId: string,
148
+ cachedVersionId?: string,
149
+ timeout: number = DEFAULT_TIMEOUT,
150
+ debug: boolean = false
151
+ ): Promise<FlowFetchResponse | null> {
152
+ const params = new URLSearchParams({ installId });
153
+ if (cachedVersionId) {
154
+ params.set('versionId', cachedVersionId);
155
+ }
156
+
157
+ const url = `${endpoint}/flows/${slug}?${params.toString()}`;
158
+ const controller = new AbortController();
159
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
160
+
161
+ try {
162
+ if (debug) {
163
+ console.log(`[Mobana] GET ${url}`);
164
+ }
165
+
166
+ const response = await fetch(url, {
167
+ method: 'GET',
168
+ headers: buildHeaders(appKey),
169
+ signal: controller.signal,
170
+ });
171
+
172
+ clearTimeout(timeoutId);
173
+
174
+ if (!response.ok) {
175
+ if (debug) {
176
+ console.log(`[Mobana] Flow fetch failed: ${response.status}`);
177
+ }
178
+ return null;
179
+ }
180
+
181
+ const data = await response.json();
182
+
183
+ if (debug) {
184
+ console.log(`[Mobana] Flow response:`, data);
185
+ }
186
+
187
+ return data as FlowFetchResponse;
188
+ } catch (error) {
189
+ clearTimeout(timeoutId);
190
+
191
+ if (debug) {
192
+ if (error instanceof Error && error.name === 'AbortError') {
193
+ console.log(`[Mobana] Flow fetch timed out after ${timeout}ms`);
194
+ } else {
195
+ console.log(`[Mobana] Flow fetch error:`, error);
196
+ }
197
+ }
198
+
199
+ return null;
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Track a flow event
205
+ *
206
+ * @param endpoint - API endpoint
207
+ * @param slug - Flow slug identifier
208
+ * @param installId - Install ID for tracking
209
+ * @param versionId - Flow version that was shown
210
+ * @param sessionId - Session ID for grouping events from a single flow presentation
211
+ * @param event - Event name ('step-1', 'notifications-enabled', 'welcome-viewed', or other event)
212
+ * @param step - Optional step number for multi-step flows
213
+ * @param data - Optional custom event data
214
+ * @param debug - Enable debug logging
215
+ */
216
+ export async function trackFlowEvent(
217
+ endpoint: string,
218
+ appKey: string,
219
+ slug: string,
220
+ installId: string,
221
+ versionId: string,
222
+ sessionId: string,
223
+ event: string,
224
+ step?: number,
225
+ data?: unknown,
226
+ debug: boolean = false
227
+ ): Promise<boolean> {
228
+ const url = `${endpoint}/flows/${slug}/events`;
229
+
230
+ try {
231
+ if (debug) {
232
+ console.log(`[Mobana] POST ${url}`, { installId, versionId, sessionId, event, step, data });
233
+ }
234
+
235
+ const response = await fetch(url, {
236
+ method: 'POST',
237
+ headers: buildHeaders(appKey),
238
+ body: JSON.stringify({
239
+ installId,
240
+ versionId,
241
+ sessionId,
242
+ event,
243
+ step,
244
+ data,
245
+ }),
246
+ });
247
+
248
+ if (debug) {
249
+ console.log(`[Mobana] Flow event response: ${response.status}`);
250
+ }
251
+
252
+ return response.ok;
253
+ } catch (error) {
254
+ if (debug) {
255
+ console.log(`[Mobana] Flow event error:`, error);
256
+ }
257
+ return false;
258
+ }
259
+ }
@@ -0,0 +1,2 @@
1
+ export { generateBridgeScript, buildFlowHtml } from './injectBridge';
2
+ export type { BridgeContext } from './injectBridge';