@umituz/react-native-onboarding 2.6.9 → 2.6.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-onboarding",
3
- "version": "2.6.9",
3
+ "version": "2.6.10",
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",
@@ -0,0 +1,171 @@
1
+ /**
2
+ * useOnboardingScreenState Hook
3
+ * Single Responsibility: Manage onboarding screen state and handlers
4
+ */
5
+
6
+ import { useMemo } from "react";
7
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
9
+ import type { OnboardingSlide } from "../../domain/entities/OnboardingSlide";
10
+ import { useOnboardingStore } from "../../infrastructure/storage/OnboardingStore";
11
+ import { useOnboardingNavigation } from "../../infrastructure/hooks/useOnboardingNavigation";
12
+ import { useOnboardingAnswers } from "../../infrastructure/hooks/useOnboardingAnswers";
13
+ import { OnboardingSlideService } from "../../infrastructure/services/OnboardingSlideService";
14
+ import { OnboardingValidationService } from "../../infrastructure/services/OnboardingValidationService";
15
+ import { shouldUseGradient } from "../../infrastructure/utils/gradientUtils";
16
+
17
+ export interface UseOnboardingScreenStateProps {
18
+ slides: OnboardingSlide[] | undefined;
19
+ storageKey?: string;
20
+ onComplete?: () => void | Promise<void>;
21
+ onSkip?: () => void | Promise<void>;
22
+ globalUseGradient?: boolean;
23
+ }
24
+
25
+ export interface UseOnboardingScreenStateReturn {
26
+ filteredSlides: OnboardingSlide[];
27
+ currentSlide: OnboardingSlide | undefined;
28
+ currentIndex: number;
29
+ isFirstSlide: boolean;
30
+ isLastSlide: boolean;
31
+ currentAnswer: any;
32
+ isAnswerValid: boolean;
33
+ useGradient: boolean;
34
+ containerStyle: any;
35
+ handleNext: () => Promise<void>;
36
+ handlePrevious: () => void;
37
+ handleSkip: () => Promise<void>;
38
+ setCurrentAnswer: (value: any) => void;
39
+ }
40
+
41
+ export function useOnboardingScreenState({
42
+ slides,
43
+ storageKey,
44
+ onComplete,
45
+ onSkip,
46
+ globalUseGradient = false,
47
+ }: UseOnboardingScreenStateProps): UseOnboardingScreenStateReturn {
48
+ const insets = useSafeAreaInsets();
49
+ const tokens = useAppDesignTokens();
50
+ const onboardingStore = useOnboardingStore();
51
+
52
+ // Filter slides using service
53
+ const filteredSlides = useMemo(() => {
54
+ if (!slides || !Array.isArray(slides) || slides.length === 0) {
55
+ return [];
56
+ }
57
+ const userData = onboardingStore.getUserData();
58
+ return OnboardingSlideService.filterSlides(slides, userData);
59
+ }, [slides, onboardingStore]);
60
+
61
+ // Navigation hook
62
+ const {
63
+ currentIndex,
64
+ goToNext,
65
+ goToPrevious,
66
+ complete: completeOnboarding,
67
+ skip: skipOnboarding,
68
+ isLastSlide,
69
+ isFirstSlide,
70
+ } = useOnboardingNavigation(
71
+ filteredSlides.length,
72
+ async () => {
73
+ await onboardingStore.complete(storageKey);
74
+ if (onComplete) {
75
+ await onComplete();
76
+ }
77
+ },
78
+ async () => {
79
+ await onboardingStore.skip(storageKey);
80
+ if (onSkip) {
81
+ await onSkip();
82
+ }
83
+ },
84
+ );
85
+
86
+ // Get current slide
87
+ const currentSlide = useMemo(
88
+ () => OnboardingSlideService.getSlideAtIndex(filteredSlides, currentIndex),
89
+ [filteredSlides, currentIndex],
90
+ );
91
+
92
+ // Answer management hook
93
+ const {
94
+ currentAnswer,
95
+ setCurrentAnswer,
96
+ loadAnswerForSlide,
97
+ saveCurrentAnswer,
98
+ } = useOnboardingAnswers(currentSlide);
99
+
100
+ // Handle next slide
101
+ const handleNext = async () => {
102
+ await saveCurrentAnswer(currentSlide);
103
+ if (isLastSlide) {
104
+ await completeOnboarding();
105
+ } else {
106
+ goToNext();
107
+ const nextSlide = OnboardingSlideService.getSlideAtIndex(
108
+ filteredSlides,
109
+ currentIndex + 1,
110
+ );
111
+ loadAnswerForSlide(nextSlide);
112
+ }
113
+ };
114
+
115
+ // Handle previous slide
116
+ const handlePrevious = () => {
117
+ goToPrevious();
118
+ const prevSlide = OnboardingSlideService.getSlideAtIndex(
119
+ filteredSlides,
120
+ currentIndex - 1,
121
+ );
122
+ loadAnswerForSlide(prevSlide);
123
+ };
124
+
125
+ // Handle skip
126
+ const handleSkip = async () => {
127
+ await skipOnboarding();
128
+ };
129
+
130
+ // Check if gradient should be used
131
+ const useGradient = shouldUseGradient(currentSlide, globalUseGradient);
132
+
133
+ // Validate answer using service
134
+ const isAnswerValid = useMemo(() => {
135
+ if (!currentSlide?.question) {
136
+ return true;
137
+ }
138
+ return OnboardingValidationService.validateAnswer(
139
+ currentSlide.question,
140
+ currentAnswer,
141
+ );
142
+ }, [currentSlide, currentAnswer]);
143
+
144
+ // Container style
145
+ const containerStyle = useMemo(
146
+ () => [
147
+ {
148
+ paddingTop: insets.top,
149
+ backgroundColor: useGradient ? "transparent" : tokens.colors.backgroundPrimary,
150
+ },
151
+ ],
152
+ [insets.top, useGradient, tokens.colors.backgroundPrimary],
153
+ );
154
+
155
+ return {
156
+ filteredSlides,
157
+ currentSlide,
158
+ currentIndex,
159
+ isFirstSlide,
160
+ isLastSlide,
161
+ currentAnswer,
162
+ isAnswerValid,
163
+ useGradient,
164
+ containerStyle,
165
+ handleNext,
166
+ handlePrevious,
167
+ handleSkip,
168
+ setCurrentAnswer,
169
+ };
170
+ }
171
+
@@ -7,17 +7,10 @@
7
7
  * This component only handles UI coordination - no business logic
8
8
  */
9
9
 
10
- import React, { useMemo } from "react";
10
+ import React from "react";
11
11
  import { StyleSheet } from "react-native";
12
- import { useSafeAreaInsets } from "react-native-safe-area-context";
13
- import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
14
12
  import type { OnboardingOptions } from "../../domain/entities/OnboardingOptions";
15
- import { useOnboardingNavigation } from "../../infrastructure/hooks/useOnboardingNavigation";
16
- import { useOnboardingAnswers } from "../../infrastructure/hooks/useOnboardingAnswers";
17
- import { useOnboardingStore } from "../../infrastructure/storage/OnboardingStore";
18
- import { OnboardingSlideService } from "../../infrastructure/services/OnboardingSlideService";
19
- import { OnboardingValidationService } from "../../infrastructure/services/OnboardingValidationService";
20
- import { shouldUseGradient } from "../../infrastructure/utils/gradientUtils";
13
+ import { useOnboardingScreenState } from "../hooks/useOnboardingScreenState";
21
14
  import { OnboardingScreenContent } from "../components/OnboardingScreenContent";
22
15
 
23
16
  export interface OnboardingScreenProps extends OnboardingOptions {
@@ -99,116 +92,31 @@ export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
99
92
  useGradient: globalUseGradient = false,
100
93
  SliderComponent,
101
94
  }) => {
102
- const insets = useSafeAreaInsets();
103
- const tokens = useAppDesignTokens();
104
- const onboardingStore = useOnboardingStore();
105
-
106
- // Filter slides using service
107
- const filteredSlides = useMemo(() => {
108
- if (!slides || !Array.isArray(slides) || slides.length === 0) {
109
- return [];
110
- }
111
- const userData = onboardingStore.getUserData();
112
- return OnboardingSlideService.filterSlides(slides, userData);
113
- }, [slides, onboardingStore]);
114
-
115
- // Navigation hook
116
95
  const {
96
+ filteredSlides,
97
+ currentSlide,
117
98
  currentIndex,
118
- goToNext,
119
- goToPrevious,
120
- complete: completeOnboarding,
121
- skip: skipOnboarding,
122
- isLastSlide,
123
99
  isFirstSlide,
124
- } = useOnboardingNavigation(
125
- filteredSlides.length,
126
- async () => {
127
- await onboardingStore.complete(storageKey);
128
- if (onComplete) {
129
- await onComplete();
130
- }
131
- },
132
- async () => {
133
- await onboardingStore.skip(storageKey);
134
- if (onSkip) {
135
- await onSkip();
136
- }
137
- },
138
- );
139
-
140
- // Get current slide
141
- const currentSlide = useMemo(
142
- () => OnboardingSlideService.getSlideAtIndex(filteredSlides, currentIndex),
143
- [filteredSlides, currentIndex],
144
- );
145
-
146
- // Answer management hook
147
- const {
100
+ isLastSlide,
148
101
  currentAnswer,
102
+ isAnswerValid,
103
+ useGradient,
104
+ containerStyle,
105
+ handleNext,
106
+ handlePrevious,
107
+ handleSkip,
149
108
  setCurrentAnswer,
150
- loadAnswerForSlide,
151
- saveCurrentAnswer,
152
- } = useOnboardingAnswers(currentSlide);
153
-
154
- // Handle next slide
155
- const handleNext = async () => {
156
- await saveCurrentAnswer(currentSlide);
157
- if (isLastSlide) {
158
- await completeOnboarding();
159
- } else {
160
- goToNext();
161
- const nextSlide = OnboardingSlideService.getSlideAtIndex(
162
- filteredSlides,
163
- currentIndex + 1,
164
- );
165
- loadAnswerForSlide(nextSlide);
166
- }
167
- };
168
-
169
- // Handle previous slide
170
- const handlePrevious = () => {
171
- goToPrevious();
172
- const prevSlide = OnboardingSlideService.getSlideAtIndex(
173
- filteredSlides,
174
- currentIndex - 1,
175
- );
176
- loadAnswerForSlide(prevSlide);
177
- };
178
-
179
- // Handle skip
180
- const handleSkip = async () => {
181
- await skipOnboarding();
182
- };
183
-
184
- // Check if gradient should be used
185
- const useGradient = shouldUseGradient(currentSlide, globalUseGradient);
186
-
187
- // Validate answer using service
188
- const isAnswerValid = useMemo(() => {
189
- if (!currentSlide?.question) {
190
- return true;
191
- }
192
- return OnboardingValidationService.validateAnswer(
193
- currentSlide.question,
194
- currentAnswer,
195
- );
196
- }, [currentSlide, currentAnswer]);
197
-
198
- const containerStyle = useMemo(
199
- () => [
200
- styles.container,
201
- {
202
- paddingTop: insets.top,
203
- backgroundColor: useGradient ? "transparent" : tokens.colors.backgroundPrimary,
204
- },
205
- ],
206
- [insets.top, useGradient, tokens.colors.backgroundPrimary],
207
- );
109
+ } = useOnboardingScreenState({
110
+ slides,
111
+ storageKey,
112
+ onComplete,
113
+ onSkip,
114
+ globalUseGradient,
115
+ });
208
116
 
209
117
  return (
210
118
  <OnboardingScreenContent
211
- containerStyle={containerStyle}
119
+ containerStyle={[styles.container, containerStyle]}
212
120
  useGradient={useGradient}
213
121
  currentSlide={currentSlide}
214
122
  isFirstSlide={isFirstSlide}