diff --git a/.eslintrc.js b/.eslintrc.js index ef87eb5..0b841ff 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ module.exports = { root: true, parser: "@typescript-eslint/parser", diff --git a/.gitignore b/.gitignore index 51a965f..be93998 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ web-build/ # The following patterns were generated by expo-cli expo-env.d.ts -# @end expo-cli \ No newline at end of file +# @end expo-cli +.idea/ \ No newline at end of file diff --git a/app/(auth)/login.tsx b/app/(auth)/login.tsx index 05134f2..62f2339 100644 --- a/app/(auth)/login.tsx +++ b/app/(auth)/login.tsx @@ -1,30 +1,37 @@ import {View, SafeAreaView, TouchableOpacity, Text} from 'react-native'; -import React from "react"; + +import React, { useState } from "react"; import LoginSignupHeader from "@/components/LoginSignupHeader"; import {CustomInput} from "@/components/elements/CustomInput"; import CustomButton from "@/components/elements/CustomButton"; -import { router } from "expo-router"; // Make sure to import the router object +import {router} from "expo-router"; import {LoginSignupFooter} from "@/components/elements/LoginSignupFooter"; + export default function Login() { + const [login, setLogin] = useState(""); + const [password, setPassword] = useState(""); + const handleSignupPress = () => { - router.push("/signup"); // Use the router object to navigate to the "/signup" route + router.push("/(tabs)/signup"); + } + const handleFormSubmit = () => { + } - return ( - - + + - Nie masz konta? Zarejestruj się! - - + Nie masz konta? + Zarejestruj się! + ); -} \ No newline at end of file +} diff --git a/app/(auth)/signup.tsx b/app/(auth)/signup.tsx index 28e1036..5147e3e 100644 --- a/app/(auth)/signup.tsx +++ b/app/(auth)/signup.tsx @@ -1,27 +1,32 @@ import {View, SafeAreaView, TouchableOpacity, Text} from 'react-native'; +import React, { useState } from "react"; import LoginSignupHeader from "@/components/LoginSignupHeader"; import {CustomInput} from "@/components/elements/CustomInput"; import CustomButton from "@/components/elements/CustomButton"; -import {router} from "expo-router"; +import {router, useNavigation} from "expo-router"; import {LoginSignupFooter} from "@/components/elements/LoginSignupFooter"; export default function Signup() { + const [login, setLogin] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); const handleSignupPress = () => { - router.push("/login") + router.push("/(tabs)/login"); } return ( - - - + + + - Posiadasz konto? Zaloguj się! + Posiadasz konto? + Zaloguj się! diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 712ac95..13a6398 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useRef, useState } from 'react'; +/* eslint-disable @typescript-eslint/no-require-imports */ +import React, { useEffect, useState } from 'react'; import { TextInput, ScrollView, Text, View, ImageBackground, Image } from 'react-native'; import { collection, getDocs, orderBy, query, where } from "firebase/firestore" import { FIREBASE_DB } from '@/firebase.config'; @@ -7,6 +8,7 @@ import SectionText from '@/components/HomePage/SectionText'; import ScrollCard from '@/components/HomePage/ScrollCard'; import { Ionicons } from '@expo/vector-icons'; import { router } from 'expo-router'; +import { ThemedView } from '@/components/ThemedView'; const HomeScreen = () => { const [allTips, setAllTips] = useState([]); @@ -88,7 +90,7 @@ const HomeScreen = () => { } return ( - + {/* Header Section */} @@ -155,7 +157,7 @@ const HomeScreen = () => { - + ); }; diff --git a/app/(tabs)/settings.tsx b/app/(tabs)/settings.tsx index 73f8b7f..66b4004 100644 --- a/app/(tabs)/settings.tsx +++ b/app/(tabs)/settings.tsx @@ -1,23 +1,42 @@ +/* eslint-disable @typescript-eslint/no-require-imports */ import {SafeAreaView, Switch, Text, View, TouchableHighlight, Appearance, Image} from 'react-native'; import React, {useEffect, useState} from "react"; import {ThemedText} from '@/components/ThemedText'; import {THEME_PREFERENCE_KEY} from "@/hooks/useColorScheme"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import CustomButton from '@/components/elements/CustomButton'; -import { router } from 'expo-router'; export default function Settings() { - const [darkTheme, changeDarkToggle] = useState(false); - const canChangeTheme = typeof (Appearance.setColorScheme) === "function"; + const [darkTheme, setDarkTheme] = useState(false); + + const canChangeTheme = typeof Appearance.setColorScheme === "function"; + + useEffect(() => { + const fetchThemePreference = async () => { + try { + const storedTheme = await AsyncStorage.getItem(THEME_PREFERENCE_KEY); + if (storedTheme) { + setDarkTheme(storedTheme === 'dark'); + } + } catch (error) { + console.error("Failed to fetch theme preference:", error); + } + }; + + fetchThemePreference(); + }, []); + useEffect(() => { - if (canChangeTheme) Appearance.setColorScheme(darkTheme ? "dark" : "light"); + if (canChangeTheme) { + Appearance.setColorScheme(darkTheme ? "dark" : "light"); + } AsyncStorage.setItem(THEME_PREFERENCE_KEY, darkTheme ? "dark" : "light"); }, [darkTheme]); + return ( - Ustawienia + Ustawienia Użyj ciemny motyw @@ -34,7 +53,7 @@ export default function Settings() { - router.push('/login')}/> - router.push('/signup')}/> diff --git a/app/(tabs)/shop.tsx b/app/(tabs)/shop.tsx index e751f92..eb7fd9b 100644 --- a/app/(tabs)/shop.tsx +++ b/app/(tabs)/shop.tsx @@ -1,10 +1,58 @@ -import { SafeAreaView, Text, View } from "react-native"; - -function Shop() { - return - - Shop - - +import {ImageBackground, ScrollView, Text, TextInput, View} from "react-native"; +import SectionText from "@/components/HomePage/SectionText"; +import ScrollCard from "@/components/HomePage/ScrollCard"; +import React, {useEffect, useState} from "react"; +import {collection, getDocs, query} from "firebase/firestore"; +import {FIREBASE_DB} from "@/firebase.config"; +import {shuffleArray} from "@/functions/shuffleArray"; +import {Ionicons} from "@expo/vector-icons"; + +const Shop = () => { + const [shopItems, setShopItems] = useState([]); + useEffect(() => { + + const fetchItems = async () => { + const shopCollectionReference = collection(FIREBASE_DB, "shop"); + const shopQuery = query(shopCollectionReference); + const querySnapshot = await getDocs(shopQuery); + + const fetchedTips: ShopItem[] = querySnapshot.docs.map((doc) => ({ + id: doc.id, + ...doc.data() as ShopItemData + })); + + setShopItems(fetchedTips); + } + fetchItems(); + }, []); + return + + + + + Treebie + + + Ucz się, działaj, zgarniaj + + + + + + + + {shopItems.slice(0, 5).map((item) => { + return + })} + + + ; } + export default Shop; \ No newline at end of file diff --git a/app/(tabs)/tree.tsx b/app/(tabs)/tree.tsx new file mode 100644 index 0000000..daeb9cb --- /dev/null +++ b/app/(tabs)/tree.tsx @@ -0,0 +1,21 @@ +import { Feather } from '@expo/vector-icons' +import React from 'react' +import { SafeAreaView, Text, TouchableOpacity, View } from 'react-native' + +const tree = () => { + return ( + + + + + + + + + + + + ) +} + +export default tree \ No newline at end of file diff --git a/app/(tabs)/welcome.tsx b/app/(tabs)/welcome.tsx new file mode 100644 index 0000000..292f8ac --- /dev/null +++ b/app/(tabs)/welcome.tsx @@ -0,0 +1,18 @@ +import {ThemedText} from "@/components/ThemedText"; +import {SafeAreaView} from "react-native-safe-area-context"; +import React from "react"; +import {ImageBackground} from "react-native"; + +export default function Welcome(props: {}) { + return + + Save + The + Planet + ; +} \ No newline at end of file diff --git a/app/+not-found.tsx b/app/+not-found.tsx index 963b04f..4103e59 100644 --- a/app/+not-found.tsx +++ b/app/+not-found.tsx @@ -3,6 +3,7 @@ import { StyleSheet } from 'react-native'; import { ThemedText } from '@/components/ThemedText'; import { ThemedView } from '@/components/ThemedView'; +import React from 'react'; export default function NotFoundScreen() { return ( diff --git a/app/_layout.tsx b/app/_layout.tsx index 8672935..8ad55c9 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -42,6 +42,7 @@ export default function RootLayout() { + diff --git a/app/ai/index.tsx b/app/ai/index.tsx index 7ed8c6b..cad359c 100644 --- a/app/ai/index.tsx +++ b/app/ai/index.tsx @@ -1,9 +1,11 @@ import Message from '@/components/Ai/Message'; import SkeletonMessage from '@/components/Ai/SkeletonMessage'; +import { ThemedText } from '@/components/ThemedText'; +import { useThemeColor } from '@/hooks/useThemeColor'; import { Feather, Ionicons } from '@expo/vector-icons'; import { router, useLocalSearchParams } from 'expo-router'; import React, { useEffect, useState } from 'react'; -import { KeyboardAvoidingView, Platform, SafeAreaView, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native'; +import { KeyboardAvoidingView, Platform, SafeAreaView, ScrollView, TextInput, TouchableOpacity, View } from 'react-native'; const Index = () => { const userMessageParam = (useLocalSearchParams().text as string) || null; @@ -78,8 +80,9 @@ const Index = () => { } }, [userMessageParam]); + const backgroundColor = useThemeColor({ light: "", dark: ""}, 'background'); return ( - + {/* Header */} { > - + Eco Chat - + {/* Scrollable Content */} @@ -106,8 +109,8 @@ const Index = () => { keyboardVerticalOffset={Platform.OS === 'ios' ? -20 : 0} style={{ position: 'absolute', bottom: 0, width: '100%' }} > - - + + { onChangeText={text => setInputValue(text)} editable={!isFetching} /> - handleSend()} /> + handleSend()} /> diff --git a/app/all/index.tsx b/app/all/index.tsx index 2f2aab0..3bd0b05 100644 --- a/app/all/index.tsx +++ b/app/all/index.tsx @@ -1,11 +1,13 @@ import ScrollCard from '@/components/HomePage/ScrollCard'; +import { ThemedText } from '@/components/ThemedText'; import { FIREBASE_DB } from '@/firebase.config'; import { shuffleArray } from '@/functions/shuffleArray'; +import { useThemeColor } from '@/hooks/useThemeColor'; import { Ionicons } from '@expo/vector-icons'; import { router, useLocalSearchParams } from 'expo-router'; import { collection, getDocs, orderBy, query, where } from 'firebase/firestore'; import React, { useEffect, useState } from 'react'; -import { SafeAreaView, ScrollView, Text, TouchableOpacity, View } from 'react-native'; +import { SafeAreaView, ScrollView, TouchableOpacity, View } from 'react-native'; const Index = () => { const { allType } = useLocalSearchParams(); @@ -75,8 +77,10 @@ const Index = () => { )); } + const backgroundColor = useThemeColor({ light: "", dark: ""}, 'background'); + return ( - + { > - + {allType === "liked" ? "Polubione" : "Wszystkie"} - + {displayTips()} diff --git a/app/shop/[id]/index.tsx b/app/shop/[id]/index.tsx new file mode 100644 index 0000000..a966f44 --- /dev/null +++ b/app/shop/[id]/index.tsx @@ -0,0 +1,84 @@ +/* eslint-disable @typescript-eslint/no-require-imports */ +import { Image, ImageBackground, ScrollView, Text, TouchableOpacity, View } from "react-native"; +import CustomButton from "@/components/elements/CustomButton"; +import { Ionicons } from "@expo/vector-icons"; +import { Href, router, useLocalSearchParams } from "expo-router"; +import { useEffect, useState } from "react"; +import { addDoc, collection, deleteDoc, doc, getDoc, getDocs, query, where } from "firebase/firestore"; +import { FIREBASE_DB } from "@/firebase.config"; +import TipListElement from "@/components/TipsPage/TipListElement"; +import React from "react"; +import { ThemedText } from "@/components/ThemedText"; +import { ThemedView } from "@/components/ThemedView"; + +type TipFields = { + title: string; + description: string; + list: string[]; +}; + +const Index = () => { + const local = useLocalSearchParams(); + let redirect = "/shop" + if (local.redirect) { + redirect = local.redirect === "liked" ? "/shop?allType=liked" : "/shop?allType=all"; + } + const [liked, setLiked] = useState(false); + const [item, setItem] = useState(undefined); + const USERID = "1"; + + useEffect(() => { + const getData = async (id: string) => { + try { + const docRef = doc(FIREBASE_DB, "shop", id); + const res = await getDoc(docRef); + if (res.exists()) { + setItem(res.data() as ShopItem); + } else { + console.warn("No such document!"); + } + } catch (error) { + console.error("Error fetching document: ", error); + router.replace("/(tabs)/"); + } + }; + + + if (local.id) { + getData(local.id.toString()); + } + }, [local.id]); + + return ( + + + + + + router.replace(redirect as Href)} + > + + + + + + + + + + + {item?.name} + + + + + ); +}; + +export default Index; diff --git a/app/shop/index.tsx b/app/shop/index.tsx new file mode 100644 index 0000000..d6f6831 --- /dev/null +++ b/app/shop/index.tsx @@ -0,0 +1,64 @@ +import {SafeAreaView, ScrollView, Text, TouchableOpacity, View} from "react-native"; +import {router, useLocalSearchParams} from "expo-router"; +import {collection, getDocs, query} from "firebase/firestore"; +import {FIREBASE_DB} from "@/firebase.config"; +import {shuffleArray} from "@/functions/shuffleArray"; +import React, {useEffect, useState} from "react"; +import {Ionicons} from "@expo/vector-icons"; +import {ThemedText} from "@/components/ThemedText"; +import ScrollCard from "@/components/HomePage/ScrollCard"; + +function Index() { + const [shopItems, setShopItems] = useState([]); + const [displayShopItems, setDisplayShopItems] = useState([]); + useEffect(() => { + + const fetchItems = async () => { + const shopCollectionReference = collection(FIREBASE_DB, "shop"); + const shopQuery = query(shopCollectionReference); + const querySnapshot = await getDocs(shopQuery); + + const fetchedTips: ShopItem[] = querySnapshot.docs.map((doc) => ({ + id: doc.id, + ...doc.data() as ShopItemData + })); + + setShopItems(fetchedTips); + setDisplayShopItems(shuffleArray([...fetchedTips]).slice(0, 20)); + } + fetchItems(); + }, []); + + function displayItems(): JSX.Element[] { + return displayShopItems + .reduce((rows, card, index) => { + if (index % 2 === 0) rows.push([]); + rows[rows.length - 1].push(card); + return rows; + }, []) + .map((row, rowIndex) => ( + + {row.map((card) => ( + + ))} + + )); + } + + return + + router.replace("/shop")} + > + + + + + {displayItems()} + + +} + +export default Index; \ No newline at end of file diff --git a/app/tip/[id]/index.tsx b/app/tip/[id]/index.tsx index ee0af78..694f23f 100644 --- a/app/tip/[id]/index.tsx +++ b/app/tip/[id]/index.tsx @@ -1,13 +1,15 @@ +/* eslint-disable @typescript-eslint/no-require-imports */ import { Image, ImageBackground, ScrollView, Text, TouchableOpacity, View } from "react-native"; import CustomButton from "@/components/elements/CustomButton"; import { Ionicons } from "@expo/vector-icons"; -import { router, useLocalSearchParams } from "expo-router"; - - +import { Href, router, useLocalSearchParams } from "expo-router"; import { useEffect, useState } from "react"; import { addDoc, collection, deleteDoc, doc, getDoc, getDocs, query, where } from "firebase/firestore"; import { FIREBASE_DB } from "@/firebase.config"; import TipListElement from "@/components/TipsPage/TipListElement"; +import React from "react"; +import { ThemedText } from "@/components/ThemedText"; +import { ThemedView } from "@/components/ThemedView"; type TipFields = { title: string; @@ -91,7 +93,7 @@ const Index = () => { }; return ( - + { router.replace(redirect)} + onPress={() => router.replace(redirect as Href)} > @@ -118,16 +119,16 @@ const Index = () => { - + {tip?.title} - - + + {tip?.description} - + {tip?.list && ( - Some Tips + Some Tips {tip.list.map((dot, index) => ( @@ -144,7 +145,7 @@ const Index = () => { buttonType="primary" textStyles="font-bold text-3xl" /> - + ); }; diff --git a/components/Ai/JumpingBubbles.tsx b/components/Ai/JumpingBubbles.tsx index 2417692..df50940 100644 --- a/components/Ai/JumpingBubbles.tsx +++ b/components/Ai/JumpingBubbles.tsx @@ -9,17 +9,16 @@ const JumpingBubbles = () => { useEffect(() => { // Define the jumping animation - //@ts-ignore - const animateBubble = (bubble) => { + const animateBubble = (bubble: Animated.Value) => { return Animated.sequence([ Animated.timing(bubble, { - toValue: 1.5, // Scale up + toValue: 1.5, duration: 300, easing: Easing.ease, useNativeDriver: true, }), Animated.timing(bubble, { - toValue: 1, // Scale back down + toValue: 1, duration: 300, easing: Easing.ease, useNativeDriver: true, @@ -27,7 +26,6 @@ const JumpingBubbles = () => { ]); }; - // Start looping animation with delays for each bubble Animated.loop( Animated.stagger(150, [ animateBubble(bubble1), diff --git a/components/HomePage/ScrollCard.tsx b/components/HomePage/ScrollCard.tsx index 7a6d4b8..94f4999 100644 --- a/components/HomePage/ScrollCard.tsx +++ b/components/HomePage/ScrollCard.tsx @@ -1,23 +1,27 @@ +/* eslint-disable @typescript-eslint/no-require-imports */ +import React from "react"; import { Image, Text, View, TouchableOpacity } from "react-native"; -import { router } from "expo-router"; +import { Href, router } from "expo-router"; interface ScrollCardProps { id: string; title: string; imageName: string; containerStyle?: string; - redirect?: string + redirect?: string; + routeBase?: string; } -const ScrollCard = ({ id, title, imageName, containerStyle, redirect }: ScrollCardProps) => { +const ScrollCard = ({ id, title, imageName, containerStyle, redirect, routeBase }: ScrollCardProps) => { + routeBase ??= "tip"; const handlePress = () => { - //@ts-ignore - redirect ? router.push(`/tip/${id}?redirect=${redirect}`) : router.push(`/tip/${id}`) + const route: Href = redirect + ? (`/${routeBase}/${id}?redirect=${redirect}` as Href) + : (`/${routeBase}/${id}` as Href); + router.push(route); } return ( - //@ts-ignore - {/* */} {title} diff --git a/components/HomePage/SectionText.tsx b/components/HomePage/SectionText.tsx index a4e4d8e..94261e5 100644 --- a/components/HomePage/SectionText.tsx +++ b/components/HomePage/SectionText.tsx @@ -1,5 +1,7 @@ -import { router } from 'expo-router' -import { Text, TouchableOpacity, View } from 'react-native' +import { router, Href } from 'expo-router'; +import { Text, TouchableOpacity, View } from 'react-native'; +import { ThemedText } from '../ThemedText'; +import React from 'react'; interface SectionTextProps { title: string; @@ -10,15 +12,14 @@ interface SectionTextProps { const SectionText = ({ title, route, containerStyles }: SectionTextProps) => { return ( - {title} + {title} {route && ( - //@ts-ignore - router.push(route)}> + router.push(route as Href)}> Zobacz więcej )} - ) -} + ); +}; -export default SectionText \ No newline at end of file +export default SectionText; diff --git a/components/ThemedText.tsx b/components/ThemedText.tsx index c0e1a78..5966b1a 100644 --- a/components/ThemedText.tsx +++ b/components/ThemedText.tsx @@ -1,4 +1,5 @@ import { Text, type TextProps, StyleSheet } from 'react-native'; +import React from 'react'; import { useThemeColor } from '@/hooks/useThemeColor'; @@ -21,7 +22,7 @@ export function ThemedText({ { - + {text} - + ) } diff --git a/components/elements/CustomInput.tsx b/components/elements/CustomInput.tsx index 9809917..fec5a43 100644 --- a/components/elements/CustomInput.tsx +++ b/components/elements/CustomInput.tsx @@ -1,4 +1,4 @@ -import {StyleSheet, View, TextInput} from "react-native"; +import { StyleSheet, View, TextInput, NativeSyntheticEvent, TextInputChangeEventData } from "react-native"; import React from "react"; type InputType = "text" | "password" | "email"; @@ -6,9 +6,11 @@ type InputType = "text" | "password" | "email"; type CustomInputOptions = { type?: InputType, placeholder?: string, + value?: string, + onChangeText?: ((text: string) => void) | undefined }; -export function CustomInput({type, placeholder, ...rest}: Readonly) { +export function CustomInput({ type, placeholder, value, onChangeText, ...rest }: Readonly) { if (type === undefined) type = "text"; return ( ); } diff --git a/constants/Colors.ts b/constants/Colors.ts index dfb581d..47e749e 100644 --- a/constants/Colors.ts +++ b/constants/Colors.ts @@ -9,7 +9,7 @@ const tintColorDark = '#b7c892'; export const Colors = { light: { text: '#202f11', - background: '#f8f2f3', + background: '#ffffff', tint: tintColorLight, icon: '#ae7e2d', primary: '#606c38', diff --git a/firebase.config.tsx b/firebase.config.tsx index 3bf8616..5910dc4 100644 --- a/firebase.config.tsx +++ b/firebase.config.tsx @@ -1,7 +1,6 @@ import { initializeApp } from "firebase/app"; import { getAuth } from "firebase/auth"; import { getFirestore } from "firebase/firestore"; -import { Extrapolation } from "react-native-reanimated"; const firebaseConfig = { apiKey: "***REMOVED***", diff --git a/functions/shuffleArray.tsx b/functions/shuffleArray.tsx index 75b6d94..93dc6de 100644 --- a/functions/shuffleArray.tsx +++ b/functions/shuffleArray.tsx @@ -1,7 +1,7 @@ -export const shuffleArray = (array : TipFields[]) => { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } - return array; - }; \ No newline at end of file +export function shuffleArray(array: T[]): T[] { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; +}; \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 5d97931..7f4bfc3 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-require-imports */ +/* eslint-disable no-undef */ /** @type {import('tailwindcss').Config} */ module.exports = { // NOTE: Update this to include the paths to all of your component files. diff --git a/typing.d.ts b/typing.d.ts index 2b52c41..2a03a3f 100644 --- a/typing.d.ts +++ b/typing.d.ts @@ -21,7 +21,14 @@ interface TipFields extends TipData { interface ChallengeFields extends ChallengeData { id: string; } - +interface ShopItemData { + name: string; + price: number; + image: string; +} +interface ShopItem extends ShopItemData { + id: string; +} interface MessageType { message: string;