react-native-carrot-quest 0.1.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 (121) hide show
  1. package/CarrotQuest.podspec +34 -0
  2. package/LICENSE +20 -0
  3. package/README.md +457 -0
  4. package/android/CMakeLists.txt +24 -0
  5. package/android/build.gradle +136 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/cpp-adapter.cpp +11 -0
  8. package/android/src/main/java/com/margelo/nitro/carrotquest/CarrotQuest.kt +585 -0
  9. package/android/src/main/java/com/margelo/nitro/carrotquest/CarrotQuestPackage.kt +22 -0
  10. package/android/src/main/java/com/margelo/nitro/carrotquest/CarrotQuestSetup.kt +472 -0
  11. package/ios/CarrotQuest.swift +588 -0
  12. package/ios/CarrotQuestSetup.swift +333 -0
  13. package/lib/module/CarrotQuest.nitro.js +4 -0
  14. package/lib/module/CarrotQuest.nitro.js.map +1 -0
  15. package/lib/module/carrot-quest.js +136 -0
  16. package/lib/module/carrot-quest.js.map +1 -0
  17. package/lib/module/carrot-quest.native.js +155 -0
  18. package/lib/module/carrot-quest.native.js.map +1 -0
  19. package/lib/module/index.js +6 -0
  20. package/lib/module/index.js.map +1 -0
  21. package/lib/module/package.json +1 -0
  22. package/lib/module/subscriptions.js +76 -0
  23. package/lib/module/subscriptions.js.map +1 -0
  24. package/lib/typescript/package.json +1 -0
  25. package/lib/typescript/src/CarrotQuest.nitro.d.ts +394 -0
  26. package/lib/typescript/src/CarrotQuest.nitro.d.ts.map +1 -0
  27. package/lib/typescript/src/carrot-quest.d.ts +37 -0
  28. package/lib/typescript/src/carrot-quest.d.ts.map +1 -0
  29. package/lib/typescript/src/carrot-quest.native.d.ts +37 -0
  30. package/lib/typescript/src/carrot-quest.native.d.ts.map +1 -0
  31. package/lib/typescript/src/index.d.ts +6 -0
  32. package/lib/typescript/src/index.d.ts.map +1 -0
  33. package/lib/typescript/src/subscriptions.d.ts +32 -0
  34. package/lib/typescript/src/subscriptions.d.ts.map +1 -0
  35. package/nitro.json +23 -0
  36. package/nitrogen/generated/android/c++/JCarrotQuestEventValue.cpp +30 -0
  37. package/nitrogen/generated/android/c++/JCarrotQuestEventValue.hpp +83 -0
  38. package/nitrogen/generated/android/c++/JCarrotQuestLogCategory.hpp +76 -0
  39. package/nitrogen/generated/android/c++/JCarrotQuestLogEntry.hpp +100 -0
  40. package/nitrogen/generated/android/c++/JCarrotQuestLogLevel.hpp +70 -0
  41. package/nitrogen/generated/android/c++/JCarrotQuestProperty.hpp +74 -0
  42. package/nitrogen/generated/android/c++/JCarrotQuestPropertyKind.hpp +61 -0
  43. package/nitrogen/generated/android/c++/JCarrotQuestPropertyOperation.hpp +73 -0
  44. package/nitrogen/generated/android/c++/JCarrotQuestSetupOptions.hpp +94 -0
  45. package/nitrogen/generated/android/c++/JCarrotQuestTheme.hpp +64 -0
  46. package/nitrogen/generated/android/c++/JCarrotQuestUrlSource.hpp +64 -0
  47. package/nitrogen/generated/android/c++/JCarrotQuestUserProperties.hpp +84 -0
  48. package/nitrogen/generated/android/c++/JFunc_void_CarrotQuestLogEntry.hpp +84 -0
  49. package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +75 -0
  50. package/nitrogen/generated/android/c++/JFunc_void_double.hpp +75 -0
  51. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  52. package/nitrogen/generated/android/c++/JHybridCarrotQuestSpec.cpp +388 -0
  53. package/nitrogen/generated/android/c++/JHybridCarrotQuestSpec.hpp +96 -0
  54. package/nitrogen/generated/android/carrotquest+autolinking.cmake +82 -0
  55. package/nitrogen/generated/android/carrotquest+autolinking.gradle +27 -0
  56. package/nitrogen/generated/android/carrotquestOnLoad.cpp +62 -0
  57. package/nitrogen/generated/android/carrotquestOnLoad.hpp +34 -0
  58. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/CarrotQuestEventValue.kt +75 -0
  59. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/CarrotQuestLogCategory.kt +29 -0
  60. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/CarrotQuestLogEntry.kt +81 -0
  61. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/CarrotQuestLogLevel.kt +27 -0
  62. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/CarrotQuestProperty.kt +66 -0
  63. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/CarrotQuestPropertyKind.kt +24 -0
  64. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/CarrotQuestPropertyOperation.kt +28 -0
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/CarrotQuestSetupOptions.kt +91 -0
  66. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/CarrotQuestTheme.kt +25 -0
  67. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/CarrotQuestUrlSource.kt +25 -0
  68. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/CarrotQuestUserProperties.kt +66 -0
  69. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/Func_void_CarrotQuestLogEntry.kt +80 -0
  70. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/Func_void_bool.kt +80 -0
  71. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/Func_void_double.kt +80 -0
  72. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/Func_void_std__string.kt +80 -0
  73. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/HybridCarrotQuestSpec.kt +209 -0
  74. package/nitrogen/generated/android/kotlin/com/margelo/nitro/carrotquest/carrotquestOnLoad.kt +35 -0
  75. package/nitrogen/generated/ios/CarrotQuest+autolinking.rb +62 -0
  76. package/nitrogen/generated/ios/CarrotQuest-Swift-Cxx-Bridge.cpp +97 -0
  77. package/nitrogen/generated/ios/CarrotQuest-Swift-Cxx-Bridge.hpp +637 -0
  78. package/nitrogen/generated/ios/CarrotQuest-Swift-Cxx-Umbrella.hpp +80 -0
  79. package/nitrogen/generated/ios/CarrotQuestAutolinking.mm +33 -0
  80. package/nitrogen/generated/ios/CarrotQuestAutolinking.swift +26 -0
  81. package/nitrogen/generated/ios/c++/HybridCarrotQuestSpecSwift.cpp +11 -0
  82. package/nitrogen/generated/ios/c++/HybridCarrotQuestSpecSwift.hpp +344 -0
  83. package/nitrogen/generated/ios/swift/CarrotQuestEventValue.swift +32 -0
  84. package/nitrogen/generated/ios/swift/CarrotQuestLogCategory.swift +64 -0
  85. package/nitrogen/generated/ios/swift/CarrotQuestLogEntry.swift +86 -0
  86. package/nitrogen/generated/ios/swift/CarrotQuestLogLevel.swift +56 -0
  87. package/nitrogen/generated/ios/swift/CarrotQuestProperty.swift +56 -0
  88. package/nitrogen/generated/ios/swift/CarrotQuestPropertyKind.swift +44 -0
  89. package/nitrogen/generated/ios/swift/CarrotQuestPropertyOperation.swift +60 -0
  90. package/nitrogen/generated/ios/swift/CarrotQuestSetupOptions.swift +172 -0
  91. package/nitrogen/generated/ios/swift/CarrotQuestTheme.swift +48 -0
  92. package/nitrogen/generated/ios/swift/CarrotQuestUrlSource.swift +48 -0
  93. package/nitrogen/generated/ios/swift/CarrotQuestUserProperties.swift +110 -0
  94. package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
  95. package/nitrogen/generated/ios/swift/Func_void_CarrotQuestLogEntry.swift +46 -0
  96. package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
  97. package/nitrogen/generated/ios/swift/Func_void_double.swift +46 -0
  98. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
  99. package/nitrogen/generated/ios/swift/Func_void_std__optional_double_.swift +53 -0
  100. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +53 -0
  101. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
  102. package/nitrogen/generated/ios/swift/HybridCarrotQuestSpec.swift +88 -0
  103. package/nitrogen/generated/ios/swift/HybridCarrotQuestSpec_cxx.swift +674 -0
  104. package/nitrogen/generated/shared/c++/CarrotQuestLogCategory.hpp +100 -0
  105. package/nitrogen/generated/shared/c++/CarrotQuestLogEntry.hpp +114 -0
  106. package/nitrogen/generated/shared/c++/CarrotQuestLogLevel.hpp +92 -0
  107. package/nitrogen/generated/shared/c++/CarrotQuestProperty.hpp +101 -0
  108. package/nitrogen/generated/shared/c++/CarrotQuestPropertyKind.hpp +80 -0
  109. package/nitrogen/generated/shared/c++/CarrotQuestPropertyOperation.hpp +96 -0
  110. package/nitrogen/generated/shared/c++/CarrotQuestSetupOptions.hpp +121 -0
  111. package/nitrogen/generated/shared/c++/CarrotQuestTheme.hpp +84 -0
  112. package/nitrogen/generated/shared/c++/CarrotQuestUrlSource.hpp +84 -0
  113. package/nitrogen/generated/shared/c++/CarrotQuestUserProperties.hpp +97 -0
  114. package/nitrogen/generated/shared/c++/HybridCarrotQuestSpec.cpp +55 -0
  115. package/nitrogen/generated/shared/c++/HybridCarrotQuestSpec.hpp +118 -0
  116. package/package.json +140 -0
  117. package/src/CarrotQuest.nitro.ts +493 -0
  118. package/src/carrot-quest.native.tsx +241 -0
  119. package/src/carrot-quest.tsx +207 -0
  120. package/src/index.tsx +57 -0
  121. package/src/subscriptions.ts +83 -0
@@ -0,0 +1,34 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "CarrotQuest"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/ticketscloud/react-native-carrot-quest.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = [
17
+ "ios/**/*.{swift}",
18
+ "ios/**/*.{m,mm}",
19
+ "cpp/**/*.{hpp,cpp}",
20
+ ]
21
+
22
+ s.dependency 'React-jsi'
23
+ s.dependency 'React-callinvoker'
24
+
25
+ # Allows any 3.x. The public API used here was verified against 3.1.6 and
26
+ # 3.2.1; CarrotquestSDK is a closed-source vendor pod, so pin it yourself in
27
+ # your app's Podfile if you need a specific build.
28
+ s.dependency 'CarrotquestSDK', '~> 3.1'
29
+
30
+ load 'nitrogen/generated/ios/CarrotQuest+autolinking.rb'
31
+ add_nitrogen_files(s)
32
+
33
+ install_modules_dependencies(s)
34
+ end
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hamed
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,457 @@
1
+ # react-native-carrot-quest
2
+
3
+ Unofficial React Native bindings for the [Carrot quest](https://www.carrotquest.io/) mobile SDKs, built with [Nitro Modules](https://nitro.margelo.com/).
4
+
5
+ > **Not affiliated with Carrot quest.** Carrot does not officially support React
6
+ > Native, so this package maintains its own compatibility coverage against the
7
+ > vendor's iOS and Android SDKs. Versions are pinned deliberately — see
8
+ > [SDK versions](#sdk-versions).
9
+
10
+ - Swift and Kotlin implementations, no Objective-C bridging layer
11
+ - Fully typed across the JS/native boundary — the spec is the single source of truth
12
+ - `setup()` resolves only when the SDK reports success, so auth can never race init
13
+ - Native init hooks so the SDK is ready before your JS bundle loads
14
+ - A `verify:native` check that fails if a pinned SDK stops exposing a call the bridge makes
15
+
16
+ ## Requirements
17
+
18
+ | | Minimum |
19
+ |---|---|
20
+ | React Native | 0.75 (New Architecture) |
21
+ | iOS | 13.4 |
22
+ | Android | API 24 |
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install react-native-carrot-quest react-native-nitro-modules
28
+ ```
29
+
30
+ `react-native-nitro-modules` is a required peer dependency.
31
+
32
+ ### iOS
33
+
34
+ ```bash
35
+ cd ios && pod install
36
+ ```
37
+
38
+ ### Android
39
+
40
+ The Carrot Android SDK and two of its transitive dependencies live outside Maven
41
+ Central. Add both repositories to your `android/build.gradle`:
42
+
43
+ ```gradle
44
+ allprojects {
45
+ repositories {
46
+ maven { url "https://raw.github.com/carrotquest/android-sdk/carrotquest" }
47
+ // SDK 3.x pulls com.github.Redman1037:TSnackBar and com.github.chrisbanes:PhotoView
48
+ maven { url "https://jitpack.io" }
49
+ }
50
+ }
51
+ ```
52
+
53
+ ## Usage
54
+
55
+ ### Pick an API style
56
+
57
+ The default export exposes every method on one `CarrotQuest` object:
58
+
59
+ ```ts
60
+ import CarrotQuest from 'react-native-carrot-quest';
61
+
62
+ CarrotQuest.setup('your-api-key', {
63
+ locale: 'ru',
64
+ theme: 'fromDevice',
65
+ }).catch((error) => {
66
+ console.warn('Carrot Quest setup failed', error);
67
+ });
68
+
69
+ CarrotQuest.trackEvent('app_opened');
70
+ ```
71
+
72
+ Named exports remain available and call the same functions:
73
+
74
+ ```ts
75
+ import { setup, trackEvent } from 'react-native-carrot-quest';
76
+ ```
77
+
78
+ Carrot Quest is a process-wide native singleton, so a React Context provider is
79
+ not required for setup, auth, tracking, or opening chat. Add a provider (or your
80
+ existing state store) only when React components need shared **reactive** state,
81
+ such as an unread badge or chat-visibility state.
82
+
83
+ Call `setup()` once. The package owns readiness natively: if another method is
84
+ called while setup is still finishing, it waits for that same setup attempt.
85
+ Your application does not need a provider, a readiness promise, or repeated
86
+ `setup()` calls.
87
+
88
+ ### Option A: initialise from `index.js`
89
+
90
+ Use this when Carrot Quest should start as soon as the JS bundle evaluates:
91
+
92
+ ```js
93
+ import { AppRegistry } from 'react-native';
94
+ import CarrotQuest from 'react-native-carrot-quest';
95
+ import App from './App';
96
+ import { name as appName } from './app.json';
97
+
98
+ // Start setup early, but do not await it before registering the React app.
99
+ CarrotQuest.setup('your-api-key', {
100
+ locale: 'ru',
101
+ theme: 'fromDevice',
102
+ }).catch((error) => {
103
+ console.warn('Carrot Quest setup failed', error);
104
+ });
105
+
106
+ AppRegistry.registerComponent(appName, () => App);
107
+ ```
108
+
109
+ ### Option B: initialise from `App.tsx`
110
+
111
+ Use this when the first screen does not need Carrot Quest and you prefer to let
112
+ React commit its first render before setup begins:
113
+
114
+ ```tsx
115
+ import { useEffect } from 'react';
116
+ import CarrotQuest from 'react-native-carrot-quest';
117
+
118
+ export default function App() {
119
+ useEffect(() => {
120
+ CarrotQuest.setup('your-api-key', {
121
+ locale: 'ru',
122
+ theme: 'fromDevice',
123
+ }).catch((error) => {
124
+ console.warn('Carrot Quest setup failed', error);
125
+ });
126
+ }, []);
127
+
128
+ return <Navigation />;
129
+ }
130
+ ```
131
+
132
+ After this one call, use `CarrotQuest.auth()`, `openChat()`, `trackEvent()` and
133
+ the rest of the API directly wherever they are needed. Do not hide the whole
134
+ app behind the setup promise unless support chat is required for the first
135
+ screen.
136
+
137
+ ### Does setup affect startup time?
138
+
139
+ Potentially, yes—but the API style (default object, named imports, or provider)
140
+ is not what matters. The important choices are **when setup starts** and whether
141
+ your app waits for it before rendering:
142
+
143
+ | Placement | SDK starts | Startup tradeoff |
144
+ |---|---|---|
145
+ | `index.js` | During JS bundle evaluation | Ready earlier; native CPU, disk, and network work may compete with startup. Registering the app without awaiting setup keeps it off the explicit render gate. |
146
+ | `App.tsx` `useEffect` | After the first React commit | Usually the lowest-risk choice for first-render latency; chat becomes ready slightly later. |
147
+
148
+ Importing the package only creates the Nitro hybrid-object binding. The
149
+ meaningful work begins when `setup()` initializes the vendor SDK. Because the
150
+ method returns a promise, calling it does not require blocking React, but gating
151
+ your root component on that promise will extend time-to-content by the setup
152
+ duration.
153
+
154
+ Measure the impact in a **release build** over multiple cold launches. Compare a
155
+ user-visible milestone such as navigation-ready or first content with and
156
+ without early setup; timing only the setup promise measures SDK readiness, not
157
+ its effect on the rest of startup.
158
+
159
+ ### Setup guarantees
160
+
161
+ `setup()` returns a promise so the application can observe success or failure—an
162
+ invalid key or a dead network does not leave a silently half-initialised SDK.
163
+ You do not need to await that promise before calling another package method;
164
+ the native readiness queue preserves the call order.
165
+
166
+ Matching concurrent calls are single-flight. Mutable options contributed by a
167
+ call that joins during initialization are applied before every joined promise
168
+ resolves, including a call arriving while success is being finalized.
169
+
170
+ SDK operations called during initialization wait in the native setup queue.
171
+ Promise-returning operations reject with the setup error if initialization
172
+ fails; fire-and-forget operations are skipped and logged.
173
+
174
+ Calling `setup()` again with a **different** API key, locale, EU-server flag,
175
+ App Group or service mode rejects with a configuration-conflict error rather
176
+ than resolving and quietly keeping the first configuration:
177
+
178
+ ```ts
179
+ await setup(productionKey);
180
+ await setup(stagingKey); // rejects — production is still active
181
+ ```
182
+
183
+ The native SDKs cannot be re-pointed at another account at runtime. Restart the
184
+ process to change it.
185
+
186
+ ### Authenticating
187
+
188
+ Carrot has **two distinct authentication flows** with different security
189
+ properties. They are not interchangeable.
190
+
191
+ ```ts
192
+ import { auth, hashedAuth, logout } from 'react-native-carrot-quest';
193
+
194
+ // Recommended. `hash` is generated per user on your server.
195
+ const carrotId = await hashedAuth(userId, hashFromYourBackend);
196
+
197
+ // Simpler, less secure. `userAuthKey` is the single key from your Carrot
198
+ // settings, shipped inside the app and identical for every user.
199
+ const sameId = await auth(userId, USER_AUTH_KEY);
200
+
201
+ await logout();
202
+ ```
203
+
204
+ | | `hashedAuth(userId, hash)` | `auth(userId, userAuthKey)` |
205
+ |---|---|---|
206
+ | Credential | Per-user hash | One shared key |
207
+ | Lives | On your server | In the app binary |
208
+ | Use when | You have a backend | You have no backend |
209
+ | Security | **Recommended** | Anyone who extracts the key can authenticate as any user id |
210
+
211
+ > Do not pass a per-user backend hash to `auth()` — it takes the static User
212
+ > Auth Key, and authentication will fail.
213
+
214
+ ### Chat, properties, and tracking
215
+
216
+ ```ts
217
+ import {
218
+ closeChat,
219
+ getUnreadConversationsCount,
220
+ onChatVisibilityChanged,
221
+ openChat,
222
+ setProperties,
223
+ setUserProperties,
224
+ trackEvent,
225
+ trackScreen,
226
+ trackUtm,
227
+ } from 'react-native-carrot-quest';
228
+
229
+ await openChat();
230
+ await closeChat(); // iOS only
231
+
232
+ // Listeners return a subscription — remove it on unmount.
233
+ const subscription = onChatVisibilityChanged((visible) => setOpen(visible));
234
+ subscription.remove();
235
+
236
+ const unread = await getUnreadConversationsCount();
237
+
238
+ setUserProperties({ name: 'Ada', email: 'ada@example.com', custom: { plan: 'pro' } });
239
+
240
+ // Merge operations and the e-commerce namespace:
241
+ setProperties([
242
+ { key: 'cartAmount', value: '42', kind: 'ecommerce' },
243
+ { key: 'visits', value: '1', operation: 'add' },
244
+ ]);
245
+
246
+ trackEvent('checkout_completed', { total: 42, currency: 'EUR', firstOrder: true });
247
+ trackScreen('CheckoutScreen');
248
+ trackUtm('https://example.com/?utm_source=newsletter');
249
+ ```
250
+
251
+ ### Push notifications
252
+
253
+ Register the device token, then hand Carrot payloads to the SDK:
254
+
255
+ ```ts
256
+ import {
257
+ handlePushClick,
258
+ handlePushNotification,
259
+ isCarrotPush,
260
+ setPushToken,
261
+ } from 'react-native-carrot-quest';
262
+
263
+ setPushToken(token); // APNs hex string on iOS, FCM token on Android
264
+
265
+ // In your message handler:
266
+ if (isCarrotPush(remoteMessage.data)) {
267
+ handlePushNotification(remoteMessage.data);
268
+ }
269
+
270
+ // iOS, when the user taps a notification:
271
+ handlePushClick(response.notification.request.content.userInfo, /* openLink */ false);
272
+ ```
273
+
274
+ Payload arguments accept either a decoded object or a raw JSON string.
275
+
276
+ **iOS also needs, outside this package:**
277
+
278
+ 1. A Notification Service Extension target using `CarrotNotificationServiceExtension`.
279
+ 2. An App Group shared between the app and that extension, passed as
280
+ `setup(key, { appGroup: 'group.com.example' })`. Without it,
281
+ `wasPushShownEarlier` cannot work and rich pushes will not render.
282
+
283
+ **Android also needs** a `POST_NOTIFICATIONS` permission request on API 33+, and
284
+ optionally `notificationIconResourceName` in the setup options.
285
+
286
+ ### Deep links
287
+
288
+ ```ts
289
+ import { onUrlOpen } from 'react-native-carrot-quest';
290
+
291
+ // iOS only — intercept links instead of letting the SDK open them.
292
+ useEffect(() => {
293
+ const subscription = onUrlOpen('all', (url) =>
294
+ navigation.navigate(routeFor(url))
295
+ );
296
+
297
+ return () => subscription.remove();
298
+ }, [navigation]);
299
+ ```
300
+
301
+ On Android, links route through the activity named by
302
+ `parentActivityClassName`; the SDK exposes no interception hook.
303
+
304
+ > **Keep at least one `onUrlOpen` listener registered while you want links
305
+ > intercepted.** The SDK's url opener is a setter with no detach, so removing
306
+ > every listener leaves it installed with nobody to receive the url — links
307
+ > then go nowhere rather than reverting to the SDK's own handling. Removing
308
+ > subscriptions on unmount is still right; just re-register on mount.
309
+
310
+ ## API
311
+
312
+ | Method | Returns | iOS | Android |
313
+ |---|---|:---:|:---:|
314
+ | `setup(apiKey, options?)` | `Promise<void>` | ✅ | ✅ |
315
+ | `isConfigured()` | `boolean` | ✅ | ✅ |
316
+ | `getSdkVersion()` | `string` | ✅ | ✅ |
317
+ | `auth(userId, userAuthKey)` | `Promise<string \| undefined>` | ✅ | ✅ |
318
+ | `hashedAuth(userId, hash)` | `Promise<string \| undefined>` | ✅ | ✅ |
319
+ | `logout()` | `Promise<void>` | ✅ | ✅ |
320
+ | `openChat()` | `Promise<void>` | ✅ | ✅ |
321
+ | `closeChat()` | `Promise<void>` | ✅ | — |
322
+ | `isChatOpen()` | `boolean` | ✅ | — |
323
+ | `onChatVisibilityChanged(cb)` | `Subscription` | ✅ | close only |
324
+ | `setTheme(theme)` | `void` | ✅ | ✅ |
325
+ | `getUnreadConversationsCount()` | `Promise<number>` | ✅ | ✅ |
326
+ | `getUnreadMessagesCount()` | `Promise<number \| undefined>` | ✅ | — |
327
+ | `onUnreadConversationsChanged(cb)` | `Subscription` | — | ✅ |
328
+ | `setUserProperties(props)` | `void` | ✅ | ✅ |
329
+ | `setProperties(list)` | `void` | ✅ | ✅ |
330
+ | `trackEvent(name, params?)` | `void` | ✅ | ✅ |
331
+ | `trackScreen(name)` | `void` | ✅ | ✅ |
332
+ | `trackUtm(url)` | `void` | ✅ | ✅ |
333
+ | `setPushToken(token)` | `void` | ✅ | ✅ |
334
+ | `deletePushToken()` | `void` | ✅ | — |
335
+ | `pushNotificationsUnsubscribe()` | `Promise<void>` | ✅ | ✅ |
336
+ | `pushCampaignsUnsubscribe()` | `Promise<void>` | ✅ | ✅ |
337
+ | `isCarrotPush(payload)` | `boolean` | ✅ | ✅ |
338
+ | `isAutoMessage(payload)` | `boolean` | best effort | ✅ |
339
+ | `handlePushNotification(payload)` | `void` | ✅ | ✅ |
340
+ | `handlePushClick(payload, openLink?)` | `void` | ✅ | — |
341
+ | `getPushLink(payload)` | `string \| undefined` | best effort | — |
342
+ | `wasPushShownEarlier(payload)` | `Promise<boolean>` | ✅ | — |
343
+ | `onUrlOpen(source, cb)` | `Subscription` | ✅ | — |
344
+ | `openBrowserLink(url)` / `openUniversalLink(url)` | `void` | ✅ | — |
345
+ | `getDiagnostics()` | `string \| undefined` | — | ✅ |
346
+ | `onLog(cb)` | `Subscription` | — | ✅ |
347
+
348
+ Methods that cannot meaningfully fail are synchronous and return `void`. Methods
349
+ that can fail return a `Promise` so you get the error. A `—` means the platform's
350
+ SDK has no equivalent; those calls are inert rather than throwing.
351
+
352
+ ### Forwarding SDK logs
353
+
354
+ Android only. Useful for routing the SDK's own diagnostics into your crash
355
+ reporter:
356
+
357
+ ```ts
358
+ import { onLog } from 'react-native-carrot-quest';
359
+
360
+ const subscription = onLog((entry) => {
361
+ if (entry.level === 'error') {
362
+ Sentry.captureMessage(`[carrot/${entry.category}] ${entry.message}`);
363
+ }
364
+ });
365
+ ```
366
+
367
+ Entries are filtered by `logLevel`, so keep that at `info` or lower in
368
+ production — `verbose` crosses the bridge for every network call. Values are
369
+ redacted unless `logIncludeSensitive` is on.
370
+
371
+ Removing the last `onLog` subscription detaches the native sink, so log traffic
372
+ stops crossing the bridge entirely; the next subscription re-attaches it.
373
+
374
+ ### Not wrapped
375
+
376
+ **The iOS floating chat button** (`showButton(in:)`, `hideButton()`,
377
+ `setButton(icon:)`). `showButton` takes a `UIView`, so exposing it properly means
378
+ shipping a Nitro **view** component rather than a hybrid object. Open an issue if
379
+ you need it.
380
+
381
+ ### `CarrotQuestSetupOptions`
382
+
383
+ | Option | Platform | Description |
384
+ |---|---|---|
385
+ | `locale` | both | Force a UI locale, e.g. `'ru'`. See [Locale forcing](#locale-forcing) |
386
+ | `theme` | both | `light`, `dark`, `fromDevice`, `fromWeb` |
387
+ | `useEuServer` | both | Route to Carrot's EU servers. See the caveat below |
388
+ | `appGroup` | iOS | App Group shared with your Notification Service Extension |
389
+ | `isServiceMode` | iOS | Run without UI, inside an extension process |
390
+ | `parentActivityClassName` | Android | Activity the chat returns to on close |
391
+ | `notificationIconResourceName` | Android | Drawable *name* for push notifications |
392
+ | `logLevel` | Android | `none`, `error`, `warn`, `info`, `debugging`, `verbose` |
393
+ | `logIncludeSensitive` | Android | Allow tokens/user ids into SDK logs |
394
+
395
+ > **`useEuServer` on Android is inferred, not documented.** The SDK is obfuscated;
396
+ > the flag maps to the four-argument `setup(context, key, boolean, callback)`
397
+ > overload, identified from the neighbouring `isUseEuApi()` accessor. Verify your
398
+ > traffic routing before relying on it. Leaving the option unset uses the
399
+ > three-argument overload, so default behaviour is unaffected.
400
+
401
+ ### Locale forcing
402
+
403
+ The Carrot SDKs resolve their language from the system locale and expose no
404
+ public `setLanguage` API. Passing `locale` works around that, but it is a blunt
405
+ instrument:
406
+
407
+ - **iOS** writes `AppleLanguages` / `AppleLocale` in `UserDefaults`
408
+ - **Android** calls `Locale.setDefault`, which is **process-global** and affects
409
+ every locale-sensitive API in your app, not just Carrot
410
+
411
+ Only set it if you need to pin the chat to one language.
412
+
413
+ ## SDK versions
414
+
415
+ | Platform | Pinned |
416
+ |---|---|
417
+ | iOS | `CarrotquestSDK ~> 3.1` (verified against 3.2.1) |
418
+ | Android | `io.carrotquest:android-sdk:3.1.0-commonRelease` |
419
+
420
+ Carrot publishes one Android artifact per region flavour — `commonRelease` or
421
+ `usRelease`. Override from your app's `android/build.gradle`:
422
+
423
+ ```gradle
424
+ ext { carrotSdkVersion = "3.1.0-usRelease" }
425
+ ```
426
+
427
+ **Android 2.x is not supported.** It exposes a callback-free `setup()` that
428
+ reports success before initialisation finishes, a `setDebug()` toggle that 3.x
429
+ replaced with `setLogLevel`, and JSON-string event params that 3.x replaced with
430
+ `EventParams`.
431
+
432
+ ### Guarding against drift
433
+
434
+ `yarn test` fails if a version pin moves without the verified version in
435
+ `scripts/native-api.json` being updated to match. Maintainers can additionally
436
+ check every native symbol against the real SDK artifacts — see
437
+ [CONTRIBUTING.md](CONTRIBUTING.md#verifying-the-native-sdk-surface).
438
+
439
+ ## Platform support
440
+
441
+ iOS and Android only. Importing on web resolves to a stub: predicates answer
442
+ `false`/`undefined` and everything else throws, so you can guard with
443
+ `Platform.OS` without breaking bundling.
444
+
445
+ ## Contributing
446
+
447
+ - [Development workflow](CONTRIBUTING.md#development-workflow)
448
+ - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
449
+ - [Code of conduct](CODE_OF_CONDUCT.md)
450
+
451
+ ## License
452
+
453
+ MIT
454
+
455
+ ---
456
+
457
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -0,0 +1,24 @@
1
+ project(carrotquest)
2
+ cmake_minimum_required(VERSION 3.9.0)
3
+
4
+ set(PACKAGE_NAME carrotquest)
5
+ set(CMAKE_VERBOSE_MAKEFILE ON)
6
+ set(CMAKE_CXX_STANDARD 20)
7
+
8
+ # Define C++ library and add all sources
9
+ add_library(${PACKAGE_NAME} SHARED src/main/cpp/cpp-adapter.cpp)
10
+
11
+ # Add Nitrogen specs :)
12
+ include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/carrotquest+autolinking.cmake)
13
+
14
+ # Set up local includes
15
+ include_directories("src/main/cpp" "../cpp")
16
+
17
+ find_library(LOG_LIB log)
18
+
19
+ # Link all libraries together
20
+ target_link_libraries(
21
+ ${PACKAGE_NAME}
22
+ ${LOG_LIB}
23
+ android # <-- Android core
24
+ )
@@ -0,0 +1,136 @@
1
+ buildscript {
2
+ ext.CarrotQuest = [
3
+ kotlinVersion: "2.0.21",
4
+ minSdkVersion: 24,
5
+ compileSdkVersion: 36,
6
+ // Carrot publishes one artifact per region flavour: `commonRelease` for the
7
+ // default servers, `usRelease` for the US ones. Override from your app's
8
+ // android/build.gradle:
9
+ // ext { carrotSdkVersion = "3.1.0-usRelease" }
10
+ //
11
+ // Must stay on the 3.x line — 2.x has a callback-free `setup`, a `setDebug`
12
+ // toggle and JSON-string event params, none of which this bridge uses.
13
+ carrotSdkVersion: "3.1.0-commonRelease"
14
+ ]
15
+
16
+ ext.getExtOrDefault = { prop ->
17
+ if (rootProject.ext.has(prop)) {
18
+ return rootProject.ext.get(prop)
19
+ }
20
+
21
+ return CarrotQuest[prop]
22
+ }
23
+
24
+ repositories {
25
+ google()
26
+ mavenCentral()
27
+ }
28
+
29
+ dependencies {
30
+ classpath "com.android.tools.build:gradle:8.7.2"
31
+ // noinspection DifferentKotlinGradleVersion
32
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
33
+ }
34
+ }
35
+
36
+ def reactNativeArchitectures() {
37
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
38
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
39
+ }
40
+
41
+ apply plugin: "com.android.library"
42
+ apply plugin: "kotlin-android"
43
+ apply from: '../nitrogen/generated/android/carrotquest+autolinking.gradle'
44
+
45
+ apply plugin: "com.facebook.react"
46
+
47
+ android {
48
+ namespace "com.margelo.nitro.carrotquest"
49
+
50
+ compileSdkVersion getExtOrDefault("compileSdkVersion")
51
+
52
+ defaultConfig {
53
+ minSdkVersion getExtOrDefault("minSdkVersion")
54
+
55
+ externalNativeBuild {
56
+ cmake {
57
+ cppFlags "-frtti -fexceptions -Wall -fstack-protector-all"
58
+ arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
59
+ abiFilters (*reactNativeArchitectures())
60
+
61
+ buildTypes {
62
+ debug {
63
+ cppFlags "-O1 -g"
64
+ }
65
+ release {
66
+ cppFlags "-O2"
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+
73
+ externalNativeBuild {
74
+ cmake {
75
+ path "CMakeLists.txt"
76
+ }
77
+ }
78
+
79
+ packaging {
80
+ resources {
81
+ excludes += [
82
+ "META-INF",
83
+ "META-INF/**"
84
+ ]
85
+ }
86
+
87
+ jniLibs {
88
+ excludes += [
89
+ "**/libc++_shared.so",
90
+ "**/libfbjni.so",
91
+ "**/libjsi.so",
92
+ "**/libfolly_json.so",
93
+ "**/libfolly_runtime.so",
94
+ "**/libglog.so",
95
+ "**/libhermes.so",
96
+ "**/libhermes-executor-debug.so",
97
+ "**/libhermes_executor.so",
98
+ "**/libreactnative.so",
99
+ "**/libreactnativejni.so",
100
+ "**/libturbomodulejsijni.so",
101
+ "**/libreact_nativemodule_core.so",
102
+ "**/libjscexecutor.so"
103
+ ]
104
+ }
105
+ }
106
+
107
+ buildFeatures {
108
+ prefab true
109
+ }
110
+
111
+ compileOptions {
112
+ sourceCompatibility JavaVersion.VERSION_17
113
+ targetCompatibility JavaVersion.VERSION_17
114
+ }
115
+ }
116
+
117
+ repositories {
118
+ google()
119
+ mavenCentral()
120
+ // Carrot quest publishes its Android SDK from a GitHub-hosted Maven repo
121
+ // rather than Maven Central.
122
+ maven { url "https://raw.github.com/carrotquest/android-sdk/carrotquest" }
123
+ // Required for the SDK's own transitive GitHub dependencies — 3.1.0 pulls
124
+ // com.github.Redman1037:TSnackBar and com.github.chrisbanes:PhotoView.
125
+ maven { url "https://jitpack.io" }
126
+ }
127
+
128
+ dependencies {
129
+ implementation "com.facebook.react:react-android"
130
+ implementation project(":react-native-nitro-modules")
131
+
132
+ // `api`, not `implementation`, so consumers can call CarrotQuestSetup from
133
+ // their own Application class. Pinned exactly — the vendor publishes a single
134
+ // artifact per release with no semver guarantees. Bump deliberately.
135
+ api "io.carrotquest:android-sdk:${getExtOrDefault('carrotSdkVersion')}"
136
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,11 @@
1
+ #include <jni.h>
2
+ #include "carrotquestOnLoad.hpp"
3
+
4
+ #include <fbjni/fbjni.h>
5
+
6
+
7
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
8
+ return facebook::jni::initialize(vm, []() {
9
+ margelo::nitro::carrotquest::registerAllNatives();
10
+ });
11
+ }