mixpanel-react-native 3.1.2 → 3.2.0-beta.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 (93) hide show
  1. package/.claude/settings.local.json +12 -1
  2. package/.github/dependabot.yml +7 -0
  3. package/.github/workflows/node.js.yml +24 -1
  4. package/.vscode/settings.json +2 -1
  5. package/MixpanelReactNative.podspec +1 -1
  6. package/Samples/MixpanelExample/ios/MixpanelExample.xcworkspace/contents.xcworkspacedata +10 -0
  7. package/Samples/MixpanelExample/ios/Podfile.lock +1996 -0
  8. package/Samples/MixpanelStarter/.bundle/config +2 -0
  9. package/Samples/MixpanelStarter/.env.example +4 -0
  10. package/Samples/MixpanelStarter/.eslintrc.js +4 -0
  11. package/Samples/MixpanelStarter/.prettierrc.js +5 -0
  12. package/Samples/MixpanelStarter/.watchmanconfig +1 -0
  13. package/Samples/MixpanelStarter/App.tsx +10 -0
  14. package/Samples/MixpanelStarter/CLAUDE.md +538 -0
  15. package/Samples/MixpanelStarter/Gemfile +16 -0
  16. package/Samples/MixpanelStarter/INTEGRATION_GUIDE.md +606 -0
  17. package/Samples/MixpanelStarter/README.md +406 -0
  18. package/Samples/MixpanelStarter/__tests__/MixpanelContext.test.tsx +63 -0
  19. package/Samples/MixpanelStarter/android/app/build.gradle +119 -0
  20. package/Samples/MixpanelStarter/android/app/debug.keystore +0 -0
  21. package/Samples/MixpanelStarter/android/app/proguard-rules.pro +10 -0
  22. package/Samples/MixpanelStarter/android/app/src/main/AndroidManifest.xml +27 -0
  23. package/Samples/MixpanelStarter/android/app/src/main/java/com/mixpanelstarter/MainActivity.kt +22 -0
  24. package/Samples/MixpanelStarter/android/app/src/main/java/com/mixpanelstarter/MainApplication.kt +27 -0
  25. package/Samples/MixpanelStarter/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  26. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  27. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  28. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  29. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  30. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  31. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  32. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  33. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  34. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  35. package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  36. package/Samples/MixpanelStarter/android/app/src/main/res/values/strings.xml +3 -0
  37. package/Samples/MixpanelStarter/android/app/src/main/res/values/styles.xml +9 -0
  38. package/Samples/MixpanelStarter/android/build.gradle +21 -0
  39. package/Samples/MixpanelStarter/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  40. package/Samples/MixpanelStarter/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  41. package/Samples/MixpanelStarter/android/gradle.properties +44 -0
  42. package/Samples/MixpanelStarter/android/gradlew +251 -0
  43. package/Samples/MixpanelStarter/android/gradlew.bat +99 -0
  44. package/Samples/MixpanelStarter/android/settings.gradle +6 -0
  45. package/Samples/MixpanelStarter/app.json +4 -0
  46. package/Samples/MixpanelStarter/babel.config.js +14 -0
  47. package/Samples/MixpanelStarter/index.js +9 -0
  48. package/Samples/MixpanelStarter/ios/.xcode.env +11 -0
  49. package/Samples/MixpanelStarter/ios/MixpanelStarter/AppDelegate.swift +48 -0
  50. package/Samples/MixpanelStarter/ios/MixpanelStarter/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  51. package/Samples/MixpanelStarter/ios/MixpanelStarter/Images.xcassets/Contents.json +6 -0
  52. package/Samples/MixpanelStarter/ios/MixpanelStarter/Info.plist +55 -0
  53. package/Samples/MixpanelStarter/ios/MixpanelStarter/LaunchScreen.storyboard +47 -0
  54. package/Samples/MixpanelStarter/ios/MixpanelStarter/PrivacyInfo.xcprivacy +38 -0
  55. package/Samples/MixpanelStarter/ios/MixpanelStarter.xcodeproj/project.pbxproj +482 -0
  56. package/Samples/MixpanelStarter/ios/MixpanelStarter.xcodeproj/xcshareddata/xcschemes/MixpanelStarter.xcscheme +88 -0
  57. package/Samples/MixpanelStarter/ios/MixpanelStarter.xcworkspace/contents.xcworkspacedata +10 -0
  58. package/Samples/MixpanelStarter/ios/Podfile +34 -0
  59. package/Samples/MixpanelStarter/ios/Podfile.lock +2839 -0
  60. package/Samples/MixpanelStarter/jest.config.js +3 -0
  61. package/Samples/MixpanelStarter/metro.config.js +42 -0
  62. package/Samples/MixpanelStarter/package-lock.json +12141 -0
  63. package/Samples/MixpanelStarter/package.json +51 -0
  64. package/Samples/MixpanelStarter/src/@types/env.d.ts +3 -0
  65. package/Samples/MixpanelStarter/src/App.tsx +83 -0
  66. package/Samples/MixpanelStarter/src/components/ActionButton.tsx +92 -0
  67. package/Samples/MixpanelStarter/src/components/ErrorBoundary.tsx +81 -0
  68. package/Samples/MixpanelStarter/src/components/EventTrackingLog.tsx +163 -0
  69. package/Samples/MixpanelStarter/src/components/FlagCard.tsx +199 -0
  70. package/Samples/MixpanelStarter/src/components/InfoCard.tsx +77 -0
  71. package/Samples/MixpanelStarter/src/components/TestResultDisplay.tsx +181 -0
  72. package/Samples/MixpanelStarter/src/constants/tracking.ts +77 -0
  73. package/Samples/MixpanelStarter/src/contexts/MixpanelContext.tsx +159 -0
  74. package/Samples/MixpanelStarter/src/screens/FeatureFlagsScreen.tsx +1011 -0
  75. package/Samples/MixpanelStarter/src/screens/HomeScreen.tsx +307 -0
  76. package/Samples/MixpanelStarter/src/screens/OnboardingScreen.tsx +253 -0
  77. package/Samples/MixpanelStarter/src/screens/SettingsScreen.tsx +316 -0
  78. package/Samples/MixpanelStarter/src/types/flags.types.ts +42 -0
  79. package/Samples/MixpanelStarter/src/types/mixpanel.types.ts +26 -0
  80. package/Samples/MixpanelStarter/tsconfig.json +13 -0
  81. package/__tests__/flags.test.js +730 -0
  82. package/__tests__/index.test.js +7 -3
  83. package/__tests__/jest_setup.js +18 -0
  84. package/android/build.gradle +1 -1
  85. package/android/src/main/java/com/mixpanel/reactnative/MixpanelReactNativeModule.java +272 -2
  86. package/index.d.ts +64 -1
  87. package/index.js +42 -3
  88. package/ios/MixpanelReactNative.m +19 -1
  89. package/ios/MixpanelReactNative.swift +183 -5
  90. package/javascript/mixpanel-flags-js.js +463 -0
  91. package/javascript/mixpanel-flags.js +290 -0
  92. package/javascript/mixpanel-main.js +13 -1
  93. package/package.json +2 -2
@@ -0,0 +1,406 @@
1
+ # MixpanelStarter
2
+
3
+ A modern, production-ready React Native sample application demonstrating [Mixpanel React Native SDK](https://github.com/mixpanel/mixpanel-react-native) integration with TypeScript, Context API, and best practices.
4
+
5
+ ## 🎯 Purpose
6
+
7
+ This sample app bridges the gap between basic "Hello World" examples and complex production implementations. It demonstrates **80% of common Mixpanel use cases** with clean, maintainable patterns you can copy directly into your app.
8
+
9
+ ### What Makes This Different
10
+
11
+ - **Modern Architecture**: Context API + TypeScript strict mode
12
+ - **Production Patterns**: Error handling, loading states, proper lifecycle management
13
+ - **Educational**: Each screen explains what's happening and why
14
+ - **Copy-Paste Ready**: Well-commented code you can use immediately
15
+
16
+ ## ✨ Features Demonstrated
17
+
18
+ This app showcases 9 core Mixpanel SDK capabilities:
19
+
20
+ | Feature | Description | Screen |
21
+ |---------|-------------|--------|
22
+ | 🆔 **User Identification** | `identify()`, `alias()`, anonymous-to-identified flow | Onboarding |
23
+ | 👤 **User Profiles** | `getPeople().set()`, `setOnce()` for profile properties | Onboarding |
24
+ | 📊 **Event Tracking** | `track()` with custom properties and metadata | Home |
25
+ | ⏱️ **Timed Events** | `timeEvent()` for automatic duration tracking | Home |
26
+ | 🌐 **Super Properties** | `registerSuperProperties()` for global context | Home |
27
+ | 🚩 **Feature Flags** | `flags.loadFlags()`, `isEnabled()`, dynamic feature control - **FULL INTEGRATION TEST SUITE** | Feature Flags |
28
+ | 🔒 **Privacy Controls** | `optIn/OutTracking()` for GDPR compliance | Settings |
29
+ | 🗑️ **Data Management** | `reset()` for logout/data deletion | Settings |
30
+ | 🚀 **Manual Flush** | `flush()` to force send queued events | Settings |
31
+
32
+ ## 📱 App Structure
33
+
34
+ The app has 4 tabs:
35
+
36
+ 1. **Onboarding (User ID Tab)**: Demonstrates user identification lifecycle
37
+ 2. **Home (Events Tab)**: Shows event tracking and super properties
38
+ 3. **Feature Flags**: **Comprehensive integration test suite** - exercises all 8 public API methods, Promise/Callback patterns, edge cases, and type coercion
39
+ 4. **Settings**: Privacy controls and data management
40
+
41
+ ## 🚀 Quick Start
42
+
43
+ ### Prerequisites
44
+
45
+ - Node.js >= 20
46
+ - React Native development environment set up ([guide](https://reactnative.dev/docs/environment-setup))
47
+ - Mixpanel project token ([get one here](https://mixpanel.com/register))
48
+
49
+ ### Installation
50
+
51
+ ```bash
52
+ # 1. Navigate to this directory
53
+ cd Samples/MixpanelStarter
54
+
55
+ # 2. Install dependencies
56
+ npm install
57
+
58
+ # 3. Install iOS dependencies (macOS only)
59
+ cd ios && pod install && cd ..
60
+
61
+ # 4. Configure your Mixpanel token
62
+ cp .env.example .env
63
+ # Edit .env and add your token: MIXPANEL_TOKEN=your_token_here
64
+ ```
65
+
66
+ ### Running the App
67
+
68
+ ```bash
69
+ # iOS
70
+ npm run ios
71
+
72
+ # Android
73
+ npm run android
74
+ ```
75
+
76
+ **Note**: If you don't set a token in `.env`, the app will use a placeholder token. You'll see events in the console but they won't reach Mixpanel.
77
+
78
+ ## 🏗️ Project Structure
79
+
80
+ ```
81
+ MixpanelStarter/
82
+ ├── src/
83
+ │ ├── contexts/
84
+ │ │ └── MixpanelContext.tsx # Context Provider + useMixpanel hook
85
+ │ ├── screens/
86
+ │ │ ├── OnboardingScreen.tsx # User identification demos
87
+ │ │ ├── HomeScreen.tsx # Event tracking patterns
88
+ │ │ ├── FeatureFlagsScreen.tsx # Feature flags integration tests
89
+ │ │ └── SettingsScreen.tsx # Privacy & data management
90
+ │ ├── components/
91
+ │ │ ├── ActionButton.tsx # Reusable button component
92
+ │ │ ├── InfoCard.tsx # Info display card
93
+ │ │ ├── FlagCard.tsx # Flag display component
94
+ │ │ ├── TestResultDisplay.tsx # Test results visualization
95
+ │ │ ├── EventTrackingLog.tsx # Event history component
96
+ │ │ └── ErrorBoundary.tsx # Error handling wrapper
97
+ │ ├── types/
98
+ │ │ ├── mixpanel.types.ts # TypeScript definitions
99
+ │ │ └── flags.types.ts # Feature flags test types
100
+ │ ├── constants/
101
+ │ │ └── tracking.ts # Event names & properties
102
+ │ └── App.tsx # Navigation setup
103
+ ├── __tests__/
104
+ │ └── MixpanelContext.test.tsx # Basic context tests
105
+ └── .env.example # Environment template
106
+ ```
107
+
108
+ ## 📖 Key Patterns (Copy These!)
109
+
110
+ ### 1. Context Setup
111
+
112
+ ```typescript
113
+ // Wrap your app root with MixpanelProvider
114
+ <MixpanelProvider token="YOUR_TOKEN" trackAutomaticEvents={true}>
115
+ <App />
116
+ </MixpanelProvider>
117
+
118
+ // Use in any component
119
+ const { mixpanel, isInitialized, track, identify } = useMixpanel();
120
+ ```
121
+
122
+ **Why?** Provides app-wide access without prop drilling. Handles initialization and loading states automatically.
123
+
124
+ ### 2. Event Tracking with Constants
125
+
126
+ ```typescript
127
+ // Define event names as constants
128
+ const Events = {
129
+ PRODUCT_VIEWED: 'Product Viewed',
130
+ VIDEO_COMPLETED: 'Video Completed',
131
+ };
132
+
133
+ // Track events
134
+ track(Events.PRODUCT_VIEWED, {
135
+ product_id: 'prod-123',
136
+ product_name: 'Sample Product',
137
+ timestamp: new Date().toISOString(),
138
+ });
139
+ ```
140
+
141
+ **Why?** Constants prevent typos, enable autocomplete, and make refactoring easier.
142
+
143
+ ### 3. User Identification Flow
144
+
145
+ ```typescript
146
+ // Get the current anonymous ID
147
+ const previousId = await mixpanel.getDistinctId();
148
+
149
+ // Identify the user
150
+ identify(userId);
151
+
152
+ // Link previous anonymous events
153
+ alias(userId, previousId);
154
+
155
+ // Set profile properties
156
+ mixpanel.getPeople().set({
157
+ $email: userId,
158
+ signup_date: new Date().toISOString(),
159
+ });
160
+ ```
161
+
162
+ **Why?** Preserves event history when users transition from anonymous to identified.
163
+
164
+ ### 4. Super Properties for Global Context
165
+
166
+ ```typescript
167
+ // Set once for all events
168
+ mixpanel.registerSuperProperties({
169
+ 'App Version': '1.0.0',
170
+ 'Dark Mode': darkModeEnabled,
171
+ 'Platform': Platform.OS,
172
+ });
173
+
174
+ // These are automatically included in every event
175
+ ```
176
+
177
+ **Why?** Eliminates repetitive property passing. Perfect for user preferences and app state.
178
+
179
+ ### 5. Feature Flags for Dynamic Control
180
+
181
+ ```typescript
182
+ // Enable feature flags during initialization
183
+ await mixpanel.init(false, {}, undefined, false, {
184
+ enabled: true,
185
+ });
186
+
187
+ // Load flags from Mixpanel
188
+ await mixpanel.flags.loadFlags();
189
+
190
+ // Check if flags are ready
191
+ const ready = mixpanel.flags.areFlagsReady();
192
+
193
+ // Synchronous flag evaluation (fast, uses cached values)
194
+ const isEnabled = mixpanel.flags.isEnabledSync('feature-key', false);
195
+ const value = mixpanel.flags.getVariantValueSync('feature-key', 'default');
196
+
197
+ // Asynchronous flag evaluation (ensures latest values)
198
+ const enabled = await mixpanel.flags.isEnabled('feature-key', false);
199
+ const variant = await mixpanel.flags.getVariant('feature-key', {
200
+ key: 'feature-key',
201
+ value: null,
202
+ });
203
+
204
+ ```
205
+
206
+ **Why?** Enables remote feature control, A/B testing, and gradual rollouts without app updates. Perfect for experimentation and user segmentation.
207
+
208
+ #### Feature Flags Testing Screen
209
+
210
+ The Feature Flags screen is a **full integration test suite** with:
211
+
212
+ - **4 Test Modes**: Sync, Async (Promise), Edge Cases, Type Coercion
213
+ - **12 Pre-configured Flags**: Boolean gates, string experiments, dynamic configs
214
+ - **Real-time Results**: Execution time, type detection, fallback tracking
215
+ - **Event Monitoring**: Live `$experiment_started` event tracking
216
+ - **All API Methods**: getVariantSync, getVariantValueSync, isEnabledSync, getVariant, getVariantValue, isEnabled (both Promise and Callback patterns)
217
+
218
+ Use this screen during development to verify Feature Flags functionality!
219
+
220
+ ### 6. GDPR-Compliant Logout
221
+
222
+ ```typescript
223
+ // Before logout
224
+ track('User Logged Out');
225
+ await flush(); // Send pending events
226
+ reset(); // Clear all data
227
+ ```
228
+
229
+ **Why?** Ensures data is sent before clearing, and respects user privacy.
230
+
231
+ ## 🎓 Learning Resources
232
+
233
+ ### In-App Learning
234
+
235
+ Each screen includes a "What's Happening?" card that explains:
236
+ - Which SDK methods are being used
237
+ - Why you'd use this pattern
238
+ - What data is being sent to Mixpanel
239
+
240
+ ### Code Comments
241
+
242
+ Every key function includes comments explaining:
243
+ - **What** it does
244
+ - **Why** you'd use this pattern
245
+ - **When** to apply it in your app
246
+
247
+ ## 🧪 Testing
248
+
249
+ ```bash
250
+ # Run tests
251
+ npm test
252
+
253
+ # Run with coverage
254
+ npm test -- --coverage
255
+ ```
256
+
257
+ ## 🔧 Configuration
258
+
259
+ ### Environment Variables
260
+
261
+ Create a `.env` file (see `.env.example`):
262
+
263
+ ```bash
264
+ MIXPANEL_TOKEN=your_project_token_here
265
+ ```
266
+
267
+ ### Mixpanel Options
268
+
269
+ In `src/App.tsx`, you can configure:
270
+
271
+ ```typescript
272
+ <MixpanelProvider
273
+ token={token}
274
+ trackAutomaticEvents={true} // Track app lifecycle events
275
+ useNative={true} // Use native iOS/Android SDKs
276
+ >
277
+ ```
278
+
279
+ ## 📚 Next Steps
280
+
281
+ ### For Learning
282
+
283
+ 1. Open the app and explore each screen
284
+ 2. Read the "What's Happening?" cards to understand each pattern
285
+ 3. Check Mixpanel dashboard to see events arrive
286
+ 4. Modify the code and observe changes
287
+
288
+ ### For Integration
289
+
290
+ 1. Copy the `contexts/MixpanelContext.tsx` pattern into your app
291
+ 2. Define your event names in `constants/tracking.ts`
292
+ 3. Use `useMixpanel()` hook in your components
293
+ 4. See [INTEGRATION_GUIDE.md](./INTEGRATION_GUIDE.md) for step-by-step instructions
294
+
295
+ ## 🤔 Common Questions
296
+
297
+ ### Q: Which events should I track?
298
+
299
+ **A:** Focus on user actions that indicate value or progress:
300
+ - Key conversions (signup, purchase, subscription)
301
+ - Feature usage (search, filter, share)
302
+ - Content engagement (view, complete, like)
303
+
304
+ Avoid tracking:
305
+ - Every button click (too noisy)
306
+ - Internal state changes (not user-facing)
307
+ - PII without consent (privacy violation)
308
+
309
+ ### Q: When should I use super properties vs. event properties?
310
+
311
+ **A:**
312
+ - **Super Properties**: User preferences, app state, device info (changes infrequently, applies to all events)
313
+ - **Event Properties**: Action-specific data (product ID, video title, search query)
314
+
315
+ ### Q: How do I test without sending real events?
316
+
317
+ **A:**
318
+ 1. Use a test project token from Mixpanel
319
+ 2. Enable logging: `mixpanel.setLoggingEnabled(true)`
320
+ 3. Check console for event details
321
+ 4. Use opt-out during development: `mixpanel.optOutTracking()`
322
+
323
+ ### Q: What's the difference between native and JavaScript mode?
324
+
325
+ **A:**
326
+ - **Native (default)**: Uses iOS Swift SDK and Android Java SDK. Better performance, automatic properties.
327
+ - **JavaScript**: Pure JS implementation. Required for Expo and web. More portable but fewer automatic properties.
328
+
329
+ Set in `MixpanelProvider`: `useNative={true}` or `useNative={false}`
330
+
331
+ ## 🐛 Troubleshooting
332
+
333
+ ### Events not appearing in Mixpanel?
334
+
335
+ 1. Check token is correct: `getDistinctId()` should return a value
336
+ 2. Verify network connectivity
337
+ 3. Check opt-out status: `hasOptedOutTracking()`
338
+ 4. Manually flush: `flush()` to send immediately
339
+ 5. Enable logging: `setLoggingEnabled(true)`
340
+
341
+ ### TypeScript errors?
342
+
343
+ ```bash
344
+ # Clear caches
345
+ npm start -- --reset-cache
346
+
347
+ # Rebuild
348
+ cd ios && pod install && cd ..
349
+ npm run ios
350
+ ```
351
+
352
+ ### Build errors?
353
+
354
+ ```bash
355
+ # Clean iOS build
356
+ cd ios && xcodebuild clean && cd ..
357
+
358
+ # Clean Android build
359
+ cd android && ./gradlew clean && cd ..
360
+ ```
361
+
362
+ ## 📝 Design Decisions
363
+
364
+ ### Why Context API over Redux/MobX?
365
+
366
+ - **Simplicity**: No boilerplate, minimal concepts
367
+ - **Standards**: Built into React, widely understood
368
+ - **Sufficient**: Mixpanel state is simple (one instance, few methods)
369
+
370
+ ### Why TypeScript strict mode?
371
+
372
+ - **Safety**: Catches errors at compile time
373
+ - **Documentation**: Types serve as inline docs
374
+ - **Refactoring**: IDE support for safe code changes
375
+
376
+ ### Why Bottom Tabs over Stack Navigation?
377
+
378
+ - **Clarity**: Each tab demonstrates a distinct concept
379
+ - **Simplicity**: Fewer navigation concepts to learn
380
+ - **Focus**: User isn't distracted by complex flows
381
+
382
+ ## 🤝 Contributing
383
+
384
+ Found an issue or want to improve this sample?
385
+
386
+ 1. This is a reference implementation, so changes should be broadly applicable
387
+ 2. Keep patterns simple and well-commented
388
+ 3. Ensure TypeScript strict mode passes
389
+ 4. Test on both iOS and Android
390
+
391
+ ## 📄 License
392
+
393
+ This sample app follows the same license as the [Mixpanel React Native SDK](https://github.com/mixpanel/mixpanel-react-native).
394
+
395
+ ## 🔗 Resources
396
+
397
+ - [Mixpanel React Native SDK Docs](https://docs.mixpanel.com/docs/tracking/advanced/react-native)
398
+ - [Mixpanel Best Practices](https://docs.mixpanel.com/docs/tracking/how-tos/events-and-properties)
399
+ - [React Native Docs](https://reactnative.dev/)
400
+ - [React Navigation Docs](https://reactnavigation.org/)
401
+
402
+ ---
403
+
404
+ **Made with ❤️ to help you integrate Mixpanel quickly and correctly.**
405
+
406
+ Questions? Check out the [Integration Guide](./INTEGRATION_GUIDE.md) or [Mixpanel Community](https://community.mixpanel.com/).
@@ -0,0 +1,63 @@
1
+ import React from 'react';
2
+ import {render, waitFor} from '@testing-library/react-native';
3
+ import {Text} from 'react-native';
4
+ import {MixpanelProvider, useMixpanel} from '../src/contexts/MixpanelContext';
5
+
6
+ // Mock the Mixpanel SDK
7
+ jest.mock('mixpanel-react-native', () => ({
8
+ Mixpanel: jest.fn().mockImplementation(() => ({
9
+ init: jest.fn().mockResolvedValue(undefined),
10
+ registerSuperProperties: jest.fn(),
11
+ setLoggingEnabled: jest.fn(),
12
+ track: jest.fn(),
13
+ identify: jest.fn(),
14
+ alias: jest.fn(),
15
+ reset: jest.fn(),
16
+ flush: jest.fn().mockResolvedValue(undefined),
17
+ })),
18
+ }));
19
+
20
+ // Test component that uses the hook
21
+ const TestComponent = () => {
22
+ const {isInitialized, isLoading, error} = useMixpanel();
23
+
24
+ return (
25
+ <>
26
+ <Text testID="initialized">{isInitialized ? 'true' : 'false'}</Text>
27
+ <Text testID="loading">{isLoading ? 'true' : 'false'}</Text>
28
+ <Text testID="error">{error ? error.message : 'none'}</Text>
29
+ </>
30
+ );
31
+ };
32
+
33
+ describe('MixpanelContext', () => {
34
+ it('should provide Mixpanel context to children', async () => {
35
+ const {getByTestId} = render(
36
+ <MixpanelProvider token="test-token">
37
+ <TestComponent />
38
+ </MixpanelProvider>,
39
+ );
40
+
41
+ // Initially loading
42
+ expect(getByTestId('loading').props.children).toBe('true');
43
+ expect(getByTestId('initialized').props.children).toBe('false');
44
+
45
+ // Wait for initialization
46
+ await waitFor(() => {
47
+ expect(getByTestId('initialized').props.children).toBe('true');
48
+ expect(getByTestId('loading').props.children).toBe('false');
49
+ expect(getByTestId('error').props.children).toBe('none');
50
+ });
51
+ });
52
+
53
+ it('should throw error when used outside provider', () => {
54
+ // Suppress console.error for this test
55
+ const consoleError = jest.spyOn(console, 'error').mockImplementation();
56
+
57
+ expect(() => {
58
+ render(<TestComponent />);
59
+ }).toThrow('useMixpanel must be used within a MixpanelProvider');
60
+
61
+ consoleError.mockRestore();
62
+ });
63
+ });
@@ -0,0 +1,119 @@
1
+ apply plugin: "com.android.application"
2
+ apply plugin: "org.jetbrains.kotlin.android"
3
+ apply plugin: "com.facebook.react"
4
+
5
+ /**
6
+ * This is the configuration block to customize your React Native Android app.
7
+ * By default you don't need to apply any configuration, just uncomment the lines you need.
8
+ */
9
+ react {
10
+ /* Folders */
11
+ // The root of your project, i.e. where "package.json" lives. Default is '../..'
12
+ // root = file("../../")
13
+ // The folder where the react-native NPM package is. Default is ../../node_modules/react-native
14
+ // reactNativeDir = file("../../node_modules/react-native")
15
+ // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
16
+ // codegenDir = file("../../node_modules/@react-native/codegen")
17
+ // The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
18
+ // cliFile = file("../../node_modules/react-native/cli.js")
19
+
20
+ /* Variants */
21
+ // The list of variants to that are debuggable. For those we're going to
22
+ // skip the bundling of the JS bundle and the assets. By default is just 'debug'.
23
+ // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
24
+ // debuggableVariants = ["liteDebug", "prodDebug"]
25
+
26
+ /* Bundling */
27
+ // A list containing the node command and its flags. Default is just 'node'.
28
+ // nodeExecutableAndArgs = ["node"]
29
+ //
30
+ // The command to run when bundling. By default is 'bundle'
31
+ // bundleCommand = "ram-bundle"
32
+ //
33
+ // The path to the CLI configuration file. Default is empty.
34
+ // bundleConfig = file(../rn-cli.config.js)
35
+ //
36
+ // The name of the generated asset file containing your JS bundle
37
+ // bundleAssetName = "MyApplication.android.bundle"
38
+ //
39
+ // The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
40
+ // entryFile = file("../js/MyApplication.android.js")
41
+ //
42
+ // A list of extra flags to pass to the 'bundle' commands.
43
+ // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
44
+ // extraPackagerArgs = []
45
+
46
+ /* Hermes Commands */
47
+ // The hermes compiler command to run. By default it is 'hermesc'
48
+ // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
49
+ //
50
+ // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
51
+ // hermesFlags = ["-O", "-output-source-map"]
52
+
53
+ /* Autolinking */
54
+ autolinkLibrariesWithApp()
55
+ }
56
+
57
+ /**
58
+ * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
59
+ */
60
+ def enableProguardInReleaseBuilds = false
61
+
62
+ /**
63
+ * The preferred build flavor of JavaScriptCore (JSC)
64
+ *
65
+ * For example, to use the international variant, you can use:
66
+ * `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+`
67
+ *
68
+ * The international variant includes ICU i18n library and necessary data
69
+ * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
70
+ * give correct results when using with locales other than en-US. Note that
71
+ * this variant is about 6MiB larger per architecture than default.
72
+ */
73
+ def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
74
+
75
+ android {
76
+ ndkVersion rootProject.ext.ndkVersion
77
+ buildToolsVersion rootProject.ext.buildToolsVersion
78
+ compileSdk rootProject.ext.compileSdkVersion
79
+
80
+ namespace "com.mixpanelstarter"
81
+ defaultConfig {
82
+ applicationId "com.mixpanelstarter"
83
+ minSdkVersion rootProject.ext.minSdkVersion
84
+ targetSdkVersion rootProject.ext.targetSdkVersion
85
+ versionCode 1
86
+ versionName "1.0"
87
+ }
88
+ signingConfigs {
89
+ debug {
90
+ storeFile file('debug.keystore')
91
+ storePassword 'android'
92
+ keyAlias 'androiddebugkey'
93
+ keyPassword 'android'
94
+ }
95
+ }
96
+ buildTypes {
97
+ debug {
98
+ signingConfig signingConfigs.debug
99
+ }
100
+ release {
101
+ // Caution! In production, you need to generate your own keystore file.
102
+ // see https://reactnative.dev/docs/signed-apk-android.
103
+ signingConfig signingConfigs.debug
104
+ minifyEnabled enableProguardInReleaseBuilds
105
+ proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
106
+ }
107
+ }
108
+ }
109
+
110
+ dependencies {
111
+ // The version of react-native is set by the React Native Gradle Plugin
112
+ implementation("com.facebook.react:react-android")
113
+
114
+ if (hermesEnabled.toBoolean()) {
115
+ implementation("com.facebook.react:hermes-android")
116
+ } else {
117
+ implementation jscFlavor
118
+ }
119
+ }
@@ -0,0 +1,10 @@
1
+ # Add project specific ProGuard rules here.
2
+ # By default, the flags in this file are appended to flags specified
3
+ # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4
+ # You can edit the include path and order by changing the proguardFiles
5
+ # directive in build.gradle.
6
+ #
7
+ # For more details, see
8
+ # http://developer.android.com/guide/developing/tools/proguard.html
9
+
10
+ # Add any project specific keep options here:
@@ -0,0 +1,27 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+
3
+ <uses-permission android:name="android.permission.INTERNET" />
4
+
5
+ <application
6
+ android:name=".MainApplication"
7
+ android:label="@string/app_name"
8
+ android:icon="@mipmap/ic_launcher"
9
+ android:roundIcon="@mipmap/ic_launcher_round"
10
+ android:allowBackup="false"
11
+ android:theme="@style/AppTheme"
12
+ android:usesCleartextTraffic="${usesCleartextTraffic}"
13
+ android:supportsRtl="true">
14
+ <activity
15
+ android:name=".MainActivity"
16
+ android:label="@string/app_name"
17
+ android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
18
+ android:launchMode="singleTask"
19
+ android:windowSoftInputMode="adjustResize"
20
+ android:exported="true">
21
+ <intent-filter>
22
+ <action android:name="android.intent.action.MAIN" />
23
+ <category android:name="android.intent.category.LAUNCHER" />
24
+ </intent-filter>
25
+ </activity>
26
+ </application>
27
+ </manifest>
@@ -0,0 +1,22 @@
1
+ package com.mixpanelstarter
2
+
3
+ import com.facebook.react.ReactActivity
4
+ import com.facebook.react.ReactActivityDelegate
5
+ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
6
+ import com.facebook.react.defaults.DefaultReactActivityDelegate
7
+
8
+ class MainActivity : ReactActivity() {
9
+
10
+ /**
11
+ * Returns the name of the main component registered from JavaScript. This is used to schedule
12
+ * rendering of the component.
13
+ */
14
+ override fun getMainComponentName(): String = "MixpanelStarter"
15
+
16
+ /**
17
+ * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
18
+ * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
19
+ */
20
+ override fun createReactActivityDelegate(): ReactActivityDelegate =
21
+ DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
22
+ }
@@ -0,0 +1,27 @@
1
+ package com.mixpanelstarter
2
+
3
+ import android.app.Application
4
+ import com.facebook.react.PackageList
5
+ import com.facebook.react.ReactApplication
6
+ import com.facebook.react.ReactHost
7
+ import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
8
+ import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
9
+
10
+ class MainApplication : Application(), ReactApplication {
11
+
12
+ override val reactHost: ReactHost by lazy {
13
+ getDefaultReactHost(
14
+ context = applicationContext,
15
+ packageList =
16
+ PackageList(this).packages.apply {
17
+ // Packages that cannot be autolinked yet can be added manually here, for example:
18
+ // add(MyReactNativePackage())
19
+ },
20
+ )
21
+ }
22
+
23
+ override fun onCreate() {
24
+ super.onCreate()
25
+ loadReactNative(this)
26
+ }
27
+ }