@@ -3,8 +3,8 @@ import { FlashList } from '@shopify/flash-list';
33import { useRouter } from 'expo-router' ;
44import { Bell , ChevronRight , MapPin , Users } from 'lucide-react-native' ;
55import { useColorScheme } from 'nativewind' ;
6- import React , { useCallback , useEffect , useRef , useState } from 'react' ;
7- import { Dimensions , Image } from 'react-native' ;
6+ import React , { useCallback , useMemo , useRef , useState } from 'react' ;
7+ import { Dimensions , Image , StyleSheet } from 'react-native' ;
88import Animated , { useAnimatedStyle , useSharedValue , withTiming } from 'react-native-reanimated' ;
99
1010import { FocusAwareStatusBar , SafeAreaView , View } from '@/components/ui' ;
@@ -17,6 +17,50 @@ import { useIsFirstTime } from '@/lib/storage';
1717
1818const { width} = Dimensions . get ( 'window' ) ;
1919
20+ // Color constants
21+ const COLORS = {
22+ primary :'#FF7B1A' ,
23+ text :{
24+ light :{
25+ primary :'#1f2937' ,
26+ secondary :'#6b7280' ,
27+ } ,
28+ dark :{
29+ primary :'#ffffff' ,
30+ secondary :'#d1d5db' ,
31+ } ,
32+ } ,
33+ } ;
34+
35+ // Static styles
36+ const styles = StyleSheet . create ( {
37+ onboardingContainer :{
38+ height :400 ,
39+ justifyContent :'center' ,
40+ alignItems :'center' ,
41+ paddingHorizontal :32 ,
42+ } ,
43+ iconContainer :{
44+ marginBottom :32 ,
45+ alignItems :'center' ,
46+ } ,
47+ title :{
48+ fontSize :28 ,
49+ fontWeight :'bold' ,
50+ textAlign :'center' ,
51+ marginBottom :16 ,
52+ } ,
53+ description :{
54+ fontSize :16 ,
55+ textAlign :'center' ,
56+ lineHeight :24 ,
57+ } ,
58+ flexContainer :{
59+ flex :1 ,
60+ minHeight :400 ,
61+ } ,
62+ } ) ;
63+
2064type OnboardingItemProps = {
2165title :string ;
2266description :string ;
@@ -27,26 +71,37 @@ const onboardingData: OnboardingItemProps[] = [
2771{
2872title :'Resgrid Responder' ,
2973description :'Manage your status, staffing, and interact with your organization in real-time' ,
30- icon :< MapPin size = { 80 } color = "#FF7B1A" /> ,
74+ icon :< MapPin size = { 80 } color = { COLORS . primary } /> ,
3175} ,
3276{
3377title :'Instant Notifications' ,
3478description :'Receive immediate alerts for emergencies and important updates from your department' ,
35- icon :< Bell size = { 80 } color = "#FF7B1A" /> ,
79+ icon :< Bell size = { 80 } color = { COLORS . primary } /> ,
3680} ,
3781{
3882title :'Interact with Calls' ,
3983description :'Seamlessly view call information and interact with your team members for efficient emergency response' ,
40- icon :< Users size = { 80 } color = "#FF7B1A" /> ,
84+ icon :< Users size = { 80 } color = { COLORS . primary } /> ,
4185} ,
4286] ;
4387
4488const OnboardingItem :React . FC < OnboardingItemProps > = ( { title, description, icon} ) => {
89+ const { colorScheme} = useColorScheme ( ) ;
90+
91+ // Compute dynamic colors once using useMemo
92+ const textColors = useMemo ( ( ) => {
93+ const isDark = colorScheme === 'dark' ;
94+ return {
95+ title :isDark ?COLORS . text . dark . primary :COLORS . text . light . primary ,
96+ description :isDark ?COLORS . text . dark . secondary :COLORS . text . light . secondary ,
97+ } ;
98+ } , [ colorScheme ] ) ;
99+
45100return (
46- < View className = "w-full flex-1 items-center justify-center px-8" style = { { width} } >
47- < View className = "mb-8 items-center justify-center" > { icon } </ View >
48- < Text className = "mb-4 text-center text-3xl font-bold" > { title } </ Text >
49- < Text className = "text-center text-lg text-gray-600" > { description } </ Text >
101+ < View style = { [ styles . onboardingContainer , { width} ] } >
102+ < View style = { styles . iconContainer } > { icon } </ View >
103+ < Text style = { [ styles . title , { color : textColors . title } ] } > { title } </ Text >
104+ < Text style = { [ styles . description , { color : textColors . description } ] } > { description } </ Text >
50105</ View >
51106) ;
52107} ;
@@ -140,19 +195,23 @@ export default function Onboarding() {
140195< Image style = { { width :'96%' } } resizeMode = "contain" source = { colorScheme === 'dark' ?require ( '@assets/images/Resgrid_JustText_White.png' ) :require ( '@assets/images/Resgrid_JustText.png' ) } />
141196</ View >
142197
143- < FlashList
144- ref = { flatListRef }
145- data = { onboardingData }
146- renderItem = { ( { item} :{ item :OnboardingItemProps } ) => < OnboardingItem { ...item } /> }
147- horizontal
148- showsHorizontalScrollIndicator = { false }
149- pagingEnabled
150- bounces = { false }
151- keyExtractor = { ( item :OnboardingItemProps ) => item . title }
152- onScroll = { handleScroll }
153- scrollEventThrottle = { 16 }
154- testID = "onboarding-flatlist"
155- />
198+ < View style = { styles . flexContainer } >
199+ < FlashList
200+ ref = { flatListRef }
201+ data = { onboardingData }
202+ renderItem = { ( { item} :{ item :OnboardingItemProps } ) => < OnboardingItem { ...item } /> }
203+ horizontal
204+ showsHorizontalScrollIndicator = { false }
205+ pagingEnabled
206+ bounces = { false }
207+ keyExtractor = { ( item :OnboardingItemProps ) => item . title }
208+ onScroll = { handleScroll }
209+ scrollEventThrottle = { 16 }
210+ estimatedItemSize = { width }
211+ getItemType = { ( ) => 'onboarding-item' }
212+ testID = "onboarding-flatlist"
213+ />
214+ </ View >
156215
157216< Pagination currentIndex = { currentIndex } length = { onboardingData . length } />
158217