@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mobana
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,249 @@
1
+ <p align="center">
2
+ <a href="https://mobana.ai" style="vertical-align: middle;">
3
+ <img alt="Mobana" src="https://mobana.ai/images/logos/mobana-transparent.png" height="32">
4
+ </a>
5
+ <br/>
6
+ <strong style="font-size: 1.3em">Mobana</strong>
7
+ </p>
8
+
9
+ <p align="center">
10
+ Simple, privacy-focused mobile app attribution, conversions, and remote flows for React Native.
11
+ </p>
12
+
13
+ <p align="center">
14
+ <a href="https://www.npmjs.com/package/@mobana/react-native-sdk"><img src="https://img.shields.io/npm/v/@mobana/react-native-sdk.svg?style=flat-square" alt="npm version"></a>
15
+ <a href="https://www.npmjs.com/package/@mobana/react-native-sdk"><img src="https://img.shields.io/npm/dm/@mobana/react-native-sdk.svg?style=flat-square" alt="npm downloads"></a>
16
+ <a href="https://github.com/mobana-hq/react-native-sdk/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@mobana/react-native-sdk.svg?style=flat-square" alt="license"></a>
17
+ <img src="https://img.shields.io/badge/platform-iOS%20%7C%20Android-lightgrey?style=flat-square" alt="platforms">
18
+ </p>
19
+
20
+ <p align="center">
21
+ <a href="https://mobana.ai/docs">Documentation</a> ·
22
+ <a href="https://mobana.ai/docs/quick-start">Quick Start</a> ·
23
+ <a href="https://mobana.ai/docs/sdk/overview">SDK Reference</a> ·
24
+ <a href="https://mobana.ai/docs/flows/bridge-overview">Flow Bridge</a> ·
25
+ <a href="https://mobana.ai/docs/guides/gdpr">Privacy & GDPR</a>
26
+ </p>
27
+
28
+ ---
29
+
30
+ ## Core Features
31
+
32
+ - **[Attribution & Deeplinking](https://mobana.ai/attribution)** — Know where your installs come from and pass custom data (promo codes, referral IDs, content) through app store installs. No IDFA/GAID required.
33
+ - **[Conversion Tracking](https://mobana.ai/conversion-tracking)** — Track post-install events and tie them back to campaigns for ROI measurement.
34
+ - **[Flows](https://mobana.ai/flows)** — Display dynamic remote experiences (onboarding, permission prompts, paywalls) built from the Mobana dashboard — no app store updates required.
35
+
36
+ ## Requirements
37
+
38
+ | Dependency | Minimum Version |
39
+ |------------|-----------------|
40
+ | React Native | `>= 0.72` |
41
+ | React | `>= 17.0` |
42
+ | Expo SDK | `50+` (Expo Go not supported) |
43
+ | iOS | `13.4+` |
44
+ | Android | API `23+` (Android 6.0) |
45
+
46
+ ## Table of Contents
47
+
48
+ - [Installation](#installation)
49
+ - [Bare React Native](#bare-react-native)
50
+ - [Expo](#expo)
51
+ - [Optional Peer Dependencies](#optional-peer-dependencies)
52
+ - [Quick Start](#quick-start)
53
+ - [Initialize the SDK](#1-initialize-the-sdk)
54
+ - [Get Attribution](#2-get-attribution)
55
+ - [Track Conversions](#3-track-conversions)
56
+ - [Show a Flow](#4-show-a-flow-optional)
57
+ - [API](#api)
58
+ - [Flows](#flows)
59
+ - [Privacy & GDPR](#privacy--gdpr)
60
+ - [Documentation](#documentation)
61
+ - [License](#license)
62
+
63
+ ## Installation
64
+
65
+ The SDK supports both **bare React Native** and **Expo**. Pick the section that matches your project setup — the SDK API is identical in both environments.
66
+
67
+ ### Bare React Native
68
+
69
+ Attribution and conversion tracking:
70
+
71
+ ```bash
72
+ npm install @mobana/react-native-sdk \
73
+ @react-native-async-storage/async-storage
74
+ ```
75
+
76
+ Add [Flows](#flows) support (requires `react-native-webview`):
77
+
78
+ ```bash
79
+ npm install @mobana/react-native-sdk \
80
+ @react-native-async-storage/async-storage \
81
+ react-native-webview
82
+ ```
83
+
84
+ > If your flows use native permissions, you'll need to configure your iOS **Podfile** and Android **AndroidManifest.xml**. See the **[full installation guide](https://mobana.ai/docs/installation)** for platform-specific setup.
85
+
86
+ ### Expo
87
+
88
+ Attribution and conversion tracking:
89
+
90
+ ```bash
91
+ npx expo install @mobana/react-native-sdk \
92
+ @react-native-async-storage/async-storage
93
+ ```
94
+
95
+ Add [Flows](#flows) support:
96
+
97
+ ```bash
98
+ npx expo install @mobana/react-native-sdk \
99
+ @react-native-async-storage/async-storage \
100
+ react-native-webview
101
+ ```
102
+
103
+ Add the plugin to your `app.json`:
104
+
105
+ ```json
106
+ {
107
+ "expo": {
108
+ "plugins": ["@mobana/react-native-sdk"]
109
+ }
110
+ }
111
+ ```
112
+
113
+ > **Note:** This SDK uses native code — Expo Go is not supported. Use `expo-dev-client` for development builds.
114
+
115
+ ### Optional Peer Dependencies
116
+
117
+ If your Flows use permissions, haptics, reviews, or location, install the relevant optional packages:
118
+
119
+ ```bash
120
+ npm install react-native-permissions \
121
+ react-native-haptic-feedback \
122
+ react-native-in-app-review \
123
+ react-native-geolocation-service \
124
+ react-native-safe-area-context
125
+ ```
126
+
127
+ > For iOS Podfile setup, Android manifest permissions, and Expo plugin configuration, see the **[full installation guide](https://mobana.ai/docs/installation)**.
128
+
129
+ ## Quick Start
130
+
131
+ ### 1. Initialize the SDK
132
+
133
+ Call `init` once when your app starts. Get your App ID from the [Mobana dashboard](https://mobana.ai).
134
+
135
+ ```typescript
136
+ import { Mobana } from '@mobana/react-native-sdk';
137
+
138
+ await Mobana.init({
139
+ appId: 'YOUR_APP_ID',
140
+ debug: __DEV__,
141
+ });
142
+ ```
143
+
144
+ ### 2. Get Attribution
145
+
146
+ ```typescript
147
+ const attribution = await Mobana.getAttribution();
148
+
149
+ if (attribution) {
150
+ console.log(attribution.utm_source); // e.g. "facebook"
151
+ console.log(attribution.utm_campaign); // e.g. "summer_sale"
152
+ console.log(attribution.confidence); // 0.0–1.0
153
+
154
+ if (attribution.data?.promo) {
155
+ applyPromoCode(attribution.data.promo);
156
+ }
157
+ }
158
+ ```
159
+
160
+ `getAttribution()` never throws — it returns `null` when there's no match or on error.
161
+
162
+ ### 3. Track Conversions
163
+
164
+ ```typescript
165
+ Mobana.trackConversion('signup');
166
+ Mobana.trackConversion('purchase', 49.99);
167
+ ```
168
+
169
+ ### 4. Show a Flow (optional)
170
+
171
+ Wrap your app with `MobanaProvider` and start flows by slug:
172
+
173
+ ```tsx
174
+ import { MobanaProvider, Mobana } from '@mobana/react-native-sdk';
175
+
176
+ function App() {
177
+ return (
178
+ <MobanaProvider>
179
+ <YourApp />
180
+ </MobanaProvider>
181
+ );
182
+ }
183
+
184
+ // Somewhere in your app
185
+ const result = await Mobana.startFlow('onboarding');
186
+
187
+ if (result.completed) {
188
+ console.log('User completed onboarding!', result.data);
189
+ }
190
+ ```
191
+
192
+ > For the full walkthrough, see the **[Quick Start guide](https://mobana.ai/docs/quick-start)**.
193
+
194
+ ## API
195
+
196
+ | Method | Description | Reference |
197
+ |--------|-------------|-----------|
198
+ | `Mobana.init(config)` | Initialize the SDK with your App ID | [Docs →](https://mobana.ai/docs/sdk/init) |
199
+ | `Mobana.getAttribution()` | Get install attribution and deeplink data | [Docs →](https://mobana.ai/docs/sdk/get-attribution) |
200
+ | `Mobana.trackConversion(name, value?)` | Track post-install conversion events | [Docs →](https://mobana.ai/docs/sdk/track-conversion) |
201
+ | `Mobana.startFlow(slug, options?)` | Display an in-app flow | [Docs →](https://mobana.ai/docs/sdk/start-flow) |
202
+ | `Mobana.prefetchFlow(slug)` | Prefetch a flow for instant display | [Docs →](https://mobana.ai/docs/sdk/prefetch-flow) |
203
+ | `Mobana.setEnabled(enabled)` | Enable/disable the SDK (GDPR consent) | [Docs →](https://mobana.ai/docs/sdk/set-enabled) |
204
+ | `Mobana.reset()` | Clear stored data, generate new install ID | [Docs →](https://mobana.ai/docs/sdk/reset) |
205
+ | `<MobanaProvider>` | Context provider for Flows (wraps your app) | [Docs →](https://mobana.ai/docs/sdk/provider) |
206
+
207
+ > For full API reference with all options and return types, see the **[SDK Overview](https://mobana.ai/docs/sdk/overview)**.
208
+
209
+ ## Flows
210
+
211
+ Flows are rich in-app experiences (onboarding, permission prompts, paywalls, announcements) you build visually in the Mobana dashboard. They run inside a WebView and communicate with your app through a JavaScript bridge.
212
+
213
+ Inside a flow, you have access to attribution data, custom parameters, native permissions, haptics, sounds, and more — all through the `Mobana` bridge object.
214
+
215
+ | Topic | Link |
216
+ |-------|------|
217
+ | What are Flows? | [mobana.ai/flows](https://mobana.ai/flows) |
218
+ | Building Flows | [Guide →](https://mobana.ai/docs/guides/building-flows) |
219
+ | Bridge Overview | [Docs →](https://mobana.ai/docs/flows/bridge-overview) |
220
+ | Permissions | [Docs →](https://mobana.ai/docs/flows/bridge-permissions) |
221
+ | Events & Tracking | [Guide →](https://mobana.ai/docs/guides/flow-events-tracking) |
222
+ | CSS Variables | [Docs →](https://mobana.ai/docs/flows/css-variables) |
223
+
224
+ ## Privacy & GDPR
225
+
226
+ Mobana is built with privacy at its core:
227
+
228
+ - **No device IDs** — IDFA/GAID are never required or collected
229
+ - **Privacy-first matching** — attribution works without invasive device fingerprinting
230
+ - **Minimal data** — only what's needed for attribution, nothing more
231
+ - **Opt-out support** — call `Mobana.setEnabled(false)` to disable all tracking
232
+ - **GDPR/CCPA compliant** — see our [GDPR guide](https://mobana.ai/docs/guides/gdpr) and [Privacy Policy](https://mobana.ai/privacy)
233
+
234
+ ## Documentation
235
+
236
+ | Resource | Link |
237
+ |----------|------|
238
+ | Full Documentation | [mobana.ai/docs](https://mobana.ai/docs) |
239
+ | Installation Guide | [mobana.ai/docs/installation](https://mobana.ai/docs/installation) |
240
+ | Quick Start | [mobana.ai/docs/quick-start](https://mobana.ai/docs/quick-start) |
241
+ | SDK Reference | [mobana.ai/docs/sdk/overview](https://mobana.ai/docs/sdk/overview) |
242
+ | Flow Bridge API | [mobana.ai/docs/flows/bridge-overview](https://mobana.ai/docs/flows/bridge-overview) |
243
+ | Custom Endpoints | [mobana.ai/docs/guides/custom-endpoints](https://mobana.ai/docs/guides/custom-endpoints) |
244
+ | GDPR & Privacy | [mobana.ai/docs/guides/gdpr](https://mobana.ai/docs/guides/gdpr) |
245
+ | Test Setup | [mobana.ai/docs/test-setup](https://mobana.ai/docs/test-setup) |
246
+
247
+ ## License
248
+
249
+ MIT — see [LICENSE](./LICENSE) for details.
@@ -0,0 +1,50 @@
1
+ apply plugin: 'com.android.library'
2
+ apply plugin: 'kotlin-android'
3
+
4
+ def safeExtGet(prop, fallback) {
5
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
6
+ }
7
+
8
+ android {
9
+ namespace 'ai.mobana.sdk'
10
+ compileSdkVersion safeExtGet('compileSdkVersion', 34)
11
+
12
+ defaultConfig {
13
+ minSdkVersion safeExtGet('minSdkVersion', 23)
14
+ targetSdkVersion safeExtGet('targetSdkVersion', 34)
15
+ }
16
+
17
+ buildTypes {
18
+ release {
19
+ minifyEnabled false
20
+ }
21
+ }
22
+
23
+ compileOptions {
24
+ sourceCompatibility JavaVersion.VERSION_17
25
+ targetCompatibility JavaVersion.VERSION_17
26
+ }
27
+
28
+ kotlinOptions {
29
+ jvmTarget = '17'
30
+ }
31
+
32
+ sourceSets {
33
+ main {
34
+ java.srcDirs = ['src/main/java']
35
+ }
36
+ }
37
+ }
38
+
39
+ repositories {
40
+ google()
41
+ mavenCentral()
42
+ }
43
+
44
+ dependencies {
45
+ implementation "com.facebook.react:react-native:+"
46
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:${safeExtGet('kotlinVersion', '1.9.0')}"
47
+
48
+ // Google Play Install Referrer API
49
+ implementation 'com.android.installreferrer:installreferrer:2.2'
50
+ }
@@ -0,0 +1,6 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+
3
+ <!-- Install Referrer requires INTERNET permission -->
4
+ <uses-permission android:name="android.permission.INTERNET" />
5
+
6
+ </manifest>
@@ -0,0 +1,67 @@
1
+ package ai.mobana.sdk
2
+
3
+ import com.android.installreferrer.api.InstallReferrerClient
4
+ import com.android.installreferrer.api.InstallReferrerStateListener
5
+ import com.facebook.react.bridge.*
6
+
7
+ /**
8
+ * Native module for Mobana SDK
9
+ * Provides Android Install Referrer functionality
10
+ */
11
+ class MobanaModule(reactContext: ReactApplicationContext) :
12
+ ReactContextBaseJavaModule(reactContext) {
13
+
14
+ override fun getName(): String = "Mobana"
15
+
16
+ /**
17
+ * Get the Install Referrer string from Google Play Store
18
+ * Returns the full referrer string which contains UTM params and dacid
19
+ */
20
+ @ReactMethod
21
+ fun getInstallReferrer(promise: Promise) {
22
+ val context = reactApplicationContext
23
+
24
+ try {
25
+ val referrerClient = InstallReferrerClient.newBuilder(context).build()
26
+
27
+ referrerClient.startConnection(object : InstallReferrerStateListener {
28
+ override fun onInstallReferrerSetupFinished(responseCode: Int) {
29
+ when (responseCode) {
30
+ InstallReferrerClient.InstallReferrerResponse.OK -> {
31
+ try {
32
+ val response = referrerClient.installReferrer
33
+ val referrer = response.installReferrer
34
+ referrerClient.endConnection()
35
+ promise.resolve(referrer)
36
+ } catch (e: Exception) {
37
+ referrerClient.endConnection()
38
+ promise.resolve(null)
39
+ }
40
+ }
41
+ InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED -> {
42
+ // Install Referrer API not supported on this device
43
+ referrerClient.endConnection()
44
+ promise.resolve(null)
45
+ }
46
+ InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE -> {
47
+ // Connection could not be established
48
+ referrerClient.endConnection()
49
+ promise.resolve(null)
50
+ }
51
+ else -> {
52
+ referrerClient.endConnection()
53
+ promise.resolve(null)
54
+ }
55
+ }
56
+ }
57
+
58
+ override fun onInstallReferrerServiceDisconnected() {
59
+ // Connection was lost, but promise may already be resolved
60
+ // Do nothing - don't reject as this can happen after successful read
61
+ }
62
+ })
63
+ } catch (e: Exception) {
64
+ promise.resolve(null)
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,19 @@
1
+ package ai.mobana.sdk
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ /**
9
+ * React Native package that registers the Mobana native module
10
+ */
11
+ class MobanaPackage : ReactPackage {
12
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
13
+ return listOf(MobanaModule(reactContext))
14
+ }
15
+
16
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
17
+ return emptyList()
18
+ }
19
+ }
package/app.plugin.js ADDED
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Expo Config Plugin for @mobana/react-native-sdk
3
+ *
4
+ * This plugin automatically configures native dependencies for Expo projects.
5
+ *
6
+ * Usage in app.json / app.config.js:
7
+ * {
8
+ * "expo": {
9
+ * "plugins": [
10
+ * // Attribution-only (no special permissions needed):
11
+ * "@mobana/react-native-sdk"
12
+ *
13
+ * // Or with Flows that need permissions:
14
+ * ["@mobana/react-native-sdk", {
15
+ * "permissions": ["Notifications", "AppTrackingTransparency"]
16
+ * }]
17
+ * ]
18
+ * }
19
+ * }
20
+ *
21
+ * Available permissions (opt-in, none enabled by default):
22
+ * - Notifications (for push notification prompts)
23
+ * - AppTrackingTransparency (for ATT prompts on iOS 14.5+)
24
+ * - LocationWhenInUse (for location-based flows)
25
+ * - LocationAlways (for background location flows)
26
+ */
27
+
28
+ const {
29
+ withProjectBuildGradle,
30
+ withMainApplication,
31
+ withDangerousMod,
32
+ withInfoPlist,
33
+ withAndroidManifest,
34
+ } = require('@expo/config-plugins');
35
+ const fs = require('fs');
36
+ const path = require('path');
37
+
38
+ // No permissions enabled by default - users must explicitly opt-in
39
+ // This prevents unexpected permission entries in Info.plist / AndroidManifest
40
+ const DEFAULT_PERMISSIONS = [];
41
+
42
+ /**
43
+ * Add Install Referrer dependency to Android build.gradle
44
+ */
45
+ function withInstallReferrerDependency(config) {
46
+ return withProjectBuildGradle(config, (config) => {
47
+ if (config.modResults.language === 'groovy') {
48
+ const buildGradle = config.modResults.contents;
49
+
50
+ // Check if already added
51
+ if (!buildGradle.includes('com.android.installreferrer:installreferrer')) {
52
+ // Add to allprojects dependencies
53
+ const allProjectsPattern = /allprojects\s*\{[\s\S]*?repositories\s*\{/;
54
+
55
+ if (allProjectsPattern.test(buildGradle)) {
56
+ // Project has allprojects block, the dependency will be resolved
57
+ // through the library's own build.gradle
58
+ }
59
+ }
60
+ }
61
+ return config;
62
+ });
63
+ }
64
+
65
+ /**
66
+ * Add MobanaPackage to MainApplication.java
67
+ */
68
+ function withMobanaPackage(config) {
69
+ return withMainApplication(config, (config) => {
70
+ const mainApplication = config.modResults.contents;
71
+
72
+ // Check if already imported
73
+ if (!mainApplication.includes('ai.mobana.sdk.MobanaPackage')) {
74
+ // Add import
75
+ const importPattern = /^import.*$/m;
76
+ config.modResults.contents = mainApplication.replace(
77
+ importPattern,
78
+ (match) => `${match}\nimport ai.mobana.sdk.MobanaPackage;`
79
+ );
80
+
81
+ // Add to getPackages
82
+ const packagesPattern = /new PackageList\(this\)\.getPackages\(\)/;
83
+ if (packagesPattern.test(config.modResults.contents)) {
84
+ // Using autolinking, package should be auto-registered
85
+ // No manual addition needed
86
+ }
87
+ }
88
+
89
+ return config;
90
+ });
91
+ }
92
+
93
+ /**
94
+ * Add required Android permissions to AndroidManifest.xml
95
+ * This enables react-native-permissions to request these permissions at runtime
96
+ */
97
+ function withAndroidPermissions(config, permissions) {
98
+ // Skip if no permissions configured
99
+ if (!permissions || permissions.length === 0) {
100
+ return config;
101
+ }
102
+
103
+ return withAndroidManifest(config, (config) => {
104
+ const manifest = config.modResults.manifest;
105
+
106
+ // Ensure uses-permission array exists
107
+ if (!manifest['uses-permission']) {
108
+ manifest['uses-permission'] = [];
109
+ }
110
+
111
+ // Helper to add a permission if not already present
112
+ const addPermission = (permissionName) => {
113
+ const exists = manifest['uses-permission'].some(
114
+ (p) => p.$?.['android:name'] === permissionName
115
+ );
116
+ if (!exists) {
117
+ manifest['uses-permission'].push({
118
+ $: { 'android:name': permissionName },
119
+ });
120
+ }
121
+ };
122
+
123
+ // Add POST_NOTIFICATIONS for Android 13+ (API 33+)
124
+ if (permissions.includes('Notifications')) {
125
+ addPermission('android.permission.POST_NOTIFICATIONS');
126
+ }
127
+
128
+ // Add location permissions
129
+ if (permissions.includes('LocationWhenInUse') || permissions.includes('LocationAlways')) {
130
+ addPermission('android.permission.ACCESS_FINE_LOCATION');
131
+ addPermission('android.permission.ACCESS_COARSE_LOCATION');
132
+ }
133
+
134
+ // Add background location permission
135
+ if (permissions.includes('LocationAlways')) {
136
+ addPermission('android.permission.ACCESS_BACKGROUND_LOCATION');
137
+ }
138
+
139
+ return config;
140
+ });
141
+ }
142
+
143
+ /**
144
+ * Configure iOS Podfile for react-native-permissions
145
+ * This adds the setup_permissions call to enable the required permission pods
146
+ */
147
+ function withReactNativePermissionsPodfile(config, permissions) {
148
+ // Skip if no permissions configured - don't inject setup_permissions at all
149
+ if (!permissions || permissions.length === 0) {
150
+ return config;
151
+ }
152
+
153
+ return withDangerousMod(config, [
154
+ 'ios',
155
+ async (config) => {
156
+ const podfilePath = path.join(config.modRequest.platformProjectRoot, 'Podfile');
157
+
158
+ if (!fs.existsSync(podfilePath)) {
159
+ return config;
160
+ }
161
+
162
+ let podfileContents = fs.readFileSync(podfilePath, 'utf-8');
163
+
164
+ // Check if react-native-permissions setup is already configured
165
+ if (podfileContents.includes('react-native-permissions/scripts/setup')) {
166
+ return config;
167
+ }
168
+
169
+ // Build the setup_permissions call
170
+ const permissionsArray = permissions.map(p => ` '${p}'`).join(',\n');
171
+ const setupBlock = `
172
+ # react-native-permissions setup for Mobana SDK
173
+ def node_require(script)
174
+ require Pod::Executable.execute_command('node', ['-p',
175
+ "require.resolve('\#{script}', {paths: [process.argv[1]]})",
176
+ __dir__]).strip
177
+ end
178
+
179
+ node_require('react-native-permissions/scripts/setup.rb')
180
+
181
+ setup_permissions([
182
+ ${permissionsArray}
183
+ ])
184
+ `;
185
+
186
+ // Insert after the 'platform :ios' line or at the beginning of target block
187
+ const platformPattern = /^platform :ios.*$/m;
188
+ const targetPattern = /^target\s+['"].*['"]\s+do$/m;
189
+
190
+ if (platformPattern.test(podfileContents)) {
191
+ podfileContents = podfileContents.replace(
192
+ platformPattern,
193
+ (match) => `${match}\n${setupBlock}`
194
+ );
195
+ } else if (targetPattern.test(podfileContents)) {
196
+ podfileContents = podfileContents.replace(
197
+ targetPattern,
198
+ (match) => `${setupBlock}\n${match}`
199
+ );
200
+ } else {
201
+ // Fallback: prepend to file
202
+ podfileContents = setupBlock + '\n' + podfileContents;
203
+ }
204
+
205
+ fs.writeFileSync(podfilePath, podfileContents);
206
+
207
+ return config;
208
+ },
209
+ ]);
210
+ }
211
+
212
+ /**
213
+ * Add required iOS Info.plist entries for permissions
214
+ */
215
+ function withPermissionInfoPlist(config, permissions) {
216
+ // Skip if no permissions configured
217
+ if (!permissions || permissions.length === 0) {
218
+ return config;
219
+ }
220
+
221
+ return withInfoPlist(config, (config) => {
222
+ // Add usage description strings for permissions that require them
223
+ if (permissions.includes('Notifications')) {
224
+ // Notifications don't require a usage description, but we can add a background mode
225
+ }
226
+
227
+ if (permissions.includes('AppTrackingTransparency')) {
228
+ if (!config.modResults.NSUserTrackingUsageDescription) {
229
+ config.modResults.NSUserTrackingUsageDescription =
230
+ 'This identifier will be used to deliver personalized ads to you.';
231
+ }
232
+ }
233
+
234
+ if (permissions.includes('LocationWhenInUse') || permissions.includes('LocationAlways')) {
235
+ if (!config.modResults.NSLocationWhenInUseUsageDescription) {
236
+ config.modResults.NSLocationWhenInUseUsageDescription =
237
+ 'This app needs access to your location.';
238
+ }
239
+ }
240
+
241
+ if (permissions.includes('LocationAlways')) {
242
+ if (!config.modResults.NSLocationAlwaysAndWhenInUseUsageDescription) {
243
+ config.modResults.NSLocationAlwaysAndWhenInUseUsageDescription =
244
+ 'This app needs access to your location in the background.';
245
+ }
246
+ }
247
+
248
+ return config;
249
+ });
250
+ }
251
+
252
+ /**
253
+ * Main plugin entry point
254
+ * @param {object} config - Expo config
255
+ * @param {object} props - Plugin props
256
+ * @param {string[]} props.permissions - Array of permissions to enable
257
+ */
258
+ function withMobana(config, props = {}) {
259
+ const permissions = props.permissions || DEFAULT_PERMISSIONS;
260
+
261
+ // Android configuration
262
+ config = withInstallReferrerDependency(config);
263
+ config = withAndroidPermissions(config, permissions);
264
+ // Note: For Expo SDK 50+, native modules are auto-linked
265
+ // withMobanaPackage is kept for older versions
266
+
267
+ // iOS configuration for react-native-permissions
268
+ config = withReactNativePermissionsPodfile(config, permissions);
269
+ config = withPermissionInfoPlist(config, permissions);
270
+
271
+ return config;
272
+ }
273
+
274
+ module.exports = withMobana;