noboarding 1.0.3-beta โ†’ 1.0.6-beta

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.
@@ -96,6 +96,7 @@ const OnboardingFlow = ({ apiKey, testKey, productionKey, onComplete, onSkip, ba
96
96
  const [loading, setLoading] = (0, react_1.useState)(true);
97
97
  const [error, setError] = (0, react_1.useState)(null);
98
98
  const [screens, setScreens] = (0, react_1.useState)([]);
99
+ const [assets, setAssets] = (0, react_1.useState)([]);
99
100
  const [currentIndex, setCurrentIndex] = (0, react_1.useState)(0);
100
101
  const [collectedData, setCollectedData] = (0, react_1.useState)({});
101
102
  const [variables, setVariables] = (0, react_1.useState)(initialVariables || {});
@@ -136,7 +137,7 @@ const OnboardingFlow = ({ apiKey, testKey, productionKey, onComplete, onSkip, ba
136
137
  }
137
138
  }, [currentIndex, screens]);
138
139
  const initializeFlow = async () => {
139
- var _a, _b, _c;
140
+ var _a, _b, _c, _d, _e, _f, _g;
140
141
  try {
141
142
  setLoading(true);
142
143
  setError(null);
@@ -154,6 +155,10 @@ const OnboardingFlow = ({ apiKey, testKey, productionKey, onComplete, onSkip, ba
154
155
  const configResponse = await api.getConfig();
155
156
  // Store flow_id for analytics
156
157
  flowIdRef.current = configResponse.config_id;
158
+ // Store assets from config
159
+ if (configResponse.config.assets) {
160
+ setAssets(configResponse.config.assets);
161
+ }
157
162
  // Handle A/B test experiment assignment
158
163
  let screensToUse = configResponse.config.screens;
159
164
  if (configResponse.experiments && configResponse.experiments.length > 0) {
@@ -161,11 +166,23 @@ const OnboardingFlow = ({ apiKey, testKey, productionKey, onComplete, onSkip, ba
161
166
  const experiment = configResponse.experiments[0];
162
167
  try {
163
168
  const assignment = await api.assignVariant(experiment.id, userIdRef.current);
169
+ console.log('๐Ÿงช A/B Test Assignment:', {
170
+ experiment_id: experiment.id,
171
+ experiment_name: experiment.name,
172
+ variant_id: assignment.variant_id,
173
+ has_variant_screens: ((_b = (_a = assignment.variant_config) === null || _a === void 0 ? void 0 : _a.screens) === null || _b === void 0 ? void 0 : _b.length) > 0,
174
+ variant_screen_count: ((_d = (_c = assignment.variant_config) === null || _c === void 0 ? void 0 : _c.screens) === null || _d === void 0 ? void 0 : _d.length) || 0,
175
+ cached: assignment.cached,
176
+ });
164
177
  // Set experiment context so all events get tagged
165
178
  analytics.setExperimentContext(experiment.id, assignment.variant_id);
166
179
  // Use variant screens if available
167
- if (((_b = (_a = assignment.variant_config) === null || _a === void 0 ? void 0 : _a.screens) === null || _b === void 0 ? void 0 : _b.length) > 0) {
180
+ if (((_f = (_e = assignment.variant_config) === null || _e === void 0 ? void 0 : _e.screens) === null || _f === void 0 ? void 0 : _f.length) > 0) {
168
181
  screensToUse = assignment.variant_config.screens;
182
+ console.log('๐Ÿ“ฑ Using variant screens:', assignment.variant_config.screens.length, 'screens');
183
+ }
184
+ else {
185
+ console.log('๐Ÿ“ฑ Using base flow screens (variant has no screens defined)');
169
186
  }
170
187
  }
171
188
  catch (err) {
@@ -181,7 +198,7 @@ const OnboardingFlow = ({ apiKey, testKey, productionKey, onComplete, onSkip, ba
181
198
  // Track onboarding start with first screen
182
199
  analytics.track('onboarding_started', {
183
200
  flow_id: flowIdRef.current,
184
- screen_id: (_c = normalizedScreens[0]) === null || _c === void 0 ? void 0 : _c.id,
201
+ screen_id: (_g = normalizedScreens[0]) === null || _g === void 0 ? void 0 : _g.id,
185
202
  });
186
203
  setLoading(false);
187
204
  }
@@ -302,7 +319,7 @@ const OnboardingFlow = ({ apiKey, testKey, productionKey, onComplete, onSkip, ba
302
319
  }
303
320
  };
304
321
  return (<react_native_1.View style={styles.container}>
305
- <ElementRenderer_1.ElementRenderer elements={currentScreen.elements} analytics={analyticsRef.current} screenId={currentScreen.id} onNavigate={handleElementNavigate} onDismiss={onSkip ? handleSkipAll : handleNext} variables={allVariables} onSetVariable={handleSetVariable}/>
322
+ <ElementRenderer_1.ElementRenderer elements={currentScreen.elements} analytics={analyticsRef.current} screenId={currentScreen.id} onNavigate={handleElementNavigate} onDismiss={onSkip ? handleSkipAll : handleNext} variables={allVariables} onSetVariable={handleSetVariable} assets={assets}/>
306
323
  </react_native_1.View>);
307
324
  }
308
325
  // Handle custom_screen type โ€” developer-registered React Native components
@@ -0,0 +1,19 @@
1
+ import { Animated } from 'react-native';
2
+ import type { EntranceAnimation, InteractiveAnimation, HapticType } from './types';
3
+ export declare const triggerHaptic: (type?: HapticType) => void;
4
+ export declare const getEasing: (easingType?: string) => import("react-native").EasingFunction;
5
+ export interface EntranceAnimationValues {
6
+ opacity: Animated.Value;
7
+ translateY: Animated.Value;
8
+ translateX: Animated.Value;
9
+ scale: Animated.Value;
10
+ }
11
+ export declare const createEntranceAnimationValues: () => EntranceAnimationValues;
12
+ export declare const startEntranceAnimation: (config: EntranceAnimation, values: EntranceAnimationValues, delay?: number) => void;
13
+ export declare const startInteractiveAnimation: (config: InteractiveAnimation, animatedValue: Animated.Value) => void;
14
+ export interface TypewriterState {
15
+ displayedText: string;
16
+ currentIndex: number;
17
+ isComplete: boolean;
18
+ }
19
+ export declare const shouldTriggerHaptic: (index: number, frequency: string) => boolean;
@@ -0,0 +1,252 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shouldTriggerHaptic = exports.startInteractiveAnimation = exports.startEntranceAnimation = exports.createEntranceAnimationValues = exports.getEasing = exports.triggerHaptic = void 0;
4
+ const react_native_1 = require("react-native");
5
+ // Lazy load haptics to avoid errors if not installed
6
+ let Haptics = null;
7
+ try {
8
+ Haptics = require('expo-haptics');
9
+ }
10
+ catch (e) {
11
+ console.warn('expo-haptics not installed, haptic feedback will be disabled');
12
+ }
13
+ // โ”€โ”€โ”€ Haptic Feedback Helper โ”€โ”€โ”€
14
+ const triggerHaptic = (type = 'light') => {
15
+ if (!Haptics) {
16
+ // Silently skip if expo-haptics not available
17
+ return;
18
+ }
19
+ try {
20
+ switch (type) {
21
+ case 'light':
22
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
23
+ break;
24
+ case 'medium':
25
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
26
+ break;
27
+ case 'heavy':
28
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
29
+ break;
30
+ case 'success':
31
+ Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
32
+ break;
33
+ case 'warning':
34
+ Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);
35
+ break;
36
+ case 'error':
37
+ Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
38
+ break;
39
+ }
40
+ }
41
+ catch (error) {
42
+ console.warn('Haptic feedback failed:', error);
43
+ }
44
+ };
45
+ exports.triggerHaptic = triggerHaptic;
46
+ // โ”€โ”€โ”€ Easing Function Mapper โ”€โ”€โ”€
47
+ const getEasing = (easingType) => {
48
+ switch (easingType) {
49
+ case 'linear':
50
+ return react_native_1.Easing.linear;
51
+ case 'ease-in':
52
+ return react_native_1.Easing.in(react_native_1.Easing.ease);
53
+ case 'ease-out':
54
+ return react_native_1.Easing.out(react_native_1.Easing.ease);
55
+ case 'ease-in-out':
56
+ return react_native_1.Easing.inOut(react_native_1.Easing.ease);
57
+ case 'spring':
58
+ return react_native_1.Easing.elastic(1);
59
+ default:
60
+ return react_native_1.Easing.inOut(react_native_1.Easing.ease);
61
+ }
62
+ };
63
+ exports.getEasing = getEasing;
64
+ const createEntranceAnimationValues = () => ({
65
+ opacity: new react_native_1.Animated.Value(0),
66
+ translateY: new react_native_1.Animated.Value(0),
67
+ translateX: new react_native_1.Animated.Value(0),
68
+ scale: new react_native_1.Animated.Value(1),
69
+ });
70
+ exports.createEntranceAnimationValues = createEntranceAnimationValues;
71
+ const startEntranceAnimation = (config, values, delay = 0) => {
72
+ const duration = config.duration || 400;
73
+ const totalDelay = (config.delay || 0) + delay;
74
+ const easing = (0, exports.getEasing)(config.easing);
75
+ // Set initial values based on animation type
76
+ switch (config.type) {
77
+ case 'fadeIn':
78
+ values.opacity.setValue(0);
79
+ break;
80
+ case 'slideUp':
81
+ values.opacity.setValue(0);
82
+ values.translateY.setValue(30);
83
+ break;
84
+ case 'slideDown':
85
+ values.opacity.setValue(0);
86
+ values.translateY.setValue(-30);
87
+ break;
88
+ case 'slideLeft':
89
+ values.opacity.setValue(0);
90
+ values.translateX.setValue(30);
91
+ break;
92
+ case 'slideRight':
93
+ values.opacity.setValue(0);
94
+ values.translateX.setValue(-30);
95
+ break;
96
+ case 'scaleIn':
97
+ values.opacity.setValue(0);
98
+ values.scale.setValue(0.8);
99
+ break;
100
+ case 'none':
101
+ values.opacity.setValue(1);
102
+ return;
103
+ }
104
+ // Animate to final values
105
+ react_native_1.Animated.parallel([
106
+ react_native_1.Animated.timing(values.opacity, {
107
+ toValue: 1,
108
+ duration,
109
+ delay: totalDelay,
110
+ easing,
111
+ useNativeDriver: true,
112
+ }),
113
+ react_native_1.Animated.timing(values.translateY, {
114
+ toValue: 0,
115
+ duration,
116
+ delay: totalDelay,
117
+ easing,
118
+ useNativeDriver: true,
119
+ }),
120
+ react_native_1.Animated.timing(values.translateX, {
121
+ toValue: 0,
122
+ duration,
123
+ delay: totalDelay,
124
+ easing,
125
+ useNativeDriver: true,
126
+ }),
127
+ react_native_1.Animated.timing(values.scale, {
128
+ toValue: 1,
129
+ duration,
130
+ delay: totalDelay,
131
+ easing,
132
+ useNativeDriver: true,
133
+ }),
134
+ ]).start();
135
+ };
136
+ exports.startEntranceAnimation = startEntranceAnimation;
137
+ // โ”€โ”€โ”€ Interactive Animations โ”€โ”€โ”€
138
+ const startInteractiveAnimation = (config, animatedValue) => {
139
+ const duration = config.duration || 200;
140
+ const intensity = config.intensity || (config.type === 'scale' ? 0.95 : 10);
141
+ // Trigger haptic if enabled
142
+ if (config.haptic && config.hapticType) {
143
+ (0, exports.triggerHaptic)(config.hapticType);
144
+ }
145
+ switch (config.type) {
146
+ case 'scale':
147
+ react_native_1.Animated.sequence([
148
+ react_native_1.Animated.timing(animatedValue, {
149
+ toValue: intensity,
150
+ duration: duration / 2,
151
+ easing: react_native_1.Easing.out(react_native_1.Easing.ease),
152
+ useNativeDriver: true,
153
+ }),
154
+ react_native_1.Animated.timing(animatedValue, {
155
+ toValue: 1,
156
+ duration: duration / 2,
157
+ easing: react_native_1.Easing.in(react_native_1.Easing.ease),
158
+ useNativeDriver: true,
159
+ }),
160
+ ]).start();
161
+ break;
162
+ case 'pulse':
163
+ const pulseSequence = react_native_1.Animated.sequence([
164
+ react_native_1.Animated.timing(animatedValue, {
165
+ toValue: 1.05,
166
+ duration: duration,
167
+ easing: react_native_1.Easing.inOut(react_native_1.Easing.ease),
168
+ useNativeDriver: true,
169
+ }),
170
+ react_native_1.Animated.timing(animatedValue, {
171
+ toValue: 1,
172
+ duration: duration,
173
+ easing: react_native_1.Easing.inOut(react_native_1.Easing.ease),
174
+ useNativeDriver: true,
175
+ }),
176
+ ]);
177
+ if (config.repeat) {
178
+ react_native_1.Animated.loop(pulseSequence).start();
179
+ }
180
+ else {
181
+ pulseSequence.start();
182
+ }
183
+ break;
184
+ case 'shake':
185
+ react_native_1.Animated.sequence([
186
+ react_native_1.Animated.timing(animatedValue, {
187
+ toValue: intensity,
188
+ duration: duration / 8,
189
+ useNativeDriver: true,
190
+ }),
191
+ react_native_1.Animated.timing(animatedValue, {
192
+ toValue: -intensity,
193
+ duration: duration / 4,
194
+ useNativeDriver: true,
195
+ }),
196
+ react_native_1.Animated.timing(animatedValue, {
197
+ toValue: intensity,
198
+ duration: duration / 4,
199
+ useNativeDriver: true,
200
+ }),
201
+ react_native_1.Animated.timing(animatedValue, {
202
+ toValue: -intensity,
203
+ duration: duration / 4,
204
+ useNativeDriver: true,
205
+ }),
206
+ react_native_1.Animated.timing(animatedValue, {
207
+ toValue: 0,
208
+ duration: duration / 8,
209
+ useNativeDriver: true,
210
+ }),
211
+ ]).start();
212
+ break;
213
+ case 'bounce':
214
+ react_native_1.Animated.sequence([
215
+ react_native_1.Animated.timing(animatedValue, {
216
+ toValue: -20,
217
+ duration: duration / 3,
218
+ easing: react_native_1.Easing.out(react_native_1.Easing.ease),
219
+ useNativeDriver: true,
220
+ }),
221
+ react_native_1.Animated.timing(animatedValue, {
222
+ toValue: 5,
223
+ duration: duration / 3,
224
+ easing: react_native_1.Easing.in(react_native_1.Easing.ease),
225
+ useNativeDriver: true,
226
+ }),
227
+ react_native_1.Animated.timing(animatedValue, {
228
+ toValue: 0,
229
+ duration: duration / 3,
230
+ easing: react_native_1.Easing.out(react_native_1.Easing.ease),
231
+ useNativeDriver: true,
232
+ }),
233
+ ]).start();
234
+ break;
235
+ }
236
+ };
237
+ exports.startInteractiveAnimation = startInteractiveAnimation;
238
+ const shouldTriggerHaptic = (index, frequency) => {
239
+ switch (frequency) {
240
+ case 'every':
241
+ return true;
242
+ case 'every-2':
243
+ return index % 2 === 0;
244
+ case 'every-3':
245
+ return index % 3 === 0;
246
+ case 'every-5':
247
+ return index % 5 === 0;
248
+ default:
249
+ return false;
250
+ }
251
+ };
252
+ exports.shouldTriggerHaptic = shouldTriggerHaptic;
@@ -8,6 +8,11 @@ interface ElementRendererProps {
8
8
  onDismiss?: () => void;
9
9
  variables?: Record<string, any>;
10
10
  onSetVariable?: (name: string, value: any) => void;
11
+ assets?: Array<{
12
+ name: string;
13
+ type: string;
14
+ data: string;
15
+ }>;
11
16
  }
12
17
  export declare const ElementRenderer: React.FC<ElementRendererProps>;
13
18
  export {};