Files
Galaxies-shoppingapp/app/index.tsx
T
Dennis Hundertmark b24544f115 Initial commit
Generated by create-expo-app 3.2.0.
2025-05-20 15:23:02 +02:00

146 lines
3.7 KiB
TypeScript

import ProductCard from "@/components/ProductCart";
import { fetchProducts, getCategories, Product } from "@/utils/api";
import { COLORS } from "@/utils/colors";
import { FlashList } from "@shopify/flash-list";
import { useQuery } from "@tanstack/react-query";
import { Stack } from "expo-router";
import React, { useCallback, useMemo } from "react";
import {
Pressable,
ScrollView,
StyleSheet,
View,
Text,
Platform,
} from "react-native";
import { useHeaderHeight } from "@react-navigation/elements";
import { ProductShimmerGrid } from "@/components/ProductListShimmer";
export default function Index() {
const [selectedCategory, setSelectedCategory] = React.useState("all");
const [searchQuery, setSearchQuery] = React.useState("");
const headerHeight = useHeaderHeight();
const {
data: products,
refetch,
isLoading,
isRefetching,
} = useQuery({
queryKey: ["products"],
queryFn: fetchProducts,
});
const { data: categories = [] } = useQuery({
queryKey: ["categories"],
queryFn: getCategories,
});
const renderProduct = useCallback(
({ item }: { item: Product }) => <ProductCard product={item} />,
[]
);
const allCategories = ["all", ...categories];
const filteredProducts = useMemo(() => {
return products?.filter((product) => {
if (selectedCategory !== "all") {
return product.category === selectedCategory;
}
return product.title.toLowerCase().includes(searchQuery.toLowerCase());
});
}, [products, selectedCategory, searchQuery]);
return (
<View
style={[
styles.container,
{ marginTop: Platform.select({ ios: headerHeight, android: 0 }) },
]}
>
<Stack.Screen
options={{
headerSearchBarOptions: {
onChangeText: (e) => setSearchQuery(e.nativeEvent.text),
},
}}
/>
<View style={styles.categoryContainer}>
<ScrollView
style={styles.categoryScrollView}
horizontal
showsHorizontalScrollIndicator={false}
>
{allCategories.map((category) => (
<Pressable
key={category}
onPress={() => setSelectedCategory(category)}
style={[
styles.categoryButton,
selectedCategory === category && styles.selectedCategoryButton,
]}
>
<Text
style={[
styles.categoryText,
selectedCategory === category && styles.selectedCategoryText,
]}
>
{category.charAt(0).toUpperCase() + category.slice(1)}
</Text>
</Pressable>
))}
</ScrollView>
</View>
{isLoading ? (
<ProductShimmerGrid />
) : (
<FlashList
data={filteredProducts}
renderItem={renderProduct}
estimatedItemSize={200}
numColumns={2}
contentContainerStyle={{ padding: 8 }}
keyExtractor={(item) => item.id.toString()}
onRefresh={refetch}
refreshing={isRefetching}
/>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
},
categoryContainer: {
height: 60,
zIndex: 1,
boxShadow: "0 0 10px rgba(0, 0, 0, 0.1)",
},
categoryScrollView: {
padding: 12,
},
categoryButton: {
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
marginHorizontal: 4,
backgroundColor: COLORS.lightGray,
alignSelf: "center",
},
categoryText: {
fontSize: 14,
color: COLORS.white,
},
selectedCategoryButton: {
backgroundColor: COLORS.primary,
},
selectedCategoryText: {
color: COLORS.white,
},
});