noboarding 0.1.0-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +515 -0
- package/REVENUECAT_SETUP.md +756 -0
- package/SETUP_GUIDE.md +873 -0
- package/cusomte_screens.md +1964 -0
- package/lib/OnboardingFlow.d.ts +3 -0
- package/lib/OnboardingFlow.js +235 -0
- package/lib/analytics.d.ts +25 -0
- package/lib/analytics.js +72 -0
- package/lib/api.d.ts +31 -0
- package/lib/api.js +149 -0
- package/lib/components/ElementRenderer.d.ts +13 -0
- package/lib/components/ElementRenderer.js +521 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +18 -0
- package/lib/types.d.ts +185 -0
- package/lib/types.js +2 -0
- package/lib/variableUtils.d.ts +17 -0
- package/lib/variableUtils.js +118 -0
- package/logic.md +2095 -0
- package/package.json +44 -0
- package/src/OnboardingFlow.tsx +276 -0
- package/src/analytics.ts +84 -0
- package/src/api.ts +173 -0
- package/src/components/ElementRenderer.tsx +627 -0
- package/src/index.ts +32 -0
- package/src/types.ts +242 -0
- package/src/variableUtils.ts +133 -0
- package/tsconfig.json +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
# Noboarding - React Native SDK
|
|
2
|
+
|
|
3
|
+
React Native SDK for rendering server-driven onboarding flows. Integrate once, then update your onboarding screens remotely from the dashboard — no App Store reviews needed.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install noboarding
|
|
9
|
+
# or
|
|
10
|
+
yarn add noboarding
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**📚 Complete Setup Guides:**
|
|
14
|
+
- **[AI Setup](./SETUP_GUIDE.md#ai-setup)** - Copy/paste instructions for your AI coding assistant (Claude Code, Cursor, etc.)
|
|
15
|
+
- **[Manual Setup](./SETUP_GUIDE.md#normal-setup)** - Step-by-step instructions
|
|
16
|
+
- **[RevenueCat Integration](./REVENUECAT_SETUP.md)** - Detailed RevenueCat paywall guide
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { OnboardingFlow } from 'noboarding';
|
|
22
|
+
|
|
23
|
+
function App() {
|
|
24
|
+
const [showOnboarding, setShowOnboarding] = useState(true);
|
|
25
|
+
|
|
26
|
+
if (showOnboarding) {
|
|
27
|
+
return (
|
|
28
|
+
<OnboardingFlow
|
|
29
|
+
apiKey="sk_live_your_api_key_here"
|
|
30
|
+
onComplete={(userData) => {
|
|
31
|
+
console.log('Collected data:', userData);
|
|
32
|
+
setShowOnboarding(false);
|
|
33
|
+
}}
|
|
34
|
+
onSkip={() => {
|
|
35
|
+
setShowOnboarding(false);
|
|
36
|
+
}}
|
|
37
|
+
// Optional: Get the generated user ID to sync with other services
|
|
38
|
+
onUserIdGenerated={(userId) => {
|
|
39
|
+
console.log('User ID:', userId);
|
|
40
|
+
// Use this to sync with RevenueCat, analytics, etc.
|
|
41
|
+
}}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return <YourMainApp />;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## How It Works
|
|
51
|
+
|
|
52
|
+
1. The SDK fetches your onboarding configuration from Supabase at runtime
|
|
53
|
+
2. Screens defined as JSON element trees are rendered natively using `ElementRenderer`
|
|
54
|
+
3. You update screens in the dashboard, publish, and the SDK picks up changes automatically
|
|
55
|
+
4. No app binary changes required — everything is data-driven
|
|
56
|
+
|
|
57
|
+
## Screen Types
|
|
58
|
+
|
|
59
|
+
### Custom Screen (AI-generated)
|
|
60
|
+
|
|
61
|
+
Screens built with the composable primitive system. The `ElementRenderer` recursively maps a JSON element tree to native React Native components (`View`, `Text`, `Image`, `ScrollView`, `TextInput`, `TouchableOpacity`).
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// A custom screen in your config looks like:
|
|
65
|
+
{
|
|
66
|
+
"id": "welcome",
|
|
67
|
+
"type": "custom_screen",
|
|
68
|
+
"props": {},
|
|
69
|
+
"elements": [
|
|
70
|
+
{
|
|
71
|
+
"id": "root",
|
|
72
|
+
"type": "vstack",
|
|
73
|
+
"style": { "width": "100%", "height": "100%", "padding": 24 },
|
|
74
|
+
"children": [
|
|
75
|
+
{ "id": "title", "type": "text", "props": { "text": "Welcome!" }, "style": { "fontSize": 32, "fontWeight": "700" } },
|
|
76
|
+
{ "id": "spacer", "type": "spacer" },
|
|
77
|
+
{
|
|
78
|
+
"id": "cta",
|
|
79
|
+
"type": "hstack",
|
|
80
|
+
"style": { "backgroundColor": "#000", "borderRadius": 12, "padding": 16, "justifyContent": "center" },
|
|
81
|
+
"children": [
|
|
82
|
+
{ "id": "cta_text", "type": "text", "props": { "text": "Get Started" }, "style": { "color": "#fff", "fontSize": 16 } }
|
|
83
|
+
],
|
|
84
|
+
"action": { "type": "navigate", "destination": "next" }
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Pre-built Components
|
|
93
|
+
|
|
94
|
+
- **WelcomeScreen** — Image + title + subtitle + CTA button
|
|
95
|
+
- **TextInput** — Form for collecting user data (name, email, etc.)
|
|
96
|
+
- **SocialLogin** — Apple/Google/Facebook authentication buttons
|
|
97
|
+
|
|
98
|
+
## Composable Primitives
|
|
99
|
+
|
|
100
|
+
The element tree uses these building blocks:
|
|
101
|
+
|
|
102
|
+
**Containers** (have `children` array):
|
|
103
|
+
- `vstack` — vertical flex column
|
|
104
|
+
- `hstack` — horizontal flex row
|
|
105
|
+
- `zstack` — layered/overlapping elements
|
|
106
|
+
- `scrollview` — scrollable container
|
|
107
|
+
|
|
108
|
+
**Content** (leaf elements with `props`):
|
|
109
|
+
- `text` — text content (`props.text`)
|
|
110
|
+
- `image` — image (`props.url`, `props.slotNumber`)
|
|
111
|
+
- `video` — video placeholder (`props.url`)
|
|
112
|
+
- `lottie` — Lottie animation (`props.url`)
|
|
113
|
+
- `icon` — emoji (`props.emoji`) or named icon (`props.name`, `props.library`)
|
|
114
|
+
- `input` — text field (`props.placeholder`, `props.type`)
|
|
115
|
+
- `spacer` — flexible empty space
|
|
116
|
+
- `divider` — horizontal line
|
|
117
|
+
|
|
118
|
+
## Actions
|
|
119
|
+
|
|
120
|
+
Any container can have an `action` to make it interactive:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
action: {
|
|
124
|
+
type: 'tap' | 'navigate' | 'link' | 'toggle' | 'dismiss',
|
|
125
|
+
destination?: string // URL for link, screen ID for navigate
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
| Action | Behavior |
|
|
130
|
+
|--------|----------|
|
|
131
|
+
| `tap` | Generic tap handler |
|
|
132
|
+
| `navigate` | Go to `"next"`, `"previous"`, or a specific screen ID |
|
|
133
|
+
| `link` | Open URL via `Linking.openURL` |
|
|
134
|
+
| `toggle` | Toggle selected/unselected state (visual border change) |
|
|
135
|
+
| `dismiss` | Dismiss current screen or flow |
|
|
136
|
+
|
|
137
|
+
## Custom Screens (Developer-Registered Components)
|
|
138
|
+
|
|
139
|
+
For advanced use cases requiring native features (camera, payments, biometrics) or third-party SDKs, you can create custom React Native components and register them with the SDK.
|
|
140
|
+
|
|
141
|
+
### Creating a Custom Screen
|
|
142
|
+
|
|
143
|
+
1. **Create your component** with the required props:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// screens/PaywallScreen.tsx
|
|
147
|
+
import React, { useEffect } from 'react';
|
|
148
|
+
import { View, Text, Button } from 'react-native';
|
|
149
|
+
import type { CustomScreenProps } from 'noboarding';
|
|
150
|
+
|
|
151
|
+
export const PaywallScreen: React.FC<CustomScreenProps> = ({
|
|
152
|
+
analytics,
|
|
153
|
+
onNext,
|
|
154
|
+
onSkip,
|
|
155
|
+
preview,
|
|
156
|
+
data,
|
|
157
|
+
onDataUpdate,
|
|
158
|
+
}) => {
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
analytics.track('paywall_viewed', {
|
|
161
|
+
screen_id: 'paywall',
|
|
162
|
+
});
|
|
163
|
+
}, []);
|
|
164
|
+
|
|
165
|
+
const handlePurchase = () => {
|
|
166
|
+
analytics.track('paywall_conversion', {
|
|
167
|
+
package: 'premium_monthly',
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
onDataUpdate?.({
|
|
171
|
+
premium: true,
|
|
172
|
+
purchase_date: new Date().toISOString(),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
onNext();
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// Preview mode for dashboard
|
|
179
|
+
if (preview) {
|
|
180
|
+
return (
|
|
181
|
+
<View style={{ padding: 20, alignItems: 'center' }}>
|
|
182
|
+
<Text style={{ fontSize: 64 }}>💎</Text>
|
|
183
|
+
<Text style={{ fontSize: 24, fontWeight: 'bold', marginVertical: 20 }}>
|
|
184
|
+
Paywall Preview
|
|
185
|
+
</Text>
|
|
186
|
+
<Text style={{ color: '#666', marginBottom: 20 }}>
|
|
187
|
+
(Real paywall only works in app)
|
|
188
|
+
</Text>
|
|
189
|
+
<Button title="Continue" onPress={onNext} />
|
|
190
|
+
</View>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<View style={{ flex: 1, padding: 20 }}>
|
|
196
|
+
<Text style={{ fontSize: 28, fontWeight: 'bold', marginBottom: 20 }}>
|
|
197
|
+
Unlock Premium
|
|
198
|
+
</Text>
|
|
199
|
+
<Button title="Subscribe - $9.99/month" onPress={handlePurchase} />
|
|
200
|
+
{onSkip && (
|
|
201
|
+
<Button title="Maybe Later" onPress={onSkip} color="#666" />
|
|
202
|
+
)}
|
|
203
|
+
</View>
|
|
204
|
+
);
|
|
205
|
+
};
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
2. **Register the component** in your app:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { OnboardingFlow } from 'noboarding';
|
|
212
|
+
import { PaywallScreen } from './screens/PaywallScreen';
|
|
213
|
+
|
|
214
|
+
function App() {
|
|
215
|
+
return (
|
|
216
|
+
<OnboardingFlow
|
|
217
|
+
apiKey="sk_live_your_api_key_here"
|
|
218
|
+
customComponents={{
|
|
219
|
+
PaywallScreen: PaywallScreen, // Register here
|
|
220
|
+
}}
|
|
221
|
+
onComplete={(userData) => {
|
|
222
|
+
console.log('User data:', userData);
|
|
223
|
+
// userData includes data from custom screens
|
|
224
|
+
}}
|
|
225
|
+
/>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
3. **Add to your flow** in the dashboard:
|
|
231
|
+
- Click "Add Custom Screen"
|
|
232
|
+
- Enter component name: `PaywallScreen`
|
|
233
|
+
- Position in flow
|
|
234
|
+
|
|
235
|
+
### CustomScreenProps Interface
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
interface CustomScreenProps {
|
|
239
|
+
analytics: {
|
|
240
|
+
track: (event: string, properties?: Record<string, any>) => void;
|
|
241
|
+
};
|
|
242
|
+
onNext: () => void;
|
|
243
|
+
onSkip?: () => void;
|
|
244
|
+
preview?: boolean; // True when rendering in dashboard preview
|
|
245
|
+
data?: Record<string, any>; // Previously collected user data
|
|
246
|
+
onDataUpdate?: (data: Record<string, any>) => void; // Update collected data
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### RevenueCat Integration Example
|
|
251
|
+
|
|
252
|
+
Here's a complete example integrating RevenueCat paywalls:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// screens/RevenueCatPaywall.tsx
|
|
256
|
+
import React, { useState, useEffect } from 'react';
|
|
257
|
+
import { View, Text, ActivityIndicator, Alert } from 'react-native';
|
|
258
|
+
import Purchases, { PurchasesOffering } from 'react-native-purchases';
|
|
259
|
+
import type { CustomScreenProps } from 'noboarding';
|
|
260
|
+
|
|
261
|
+
export const RevenueCatPaywall: React.FC<CustomScreenProps> = ({
|
|
262
|
+
analytics,
|
|
263
|
+
onNext,
|
|
264
|
+
onSkip,
|
|
265
|
+
preview,
|
|
266
|
+
onDataUpdate,
|
|
267
|
+
}) => {
|
|
268
|
+
const [offering, setOffering] = useState<PurchasesOffering | null>(null);
|
|
269
|
+
const [loading, setLoading] = useState(true);
|
|
270
|
+
|
|
271
|
+
useEffect(() => {
|
|
272
|
+
analytics.track('paywall_viewed');
|
|
273
|
+
loadOffering();
|
|
274
|
+
}, []);
|
|
275
|
+
|
|
276
|
+
const loadOffering = async () => {
|
|
277
|
+
try {
|
|
278
|
+
const offerings = await Purchases.getOfferings();
|
|
279
|
+
setOffering(offerings.current);
|
|
280
|
+
|
|
281
|
+
analytics.track('paywall_loaded', {
|
|
282
|
+
offering_id: offerings.current?.identifier,
|
|
283
|
+
packages_count: offerings.current?.availablePackages.length,
|
|
284
|
+
});
|
|
285
|
+
} catch (error: any) {
|
|
286
|
+
analytics.track('paywall_error', { error: error.message });
|
|
287
|
+
} finally {
|
|
288
|
+
setLoading(false);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const handlePurchase = async (packageId: string) => {
|
|
293
|
+
try {
|
|
294
|
+
analytics.track('paywall_purchase_started', { package: packageId });
|
|
295
|
+
|
|
296
|
+
const pkg = offering?.availablePackages.find(p => p.identifier === packageId);
|
|
297
|
+
if (!pkg) return;
|
|
298
|
+
|
|
299
|
+
const { customerInfo } = await Purchases.purchasePackage(pkg);
|
|
300
|
+
const isPremium = customerInfo.entitlements.active['premium'] !== undefined;
|
|
301
|
+
|
|
302
|
+
if (isPremium) {
|
|
303
|
+
analytics.track('paywall_conversion', {
|
|
304
|
+
package: packageId,
|
|
305
|
+
price: pkg.product.priceString,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
onDataUpdate?.({
|
|
309
|
+
premium: true,
|
|
310
|
+
package: packageId,
|
|
311
|
+
purchase_timestamp: new Date().toISOString(),
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
Alert.alert('Welcome to Premium!', '', [
|
|
315
|
+
{ text: 'Continue', onPress: onNext }
|
|
316
|
+
]);
|
|
317
|
+
}
|
|
318
|
+
} catch (error: any) {
|
|
319
|
+
const cancelled = error.userCancelled;
|
|
320
|
+
|
|
321
|
+
analytics.track('paywall_purchase_failed', {
|
|
322
|
+
package: packageId,
|
|
323
|
+
cancelled,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
if (!cancelled) {
|
|
327
|
+
Alert.alert('Purchase Failed', 'Please try again.');
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const handleSkip = () => {
|
|
333
|
+
analytics.track('paywall_dismissed');
|
|
334
|
+
onSkip?.() || onNext();
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// Preview mode
|
|
338
|
+
if (preview) {
|
|
339
|
+
return (
|
|
340
|
+
<View style={{ padding: 20, alignItems: 'center' }}>
|
|
341
|
+
<Text style={{ fontSize: 64, marginBottom: 20 }}>💎</Text>
|
|
342
|
+
<Text style={{ fontSize: 24, fontWeight: 'bold' }}>
|
|
343
|
+
RevenueCat Paywall
|
|
344
|
+
</Text>
|
|
345
|
+
<Text style={{ color: '#999', marginTop: 8 }}>
|
|
346
|
+
(Preview - real paywall in app)
|
|
347
|
+
</Text>
|
|
348
|
+
</View>
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (loading) {
|
|
353
|
+
return (
|
|
354
|
+
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
|
355
|
+
<ActivityIndicator size="large" />
|
|
356
|
+
</View>
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return (
|
|
361
|
+
<View style={{ flex: 1, padding: 20 }}>
|
|
362
|
+
<Text style={{ fontSize: 32, fontWeight: 'bold', marginBottom: 20 }}>
|
|
363
|
+
Unlock Premium
|
|
364
|
+
</Text>
|
|
365
|
+
|
|
366
|
+
{offering?.availablePackages.map(pkg => (
|
|
367
|
+
<TouchableOpacity
|
|
368
|
+
key={pkg.identifier}
|
|
369
|
+
onPress={() => handlePurchase(pkg.identifier)}
|
|
370
|
+
style={{
|
|
371
|
+
backgroundColor: '#007AFF',
|
|
372
|
+
padding: 16,
|
|
373
|
+
borderRadius: 12,
|
|
374
|
+
marginBottom: 12,
|
|
375
|
+
}}
|
|
376
|
+
>
|
|
377
|
+
<Text style={{ color: '#FFF', fontSize: 18, fontWeight: 'bold' }}>
|
|
378
|
+
{pkg.product.title} - {pkg.product.priceString}
|
|
379
|
+
</Text>
|
|
380
|
+
</TouchableOpacity>
|
|
381
|
+
))}
|
|
382
|
+
|
|
383
|
+
<Button title="Maybe Later" onPress={handleSkip} color="#666" />
|
|
384
|
+
</View>
|
|
385
|
+
);
|
|
386
|
+
};
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**Setup:**
|
|
390
|
+
|
|
391
|
+
1. Install RevenueCat:
|
|
392
|
+
```bash
|
|
393
|
+
npm install react-native-purchases
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
2. Configure RevenueCat in your app initialization:
|
|
397
|
+
```typescript
|
|
398
|
+
// App.tsx
|
|
399
|
+
import Purchases from 'react-native-purchases';
|
|
400
|
+
|
|
401
|
+
useEffect(() => {
|
|
402
|
+
Purchases.configure({
|
|
403
|
+
apiKey: Platform.OS === 'ios'
|
|
404
|
+
? 'appl_YOUR_KEY'
|
|
405
|
+
: 'goog_YOUR_KEY',
|
|
406
|
+
});
|
|
407
|
+
}, []);
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
3. Register and use in onboarding:
|
|
411
|
+
```typescript
|
|
412
|
+
<OnboardingFlow
|
|
413
|
+
customComponents={{
|
|
414
|
+
RevenueCatPaywall: RevenueCatPaywall,
|
|
415
|
+
}}
|
|
416
|
+
/>
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
4. Set up webhooks (see below) to track conversions server-side
|
|
420
|
+
|
|
421
|
+
### Best Practices
|
|
422
|
+
|
|
423
|
+
- ✅ Always implement `preview` mode for dashboard compatibility
|
|
424
|
+
- ✅ Track key events with `analytics.track()`
|
|
425
|
+
- ✅ Use `onDataUpdate()` to save data from custom screens
|
|
426
|
+
- ✅ Handle errors gracefully with user-friendly messages
|
|
427
|
+
- ✅ Call `onNext()` when screen is complete
|
|
428
|
+
|
|
429
|
+
For more details, see [Custom Screens Guide](./cusomte_screens.md).
|
|
430
|
+
|
|
431
|
+
## API
|
|
432
|
+
|
|
433
|
+
### OnboardingFlow Props
|
|
434
|
+
|
|
435
|
+
| Prop | Type | Required | Description |
|
|
436
|
+
|------|------|----------|-------------|
|
|
437
|
+
| `apiKey` | `string` | Yes | Your API key from the dashboard |
|
|
438
|
+
| `onComplete` | `(data?) => void` | Yes | Called when user completes onboarding |
|
|
439
|
+
| `onSkip` | `() => void` | No | Called when user skips onboarding |
|
|
440
|
+
| `baseUrl` | `string` | No | Custom API base URL |
|
|
441
|
+
| `customComponents` | `Record<string, Component>` | No | Developer-registered custom screen components |
|
|
442
|
+
| `initialVariables` | `Record<string, any>` | No | Initial values for the variable store |
|
|
443
|
+
| `onUserIdGenerated` | `(userId: string) => void` | No | Called when SDK generates user ID (use to sync with RevenueCat, analytics, etc.) |
|
|
444
|
+
|
|
445
|
+
### ElementRenderer Props
|
|
446
|
+
|
|
447
|
+
| Prop | Type | Required | Description |
|
|
448
|
+
|------|------|----------|-------------|
|
|
449
|
+
| `elements` | `ElementNode[]` | Yes | The element tree to render |
|
|
450
|
+
| `analytics` | `AnalyticsManager` | Yes | Analytics manager for tracking |
|
|
451
|
+
| `screenId` | `string` | Yes | Current screen ID for analytics |
|
|
452
|
+
| `onNavigate` | `(destination: string) => void` | Yes | Navigation handler |
|
|
453
|
+
| `onDismiss` | `() => void` | Yes | Dismiss handler |
|
|
454
|
+
|
|
455
|
+
## Auto-Tracked Events
|
|
456
|
+
|
|
457
|
+
The SDK automatically tracks:
|
|
458
|
+
|
|
459
|
+
- `onboarding_started`
|
|
460
|
+
- `screen_viewed`
|
|
461
|
+
- `screen_completed`
|
|
462
|
+
- `screen_skipped`
|
|
463
|
+
- `time_on_screen`
|
|
464
|
+
- `button_clicked`
|
|
465
|
+
- `input_focused`
|
|
466
|
+
- `input_completed`
|
|
467
|
+
- `onboarding_completed`
|
|
468
|
+
- `onboarding_abandoned`
|
|
469
|
+
- `element_action` — tracks every action with element ID, action type, and screen ID
|
|
470
|
+
|
|
471
|
+
## Exports
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
// Main component
|
|
475
|
+
import { OnboardingFlow } from 'noboarding';
|
|
476
|
+
|
|
477
|
+
// Element renderer (for custom usage)
|
|
478
|
+
import { ElementRenderer } from 'noboarding';
|
|
479
|
+
|
|
480
|
+
// Types
|
|
481
|
+
import type {
|
|
482
|
+
OnboardingFlowProps,
|
|
483
|
+
ScreenConfig,
|
|
484
|
+
OnboardingConfig,
|
|
485
|
+
ElementNode,
|
|
486
|
+
ElementType,
|
|
487
|
+
ElementAction,
|
|
488
|
+
ElementStyle,
|
|
489
|
+
ElementPosition,
|
|
490
|
+
AnalyticsEvent,
|
|
491
|
+
} from 'noboarding';
|
|
492
|
+
|
|
493
|
+
// Utilities
|
|
494
|
+
import { API, AnalyticsManager } from 'noboarding';
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
## Development
|
|
498
|
+
|
|
499
|
+
The TestApp imports the SDK from the compiled `lib/` directory (`"main": "lib/index.js"`), not from `src/` directly. After making any changes to files in `sdk/src/`, you must rebuild before testing:
|
|
500
|
+
|
|
501
|
+
```bash
|
|
502
|
+
cd sdk
|
|
503
|
+
npm run build
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
Then restart the TestApp. If you skip this step, the TestApp will still be running the old compiled code and your changes won't take effect.
|
|
507
|
+
|
|
508
|
+
## Requirements
|
|
509
|
+
|
|
510
|
+
- React Native >= 0.60.0
|
|
511
|
+
- React >= 16.8.0
|
|
512
|
+
|
|
513
|
+
## License
|
|
514
|
+
|
|
515
|
+
MIT
|