Skip to content

Commit 0f2afa1

Browse files
committed
add shop
1 parent dfe94f1 commit 0f2afa1

File tree

6 files changed

+219
-13
lines changed

6 files changed

+219
-13
lines changed

app/(tabs)/shop.tsx

+56-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,58 @@
1-
import { SafeAreaView, Text, View } from "react-native";
2-
3-
function Shop() {
4-
return <SafeAreaView>
5-
<View>
6-
<Text>Shop</Text>
7-
</View>
8-
</SafeAreaView>
1+
import {ImageBackground, ScrollView, Text, TextInput, View} from "react-native";
2+
import SectionText from "@/components/HomePage/SectionText";
3+
import ScrollCard from "@/components/HomePage/ScrollCard";
4+
import React, {useEffect, useState} from "react";
5+
import {collection, getDocs, query} from "firebase/firestore";
6+
import {FIREBASE_DB} from "@/firebase.config";
7+
import {shuffleArray} from "@/functions/shuffleArray";
8+
import {Ionicons} from "@expo/vector-icons";
9+
10+
const Shop = () => {
11+
const [shopItems, setShopItems] = useState<ShopItem[]>([]);
12+
useEffect(() => {
13+
14+
const fetchItems = async () => {
15+
const shopCollectionReference = collection(FIREBASE_DB, "shop");
16+
const shopQuery = query(shopCollectionReference);
17+
const querySnapshot = await getDocs(shopQuery);
18+
19+
const fetchedTips: ShopItem[] = querySnapshot.docs.map((doc) => ({
20+
id: doc.id,
21+
...doc.data() as ShopItemData
22+
}));
23+
24+
setShopItems(fetchedTips);
25+
}
26+
fetchItems();
27+
}, []);
28+
return <View className="mt-8">
29+
<ScrollView bounces={true} showsVerticalScrollIndicator={false}>
30+
<View style={{ overflow: 'hidden', borderBottomLeftRadius: 35, borderBottomRightRadius: 35 }}>
31+
<ImageBackground
32+
source={require('@/assets/images/banner-image.png')}
33+
className="bg-[#63784f] pt-[85px] pb-14 px-8 flex gap-2"
34+
style={{ borderBottomLeftRadius: 35, borderBottomRightRadius: 35 }}
35+
>
36+
<Text className="text-4xl tracking-widest font-bold text-secondary">
37+
Treebie
38+
</Text>
39+
<Text className="text-3xl tracking-widest font-bold text-secondary">
40+
Ucz się, działaj, zgarniaj
41+
</Text>
42+
43+
</ImageBackground>
44+
</View>
45+
<SectionText title='Sklep' containerStyles='px-8'/>
46+
47+
<ScrollView className="flex-row pl-8" horizontal showsHorizontalScrollIndicator={false} decelerationRate={0}
48+
snapToInterval={200} snapToAlignment={"start"} contentContainerClassName='pr-8'>
49+
{shopItems.slice(0, 5).map((item) => {
50+
return <ScrollCard key={`all-${item.id}`} id={item.id} imageName='logo-icon-new.png'
51+
title={item.name} containerStyle='mr-4' routeBase={"shop"} />
52+
})}
53+
</ScrollView>
54+
</ScrollView>
55+
</View>;
956
}
57+
1058
export default Shop;

app/_layout.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export default function RootLayout() {
4242
<Stack>
4343
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
4444
<Stack.Screen name="tip/[id]/index" options={{ headerShown: false }} />
45+
<Stack.Screen name="shop/[id]/index" options={{ headerShown: false }} />
4546
<Stack.Screen name="(auth)/login" options={{ headerShown: false }} />
4647
<Stack.Screen name="(auth)/signup" options={{ headerShown: false }} />
4748
<Stack.Screen name="ai/index" options={{ headerShown: false }} />

app/shop/[id]/index.tsx

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/* eslint-disable @typescript-eslint/no-require-imports */
2+
import { Image, ImageBackground, ScrollView, Text, TouchableOpacity, View } from "react-native";
3+
import CustomButton from "@/components/elements/CustomButton";
4+
import { Ionicons } from "@expo/vector-icons";
5+
import { Href, router, useLocalSearchParams } from "expo-router";
6+
import { useEffect, useState } from "react";
7+
import { addDoc, collection, deleteDoc, doc, getDoc, getDocs, query, where } from "firebase/firestore";
8+
import { FIREBASE_DB } from "@/firebase.config";
9+
import TipListElement from "@/components/TipsPage/TipListElement";
10+
import React from "react";
11+
import { ThemedText } from "@/components/ThemedText";
12+
import { ThemedView } from "@/components/ThemedView";
13+
14+
type TipFields = {
15+
title: string;
16+
description: string;
17+
list: string[];
18+
};
19+
20+
const Index = () => {
21+
const local = useLocalSearchParams();
22+
let redirect = "/shop"
23+
if (local.redirect) {
24+
redirect = local.redirect === "liked" ? "/shop?allType=liked" : "/shop?allType=all";
25+
}
26+
const [liked, setLiked] = useState(false);
27+
const [item, setItem] = useState<ShopItem | undefined>(undefined);
28+
const USERID = "1";
29+
30+
useEffect(() => {
31+
const getData = async (id: string) => {
32+
try {
33+
const docRef = doc(FIREBASE_DB, "shop", id);
34+
const res = await getDoc(docRef);
35+
if (res.exists()) {
36+
setItem(res.data() as ShopItem);
37+
} else {
38+
console.warn("No such document!");
39+
}
40+
} catch (error) {
41+
console.error("Error fetching document: ", error);
42+
router.replace("/(tabs)/");
43+
}
44+
};
45+
46+
47+
if (local.id) {
48+
getData(local.id.toString());
49+
}
50+
}, [local.id]);
51+
52+
return (
53+
<ThemedView className="flex-1">
54+
<ScrollView showsVerticalScrollIndicator={false} bounces={false}>
55+
<View style={{ overflow: "hidden", borderBottomLeftRadius: 35, borderBottomRightRadius: 35 }}>
56+
<ImageBackground
57+
source={require("@/assets/images/banner-image.png")}
58+
className="bg-[#63784f] pt-[60px] pb-14 px-8 flex gap-2"
59+
style={{ borderBottomLeftRadius: 35, borderBottomRightRadius: 35 }}
60+
>
61+
<View className="flex-row justify-between">
62+
<TouchableOpacity
63+
className="p-4 rounded-2xl bg-[#798156]"
64+
onPress={() => router.replace(redirect as Href)}
65+
>
66+
<Ionicons name="chevron-back-outline" size={18} color="white" />
67+
</TouchableOpacity>
68+
</View>
69+
70+
<Image source={require("@/assets/images/logo-icon-new.png")} className="w-full h-64" />
71+
</ImageBackground>
72+
</View>
73+
74+
<View className="mt-8 px-8">
75+
<ThemedText className="text-3xl tracking-widest font-bold">
76+
{item?.name}
77+
</ThemedText>
78+
</View>
79+
</ScrollView>
80+
</ThemedView>
81+
);
82+
};
83+
84+
export default Index;

app/shop/index.tsx

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import {SafeAreaView, ScrollView, Text, TouchableOpacity, View} from "react-native";
2+
import {router, useLocalSearchParams} from "expo-router";
3+
import {collection, getDocs, query} from "firebase/firestore";
4+
import {FIREBASE_DB} from "@/firebase.config";
5+
import {shuffleArray} from "@/functions/shuffleArray";
6+
import React, {useEffect, useState} from "react";
7+
import {Ionicons} from "@expo/vector-icons";
8+
import {ThemedText} from "@/components/ThemedText";
9+
import ScrollCard from "@/components/HomePage/ScrollCard";
10+
11+
function Index() {
12+
const [shopItems, setShopItems] = useState<ShopItem[]>([]);
13+
const [displayShopItems, setDisplayShopItems] = useState<ShopItem[]>([]);
14+
useEffect(() => {
15+
16+
const fetchItems = async () => {
17+
const shopCollectionReference = collection(FIREBASE_DB, "shop");
18+
const shopQuery = query(shopCollectionReference);
19+
const querySnapshot = await getDocs(shopQuery);
20+
21+
const fetchedTips: ShopItem[] = querySnapshot.docs.map((doc) => ({
22+
id: doc.id,
23+
...doc.data() as ShopItemData
24+
}));
25+
26+
setShopItems(fetchedTips);
27+
setDisplayShopItems(shuffleArray([...fetchedTips]).slice(0, 20));
28+
}
29+
fetchItems();
30+
}, []);
31+
32+
function displayItems(): JSX.Element[] {
33+
return displayShopItems
34+
.reduce<ShopItem[][]>((rows, card, index) => {
35+
if (index % 2 === 0) rows.push([]);
36+
rows[rows.length - 1].push(card);
37+
return rows;
38+
}, [])
39+
.map((row, rowIndex) => (
40+
<View key={rowIndex} className="flex-row justify-between mt-7">
41+
{row.map((card) => (
42+
<ScrollCard key={card.id} id={card.id}
43+
imageName={card.image} title={card.name}/>
44+
))}
45+
</View>
46+
));
47+
}
48+
49+
return <SafeAreaView className="flex-1">
50+
<View className="flex-row items-center px-8 mt-2">
51+
<TouchableOpacity
52+
className="p-3 rounded-lg bg-[#798156] mr-3"
53+
onPress={() => router.replace("/shop")}
54+
>
55+
<Ionicons name="chevron-back-outline" size={18} color="white"/>
56+
</TouchableOpacity>
57+
</View>
58+
<ScrollView className="mt-8 px-8">
59+
{displayItems()}
60+
</ScrollView>
61+
</SafeAreaView>
62+
}
63+
64+
export default Index;

components/HomePage/ScrollCard.tsx

+6-4
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ interface ScrollCardProps {
88
title: string;
99
imageName: string;
1010
containerStyle?: string;
11-
redirect?: string
11+
redirect?: string;
12+
routeBase?: string;
1213
}
1314

14-
const ScrollCard = ({ id, title, imageName, containerStyle, redirect }: ScrollCardProps) => {
15+
const ScrollCard = ({ id, title, imageName, containerStyle, redirect, routeBase }: ScrollCardProps) => {
16+
routeBase ??= "tip";
1517
const handlePress = () => {
1618
const route: Href = redirect
17-
? (`/tip/${id}?redirect=${redirect}` as Href)
18-
: (`/tip/${id}` as Href);
19+
? (`/${routeBase}/${id}?redirect=${redirect}` as Href)
20+
: (`/${routeBase}/${id}` as Href);
1921
router.push(route);
2022
}
2123
return (

typing.d.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,14 @@ interface TipFields extends TipData {
2121
interface ChallengeFields extends ChallengeData {
2222
id: string;
2323
}
24-
24+
interface ShopItemData {
25+
name: string;
26+
price: number;
27+
image: string;
28+
}
29+
interface ShopItem extends ShopItemData {
30+
id: string;
31+
}
2532

2633
interface MessageType {
2734
message: string;

0 commit comments

Comments
 (0)