import React, { useState, useEffect, useCallback, useRef } from 'react'; import { View, Text, FlatList, TouchableOpacity, Animated, ScrollView, } from 'react-native'; import { useNavigation, useRoute, RouteProp, CompositeNavigationProp, } from '@react-navigation/native'; import { BottomTabNavigationProp } from '@react-navigation/bottom-tabs'; import { StackNavigationProp } from '@react-navigation/stack'; import { getStyles } from './searchScreen.styles'; import { SearchBar, ProductCard } from '@components'; import { useAppTheme } from '@theme'; import { AppStackParamList } from '../../../navigation/appStack'; import { MainTabParamList } from '../../../navigation/mainTabNavigator'; import { useAppDispatch, useAppSelector } from '@store'; import { getProductsByCategoryThunk } from './thunk'; import { Products } from '@interfaces'; // ─── Types ─────────────────────────────────────────────────────────────────── type NavProp = CompositeNavigationProp< BottomTabNavigationProp, StackNavigationProp >; type SearchRouteProp = RouteProp; // ─── Helpers ───────────────────────────────────────────────────────────────── const getCategoryEmoji = (name: string): string => { const n = name.toLowerCase(); if (n.includes('food')) return '🍔'; if (n.includes('grocer')) return '🛒'; if (n.includes('pharma') || n.includes('medic')) return '💊'; if (n.includes('meat')) return '🥩'; if (n.includes('flower')) return '💐'; if (n.includes('electronic')) return '📱'; if (n.includes('cloth') || n.includes('fashion')) return '👗'; if (n.includes('bakery') || n.includes('cake')) return '🎂'; return '📦'; }; // ─── Component ─────────────────────────────────────────────────────────────── export const SearchScreen: React.FC = () => { const { colors } = useAppTheme(); const styles = getStyles(colors); const navigation = useNavigation(); const route = useRoute(); const dispatch = useAppDispatch(); // Redux state const { products, isLoading, error } = useAppSelector( state => state.search, ); const { categories } = useAppSelector(state => state.home); // Route params (set when navigating from HomeScreen) const routeCategoryId = route.params?.categoryId; const routeCategoryName = route.params?.categoryName; // Local state const [activeCategoryId, setActiveCategoryId] = useState( routeCategoryId, ); const [searchQuery, setSearchQuery] = useState(''); // Skeleton shimmer animation const shimmer = useRef(new Animated.Value(0)).current; useEffect(() => { if (isLoading) { Animated.loop( Animated.sequence([ Animated.timing(shimmer, { toValue: 1, duration: 900, useNativeDriver: true, }), Animated.timing(shimmer, { toValue: 0, duration: 900, useNativeDriver: true, }), ]), ).start(); } else { shimmer.stopAnimation(); } }, [isLoading, shimmer]); // Sync when route params change (e.g. user taps a different chip on HomeScreen) useEffect(() => { setActiveCategoryId(routeCategoryId); setSearchQuery(''); }, [routeCategoryId]); // Fetch products whenever active category changes useEffect(() => { dispatch(getProductsByCategoryThunk(activeCategoryId)); }, [activeCategoryId, dispatch]); // Client-side text filter on top of the fetched product set const filteredProducts: Products[] = products.filter(p => searchQuery.trim().length === 0 ? true : p.name.toLowerCase().includes(searchQuery.toLowerCase()) || (p.brand ?? '').toLowerCase().includes(searchQuery.toLowerCase()), ); // Category chips: prepend "All" const allChip = { id: 'all' as const, name: 'All', slug: 'all', imageUrl: null, isActive: true, parentId: null, }; const chipCategories = [allChip, ...categories]; const headerCategoryName = activeCategoryId === undefined || activeCategoryId === 'all' ? 'All Products' : routeCategoryName ?? 'Products'; // ─── Sub-renders ─────────────────────────────────────────────────────────── const shimmerOpacity = shimmer.interpolate({ inputRange: [0, 1], outputRange: [0.4, 1], }); const renderSkeletons = () => ( {[0, 1, 2, 3].map(i => ( ))} ); const renderEmpty = useCallback(() => { if (isLoading) return null; return ( 🔍 No products found {searchQuery.trim().length > 0 ? `No results for "${searchQuery}" in ${headerCategoryName}` : `No products available in ${headerCategoryName} right now`} ); }, [isLoading, searchQuery, headerCategoryName, styles]); const renderError = () => ( ⚠️ {error} ); const renderProductItem = useCallback( ({ item }: { item: Products }) => ( navigation.navigate('ProviderDetailsScreen', { providerId: item.id, providerName: item.name, }) } /> ), [navigation], ); // ─── Render ──────────────────────────────────────────────────────────────── return ( {/* Header */} navigation.goBack()} > {headerCategoryName} {!isLoading && ( {filteredProducts.length} item{filteredProducts.length !== 1 ? 's' : ''} )} {/* Search Bar */} {/* Category chips */} {categories.length > 0 && ( {chipCategories.map(cat => { const isActive = cat.id === 'all' ? activeCategoryId === undefined || activeCategoryId === 'all' : activeCategoryId === cat.id; const emoji = cat.id === 'all' ? '🏠' : getCategoryEmoji(cat.name); return ( { setSearchQuery(''); setActiveCategoryId(cat.id === 'all' ? undefined : cat.id); }} > {emoji} {cat.name} ); })} )} {/* Error */} {error && !isLoading && renderError()} {/* Loading skeleton */} {isLoading && renderSkeletons()} {/* Product grid */} {!isLoading && !error && ( item.id} numColumns={2} columnWrapperStyle={styles.row} renderItem={renderProductItem} ListEmptyComponent={renderEmpty} contentContainerStyle={styles.gridContent} showsVerticalScrollIndicator={false} initialNumToRender={8} maxToRenderPerBatch={10} windowSize={5} /> )} ); };