@@ -13,7 +13,6 @@ import {
1313 SafeAreaView ,
1414 Platform ,
1515 useColorScheme ,
16- Linking ,
1716} from 'react-native' ;
1817import { useRouter } from 'expo-router' ;
1918import {
@@ -23,7 +22,6 @@ import {
2322 Search ,
2423 Plus ,
2524 X ,
26- Check ,
2725 AlertCircle ,
2826 TrendingDown ,
2927 Layers ,
@@ -94,6 +92,9 @@ export default function HomeScreen() {
9492 const [ customBrand , setCustomBrand ] = useState ( '' ) ;
9593 const [ customCategory , setCustomCategory ] = useState ( 'ruj' ) ;
9694
95+ // Lazy loading state
96+ const [ visibleLimit , setVisibleLimit ] = useState ( 16 ) ;
97+
9798 // Load all simulated products
9899 const allProducts = useMemo ( ( ) => {
99100 return searchAndSimulateProducts ( '' , 'all' ) ;
@@ -227,8 +228,6 @@ export default function HomeScreen() {
227228 const inWatchlist = isInWishlist ( item . id ) ;
228229 const discountPercent = Math . abs ( Math . round ( item . change ) ) ;
229230 const isDiscounted = item . change < 0 ;
230-
231- // Calculate original price roughly if discounted
232231 const originalPrice = cheapest ? ( cheapest . price / ( 1 - discountPercent / 100 ) ) : 0 ;
233232
234233 return (
@@ -237,7 +236,6 @@ export default function HomeScreen() {
237236 onPress = { ( ) => router . push ( { pathname : '/product/[id]' , params : { id : item . id } } ) }
238237 style = { [ styles . horizontalCard , { backgroundColor : themeColors . backgroundElement , borderColor : themeColors . border } ] }
239238 >
240- { /* Wishlist toggle badge */ }
241239 < Pressable
242240 onPress = { ( ) => {
243241 if ( inWatchlist ) {
@@ -315,7 +313,6 @@ export default function HomeScreen() {
315313 onPress = { ( ) => router . push ( { pathname : '/product/[id]' , params : { id : item . id } } ) }
316314 style = { [ styles . gridCard , { backgroundColor : themeColors . backgroundElement , borderColor : themeColors . border } ] }
317315 >
318- { /* Wishlist button */ }
319316 < Pressable
320317 onPress = { ( ) => {
321318 if ( inWatchlist ) {
@@ -449,132 +446,145 @@ export default function HomeScreen() {
449446 ) }
450447 </ View >
451448
452- < ScrollView showsVerticalScrollIndicator = { false } contentContainerStyle = { { paddingBottom : BottomTabInset + Spacing . six } } >
453-
454- { /* Campaign Banner Carousel (Conditional) */ }
455- { ! hasSearch && selectedCategory === 'all' && (
456- < View style = { styles . bannerContainer } >
457- < ScrollView horizontal showsHorizontalScrollIndicator = { false } pagingEnabled snapToAlignment = "center" snapToInterval = { width - Spacing . six } contentContainerStyle = { styles . bannerScroll } >
458- { CAMPAIGNS . map ( item => (
459- < View key = { item . id } style = { [ styles . bannerCard , { backgroundColor : item . bg , borderColor : item . border } ] } >
460- < View style = { styles . bannerBadge } >
461- < Text style = { styles . bannerBadgeText } > { item . badge } </ Text >
462- </ View >
463- < Text style = { styles . bannerTitle } numberOfLines = { 1 } > { item . title } </ Text >
464- < Text style = { styles . bannerDesc } numberOfLines = { 2 } > { item . desc } </ Text >
465- </ View >
466- ) ) }
467- </ ScrollView >
468- </ View >
469- ) }
470-
471- { /* Category Circle Row */ }
472- < View style = { styles . categoriesContainer } >
473- < ScrollView horizontal showsHorizontalScrollIndicator = { false } contentContainerStyle = { styles . categoriesScroll } >
474- { CATEGORIES . map ( cat => {
475- const isSelected = selectedCategory === cat . id ;
476- return (
477- < Pressable
478- key = { cat . id }
479- onPress = { ( ) => setSelectedCategory ( cat . id ) }
480- style = { styles . categoryCircleItem }
481- >
482- < View
483- style = { [
484- styles . categoryCircle ,
485- {
486- backgroundColor : isSelected ? themeColors . primary : themeColors . backgroundElement ,
487- borderColor : isSelected ? themeColors . accent : themeColors . border
488- }
489- ] }
490- >
491- { getCategoryIcon ( cat . id , isSelected ? '#4A3538' : themeColors . text , 20 ) }
492- </ View >
493- < Text
494- style = { [
495- styles . categoryLabel ,
496- {
497- color : isSelected ? themeColors . text : themeColors . textSecondary ,
498- fontWeight : isSelected ? '700' : '500'
499- }
500- ] }
501- numberOfLines = { 1 }
502- >
503- { cat . name }
504- </ Text >
505- </ Pressable >
506- ) ;
507- } ) }
508- </ ScrollView >
509- </ View >
510-
511- { /* MAIN PRODUCT DISPLAYS */ }
512- { hasSearch || selectedCategory !== 'all' ? (
513- /* ACTIVE FILTER GRID DISPLAY */
514- < View style = { styles . sectionContainer } >
515- < View style = { styles . sectionHeaderRow } >
516- < Text style = { [ styles . sectionTitle , { color : themeColors . text } ] } > Arama ve Filtre Sonuçları</ Text >
517- < Text style = { [ styles . sectionSubtitleText , { color : themeColors . textSecondary } ] } >
518- { filteredProducts . length } ürün bulundu
519- </ Text >
520- </ View >
521- < View style = { styles . productGrid } >
522- { filteredProducts . map ( renderGridProductCard ) }
523- </ View >
524- { filteredProducts . length === 0 && (
525- < View style = { styles . emptyResults } >
526- < AlertCircle size = { 32 } color = { themeColors . textSecondary } />
527- < Text style = { [ styles . emptyResultsTitle , { color : themeColors . text } ] } > Eşleşen Ürün Bulunamadı</ Text >
528- < Text style = { [ styles . emptyResultsSubtitle , { color : themeColors . textSecondary } ] } >
529- Seçtiğiniz filtreye uygun kozmetik ürünü bulunmamaktadır.
530- </ Text >
449+ { /* Virtualized Main FlatList Grid */ }
450+ < FlatList
451+ data = { hasSearch || selectedCategory !== 'all' ? filteredProducts . slice ( 0 , visibleLimit ) : allProducts . slice ( 0 , visibleLimit ) }
452+ keyExtractor = { ( item ) => item . id }
453+ numColumns = { 2 }
454+ columnWrapperStyle = { styles . gridRow }
455+ contentContainerStyle = { [ styles . listContainer , { paddingBottom : BottomTabInset + Spacing . six } ] }
456+ showsVerticalScrollIndicator = { false }
457+ ListHeaderComponent = {
458+ < View >
459+ { /* Campaign Banner Carousel */ }
460+ { ! hasSearch && selectedCategory === 'all' && (
461+ < View style = { styles . bannerContainer } >
462+ < ScrollView horizontal showsHorizontalScrollIndicator = { false } pagingEnabled snapToAlignment = "center" snapToInterval = { width - Spacing . six } contentContainerStyle = { styles . bannerScroll } >
463+ { CAMPAIGNS . map ( item => (
464+ < View key = { item . id } style = { [ styles . bannerCard , { backgroundColor : item . bg , borderColor : item . border } ] } >
465+ < View style = { styles . bannerBadge } >
466+ < Text style = { styles . bannerBadgeText } > { item . badge } </ Text >
467+ </ View >
468+ < Text style = { styles . bannerTitle } numberOfLines = { 1 } > { item . title } </ Text >
469+ < Text style = { styles . bannerDesc } numberOfLines = { 2 } > { item . desc } </ Text >
470+ </ View >
471+ ) ) }
472+ </ ScrollView >
531473 </ View >
532474 ) }
533- </ View >
534- ) : (
535- /* DEFAULT CURATED HOMEPAGE SECTIONS */
536- < >
537- { /* Günün Fırsatları */ }
538- < View style = { styles . sectionContainer } >
539- < View style = { styles . sectionHeaderRow } >
540- < View style = { { flexDirection : 'row' , alignItems : 'center' , gap : 6 } } >
541- < Percent size = { 16 } color = { themeColors . success } />
542- < Text style = { [ styles . sectionTitle , { color : themeColors . text } ] } > Günün İndirim Fırsatları</ Text >
543- </ View >
544- </ View >
545- < ScrollView horizontal showsHorizontalScrollIndicator = { false } contentContainerStyle = { styles . horizontalScroll } >
546- { dealsOfDay . map ( renderHorizontalProductCard ) }
475+
476+ { /* Category Circle Row */ }
477+ < View style = { styles . categoriesContainer } >
478+ < ScrollView horizontal showsHorizontalScrollIndicator = { false } contentContainerStyle = { styles . categoriesScroll } >
479+ { CATEGORIES . map ( cat => {
480+ const isSelected = selectedCategory === cat . id ;
481+ return (
482+ < Pressable
483+ key = { cat . id }
484+ onPress = { ( ) => setSelectedCategory ( cat . id ) }
485+ style = { styles . categoryCircleItem }
486+ >
487+ < View
488+ style = { [
489+ styles . categoryCircle ,
490+ {
491+ backgroundColor : isSelected ? themeColors . primary : themeColors . backgroundElement ,
492+ borderColor : isSelected ? themeColors . accent : themeColors . border
493+ }
494+ ] }
495+ >
496+ { getCategoryIcon ( cat . id , isSelected ? '#4A3538' : themeColors . text , 20 ) }
497+ </ View >
498+ < Text
499+ style = { [
500+ styles . categoryLabel ,
501+ {
502+ color : isSelected ? themeColors . text : themeColors . textSecondary ,
503+ fontWeight : isSelected ? '700' : '500'
504+ }
505+ ] }
506+ numberOfLines = { 1 }
507+ >
508+ { cat . name }
509+ </ Text >
510+ </ Pressable >
511+ ) ;
512+ } ) }
547513 </ ScrollView >
548514 </ View >
549515
550- { /* Popüler Ürünler */ }
551- < View style = { styles . sectionContainer } >
552- < View style = { styles . sectionHeaderRow } >
553- < View style = { { flexDirection : 'row' , alignItems : 'center' , gap : 6 } } >
554- < ShoppingCart size = { 16 } color = { themeColors . accent } />
555- < Text style = { [ styles . sectionTitle , { color : themeColors . text } ] } > Borsada En Çok Arananlar</ Text >
516+ { /* Curated horizontal sections */ }
517+ { ! hasSearch && selectedCategory === 'all' ? (
518+ < >
519+ { /* Günün Fırsatları */ }
520+ < View style = { styles . sectionContainer } >
521+ < View style = { styles . sectionHeaderRow } >
522+ < View style = { { flexDirection : 'row' , alignItems : 'center' , gap : 6 } } >
523+ < Percent size = { 16 } color = { themeColors . success } />
524+ < Text style = { [ styles . sectionTitle , { color : themeColors . text } ] } > Günün İndirim Fırsatları</ Text >
525+ </ View >
526+ </ View >
527+ < ScrollView horizontal showsHorizontalScrollIndicator = { false } contentContainerStyle = { styles . horizontalScroll } >
528+ { dealsOfDay . map ( renderHorizontalProductCard ) }
529+ </ ScrollView >
556530 </ View >
557- </ View >
558- < ScrollView horizontal showsHorizontalScrollIndicator = { false } contentContainerStyle = { styles . horizontalScroll } >
559- { popularProducts . map ( renderHorizontalProductCard ) }
560- </ ScrollView >
561- </ View >
562531
563- { /* Sizin İçin Seçtiklerimiz (Tüm Ürünler Grid) */ }
564- < View style = { styles . sectionContainer } >
565- < View style = { styles . sectionHeaderRow } >
566- < View style = { { flexDirection : 'row' , alignItems : 'center' , gap : 6 } } >
567- < Sparkles size = { 16 } color = { themeColors . accent } />
568- < Text style = { [ styles . sectionTitle , { color : themeColors . text } ] } > Sizin İçin Seçtiklerimiz</ Text >
532+ { /* Popüler Ürünler */ }
533+ < View style = { styles . sectionContainer } >
534+ < View style = { styles . sectionHeaderRow } >
535+ < View style = { { flexDirection : 'row' , alignItems : 'center' , gap : 6 } } >
536+ < ShoppingCart size = { 16 } color = { themeColors . accent } />
537+ < Text style = { [ styles . sectionTitle , { color : themeColors . text } ] } > Borsada En Çok Arananlar</ Text >
538+ </ View >
539+ </ View >
540+ < ScrollView horizontal showsHorizontalScrollIndicator = { false } contentContainerStyle = { styles . horizontalScroll } >
541+ { popularProducts . map ( renderHorizontalProductCard ) }
542+ </ ScrollView >
543+ </ View >
544+
545+ { /* Sizin İçin Seçtiklerimiz Section Title */ }
546+ < View style = { styles . sectionContainer } >
547+ < View style = { styles . sectionHeaderRow } >
548+ < View style = { { flexDirection : 'row' , alignItems : 'center' , gap : 6 } } >
549+ < Sparkles size = { 16 } color = { themeColors . accent } />
550+ < Text style = { [ styles . sectionTitle , { color : themeColors . text } ] } > Sizin İçin Seçtiklerimiz</ Text >
551+ </ View >
552+ </ View >
553+ </ View >
554+ </ >
555+ ) : (
556+ /* Arama / Filtre Sonuçları Section Title */
557+ < View style = { styles . sectionContainer } >
558+ < View style = { styles . sectionHeaderRow } >
559+ < Text style = { [ styles . sectionTitle , { color : themeColors . text } ] } > Arama ve Filtre Sonuçları</ Text >
560+ < Text style = { [ styles . sectionSubtitleText , { color : themeColors . textSecondary } ] } >
561+ { filteredProducts . length } ürün bulundu
562+ </ Text >
569563 </ View >
570564 </ View >
571- < View style = { styles . productGrid } >
572- { allProducts . slice ( 0 , 16 ) . map ( renderGridProductCard ) }
573- </ View >
565+ ) }
566+ </ View >
567+ }
568+ renderItem = { ( { item } ) => renderGridProductCard ( item ) }
569+ onEndReached = { ( ) => {
570+ const totalLength = hasSearch || selectedCategory !== 'all' ? filteredProducts . length : allProducts . length ;
571+ if ( visibleLimit < totalLength ) {
572+ setVisibleLimit ( prev => prev + 12 ) ;
573+ }
574+ } }
575+ onEndReachedThreshold = { 0.5 }
576+ ListEmptyComponent = {
577+ ( hasSearch || selectedCategory !== 'all' ) && filteredProducts . length === 0 ? (
578+ < View style = { styles . emptyResults } >
579+ < AlertCircle size = { 32 } color = { themeColors . textSecondary } />
580+ < Text style = { [ styles . emptyResultsTitle , { color : themeColors . text } ] } > Eşleşen Ürün Bulunamadı</ Text >
581+ < Text style = { [ styles . emptyResultsSubtitle , { color : themeColors . textSecondary } ] } >
582+ Seçtiğiniz filtreye uygun kozmetik ürünü bulunmamaktadır.
583+ </ Text >
574584 </ View >
575- </ >
576- ) }
577- </ ScrollView >
585+ ) : null
586+ }
587+ / >
578588
579589 { /* QUICK PRODUCT ADD MODAL */ }
580590 < Modal
@@ -834,7 +844,10 @@ const styles = StyleSheet.create({
834844 borderWidth : 1 ,
835845 overflow : 'hidden' ,
836846 position : 'relative' ,
837- marginBottom : 10 ,
847+ } ,
848+ gridRow : {
849+ justifyContent : 'space-between' ,
850+ marginBottom : 8 ,
838851 } ,
839852 cardHeartIcon : {
840853 position : 'absolute' ,
@@ -952,6 +965,9 @@ const styles = StyleSheet.create({
952965 fontSize : 11 ,
953966 textAlign : 'center' ,
954967 } ,
968+ listContainer : {
969+ paddingHorizontal : Spacing . three ,
970+ } ,
955971 // Modal layout
956972 modalOverlay : {
957973 flex : 1 ,
0 commit comments