@umituz/react-native-onboarding 2.2.0 → 2.3.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.
package/README.md CHANGED
@@ -185,6 +185,8 @@ const slides: OnboardingSlide[] = [
185
185
  | `showProgressText` | `boolean` | `true` | Show progress text (1 of 5) |
186
186
  | `storageKey` | `string` | - | Custom storage key for completion state |
187
187
  | `autoComplete` | `boolean` | `false` | Auto-complete on last slide |
188
+ | `enableDeviceTracking` | `boolean` | `false` | Collect device info during onboarding |
189
+ | `userId` | `string` | - | User ID for device tracking (optional) |
188
190
 
189
191
  ### OnboardingSlide Interface
190
192
 
@@ -328,6 +330,69 @@ const isComplete = userData.completedAt !== undefined;
328
330
 
329
331
  // Check if onboarding was skipped
330
332
  const wasSkipped = userData.skipped === true;
333
+
334
+ // Get device info (if device tracking was enabled)
335
+ const deviceInfo = userData.deviceInfo;
336
+ ```
337
+
338
+ ## 📱 Device Tracking (Optional)
339
+
340
+ Collect device information during onboarding for analytics or support purposes.
341
+
342
+ ### Installation
343
+
344
+ First, install the device tracking package:
345
+
346
+ ```bash
347
+ npm install @umituz/react-native-device
348
+ ```
349
+
350
+ ### Usage
351
+
352
+ ```tsx
353
+ import { OnboardingScreen } from '@umituz/react-native-onboarding';
354
+
355
+ <OnboardingScreen
356
+ slides={slides}
357
+ enableDeviceTracking={true}
358
+ userId="user123" // Optional
359
+ onComplete={async () => {
360
+ const userData = onboardingStore.getUserData();
361
+
362
+ // Device info is now available
363
+ console.log('Device:', userData.deviceInfo?.platform);
364
+ console.log('OS:', userData.deviceInfo?.osVersion);
365
+ console.log('App:', userData.deviceInfo?.appVersion);
366
+ }}
367
+ />
368
+ ```
369
+
370
+ ### Device Info Structure
371
+
372
+ ```typescript
373
+ {
374
+ deviceId: string;
375
+ platform: 'ios' | 'android' | 'web';
376
+ osVersion: string;
377
+ appVersion: string;
378
+ deviceName: string;
379
+ manufacturer: string;
380
+ brand: string;
381
+ timestamp: number;
382
+ userId?: string; // If provided
383
+ }
384
+ ```
385
+
386
+ ### Manual Device Info Collection
387
+
388
+ ```tsx
389
+ import { OnboardingDeviceTrackingService } from '@umituz/react-native-onboarding';
390
+
391
+ // Collect device info manually
392
+ const deviceInfo = await OnboardingDeviceTrackingService.collectDeviceInfo('user123');
393
+
394
+ // Check if device tracking is available
395
+ const isAvailable = await OnboardingDeviceTrackingService.isDeviceTrackingAvailable();
331
396
  ```
332
397
 
333
398
  ## 🔄 Resetting Onboarding
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-onboarding",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Advanced onboarding flow for React Native apps with personalization questions, theme-aware colors, animations, and customizable slides. SOLID, DRY, KISS principles applied.",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -44,6 +44,11 @@
44
44
  "react-native-safe-area-context": "^5.0.0",
45
45
  "zustand": "^5.0.0"
46
46
  },
47
+ "peerDependenciesMeta": {
48
+ "@umituz/react-native-device": {
49
+ "optional": true
50
+ }
51
+ },
47
52
  "devDependencies": {
48
53
  "@types/react": "^18.2.45",
49
54
  "@types/react-native": "^0.73.0",
@@ -85,5 +85,17 @@ export interface OnboardingOptions {
85
85
  * Show paywall modal on onboarding completion (default: false)
86
86
  */
87
87
  showPaywallOnComplete?: boolean;
88
+
89
+ /**
90
+ * Enable device tracking (default: false)
91
+ * When enabled, collects device information during onboarding
92
+ */
93
+ enableDeviceTracking?: boolean;
94
+
95
+ /**
96
+ * User ID for device tracking (optional)
97
+ * Only used when enableDeviceTracking is true
98
+ */
99
+ userId?: string;
88
100
  }
89
101
 
@@ -39,5 +39,17 @@ export interface OnboardingUserData {
39
39
  gender?: string;
40
40
  [key: string]: any;
41
41
  };
42
+
43
+ /**
44
+ * Device information (optional)
45
+ * Only collected when enableDeviceTracking is true
46
+ */
47
+ deviceInfo?: {
48
+ deviceId?: string;
49
+ platform?: string;
50
+ osVersion?: string;
51
+ appVersion?: string;
52
+ [key: string]: any;
53
+ };
42
54
  }
43
55
 
package/src/index.ts CHANGED
@@ -52,6 +52,7 @@ export {
52
52
  useOnboardingNavigation,
53
53
  type UseOnboardingNavigationReturn,
54
54
  } from "./infrastructure/hooks/useOnboardingNavigation";
55
+ export { OnboardingDeviceTrackingService } from "./infrastructure/services/OnboardingDeviceTrackingService";
55
56
 
56
57
  // =============================================================================
57
58
  // PRESENTATION LAYER - Components and Screens
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Onboarding Device Tracking Service
3
+ *
4
+ * Handles device information collection during onboarding
5
+ * Single Responsibility: Device tracking only
6
+ */
7
+
8
+ import type { OnboardingUserData } from "../../domain/entities/OnboardingUserData";
9
+
10
+ /**
11
+ * Service for device tracking during onboarding
12
+ * Optional feature - only used when enableDeviceTracking is true
13
+ */
14
+ export class OnboardingDeviceTrackingService {
15
+ /**
16
+ * Collect device information
17
+ * @param userId - Optional user ID
18
+ * @returns Device information object
19
+ */
20
+ static async collectDeviceInfo(userId?: string): Promise<OnboardingUserData['deviceInfo']> {
21
+ try {
22
+ // Dynamic import to avoid loading @umituz/react-native-device if not needed
23
+ // @ts-expect-error - Optional peer dependency, may not be installed
24
+ const { DeviceService } = await import('@umituz/react-native-device');
25
+
26
+ const systemInfo = await DeviceService.getSystemInfo({ userId });
27
+
28
+ return {
29
+ deviceId: systemInfo.device.modelId || 'unknown',
30
+ platform: systemInfo.device.platform,
31
+ osVersion: systemInfo.device.osVersion || 'unknown',
32
+ appVersion: systemInfo.application.nativeApplicationVersion || 'unknown',
33
+ deviceName: systemInfo.device.deviceName || 'unknown',
34
+ manufacturer: systemInfo.device.manufacturer || 'unknown',
35
+ brand: systemInfo.device.brand || 'unknown',
36
+ timestamp: systemInfo.timestamp,
37
+ ...(userId && { userId }),
38
+ };
39
+ } catch (error) {
40
+ /* eslint-disable-next-line no-console */
41
+ if (__DEV__) console.warn('Failed to collect device info:', error);
42
+ return undefined;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Check if device tracking is available
48
+ * @returns True if @umituz/react-native-device is available
49
+ */
50
+ static async isDeviceTrackingAvailable(): Promise<boolean> {
51
+ try {
52
+ // @ts-expect-error - Optional peer dependency, may not be installed
53
+ await import('@umituz/react-native-device');
54
+ return true;
55
+ } catch {
56
+ return false;
57
+ }
58
+ }
59
+ }
60
+
@@ -12,6 +12,7 @@ import {
12
12
  unwrap,
13
13
  } from "@umituz/react-native-storage";
14
14
  import type { OnboardingUserData } from "../../domain/entities/OnboardingUserData";
15
+ import { OnboardingDeviceTrackingService } from "../services/OnboardingDeviceTrackingService";
15
16
 
16
17
  interface OnboardingStore {
17
18
  // State
@@ -23,7 +24,7 @@ interface OnboardingStore {
23
24
 
24
25
  // Actions
25
26
  initialize: (storageKey?: string) => Promise<void>;
26
- complete: (storageKey?: string) => Promise<void>;
27
+ complete: (storageKey?: string, options?: { enableDeviceTracking?: boolean; userId?: string }) => Promise<void>;
27
28
  skip: (storageKey?: string) => Promise<void>;
28
29
  setCurrentStep: (step: number) => void;
29
30
  reset: (storageKey?: string) => Promise<void>;
@@ -33,6 +34,7 @@ interface OnboardingStore {
33
34
  getAnswer: (questionId: string) => any;
34
35
  getUserData: () => OnboardingUserData;
35
36
  setUserData: (data: OnboardingUserData) => Promise<void>;
37
+ collectDeviceInfo: (userId?: string) => Promise<void>;
36
38
  }
37
39
 
38
40
  const DEFAULT_STORAGE_KEY = StorageKey.ONBOARDING_COMPLETED;
@@ -67,7 +69,7 @@ export const useOnboardingStore = create<OnboardingStore>((set, get) => ({
67
69
  });
68
70
  },
69
71
 
70
- complete: async (storageKey = DEFAULT_STORAGE_KEY) => {
72
+ complete: async (storageKey = DEFAULT_STORAGE_KEY, options?: { enableDeviceTracking?: boolean; userId?: string }) => {
71
73
  set({ loading: true, error: null });
72
74
 
73
75
  const result = await storageRepository.setString(storageKey, "true");
@@ -75,6 +77,12 @@ export const useOnboardingStore = create<OnboardingStore>((set, get) => ({
75
77
  // Update user data with completion timestamp
76
78
  const userData = get().userData;
77
79
  userData.completedAt = new Date().toISOString();
80
+
81
+ // Collect device info if enabled
82
+ if (options?.enableDeviceTracking) {
83
+ userData.deviceInfo = await OnboardingDeviceTrackingService.collectDeviceInfo(options.userId);
84
+ }
85
+
78
86
  await storageRepository.setObject(USER_DATA_STORAGE_KEY, userData);
79
87
 
80
88
  set({
@@ -144,6 +152,13 @@ export const useOnboardingStore = create<OnboardingStore>((set, get) => ({
144
152
  await storageRepository.setObject(USER_DATA_STORAGE_KEY, data);
145
153
  set({ userData: data });
146
154
  },
155
+
156
+ collectDeviceInfo: async (userId?: string) => {
157
+ const userData = get().userData;
158
+ userData.deviceInfo = await OnboardingDeviceTrackingService.collectDeviceInfo(userId);
159
+ await storageRepository.setObject(USER_DATA_STORAGE_KEY, userData);
160
+ set({ userData: { ...userData } });
161
+ },
147
162
  }));
148
163
 
149
164
  /**
@@ -62,6 +62,18 @@ export interface OnboardingScreenProps extends OnboardingOptions {
62
62
  * When true, shows premium paywall before completing onboarding
63
63
  */
64
64
  showPaywallOnComplete?: boolean;
65
+
66
+ /**
67
+ * Enable device tracking (default: false)
68
+ * When enabled, collects device information during onboarding
69
+ */
70
+ enableDeviceTracking?: boolean;
71
+
72
+ /**
73
+ * User ID for device tracking (optional)
74
+ * Only used when enableDeviceTracking is true
75
+ */
76
+ userId?: string;
65
77
  }
66
78
 
67
79
  /**
@@ -88,6 +100,8 @@ export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
88
100
  renderSlide,
89
101
  onUpgrade,
90
102
  showPaywallOnComplete = false,
103
+ enableDeviceTracking = false,
104
+ userId,
91
105
  }) => {
92
106
  const insets = useSafeAreaInsets();
93
107
  const tokens = useAppDesignTokens();
@@ -112,7 +126,7 @@ export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
112
126
  } = useOnboardingNavigation(
113
127
  filteredSlides.length,
114
128
  async () => {
115
- await onboardingStore.complete(storageKey);
129
+ await onboardingStore.complete(storageKey, { enableDeviceTracking, userId });
116
130
  if (onComplete) {
117
131
  await onComplete();
118
132
  }
@@ -217,16 +231,18 @@ export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
217
231
  useGradient={useGradient}
218
232
  />
219
233
  )}
220
- {renderSlide ? (
221
- renderSlide(currentSlide)
222
- ) : currentSlide.type === "question" && currentSlide.question ? (
223
- <QuestionSlide
224
- slide={currentSlide}
225
- value={currentAnswer}
226
- onChange={setCurrentAnswer}
227
- />
228
- ) : (
229
- <OnboardingSlideComponent slide={currentSlide} />
234
+ {currentSlide && (
235
+ renderSlide ? (
236
+ renderSlide(currentSlide)
237
+ ) : currentSlide.type === "question" && currentSlide.question ? (
238
+ <QuestionSlide
239
+ slide={currentSlide}
240
+ value={currentAnswer}
241
+ onChange={setCurrentAnswer}
242
+ />
243
+ ) : (
244
+ <OnboardingSlideComponent slide={currentSlide} />
245
+ )
230
246
  )}
231
247
  {renderFooter ? (
232
248
  renderFooter({