diff --git a/components/pages/todos/AddChoreDialog.tsx b/components/pages/todos/AddChoreDialog.tsx index e38180d..5d29561 100644 --- a/components/pages/todos/AddChoreDialog.tsx +++ b/components/pages/todos/AddChoreDialog.tsx @@ -1,5 +1,5 @@ import { View, Text, Button, Switch } from "react-native-ui-lib"; -import React, { useEffect, useState } from "react"; +import React, { useState } from "react"; import PointsSlider from "@/components/shared/PointsSlider"; import { repeatOptions, useToDosContext } from "@/contexts/ToDosContext"; import { Feather, AntDesign, Ionicons } from "@expo/vector-icons"; @@ -27,7 +27,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => { const [rotate, setRotate] = useState(false); const [repeatType, setRepeatType] = useState("Every week"); - const handleCLose = () => { + const handleClose = () => { setNewTitle(""); setPoints(10); setChoreDate(new Date()); @@ -50,7 +50,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => { bottom={true} height={"90%"} panDirection={PanningDirectionsEnum.DOWN} - onDismiss={() => handleCLose} + onDismiss={() => handleClose} containerStyle={{ borderRadius: 10, backgroundColor: "white", @@ -68,13 +68,13 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => { style={styles.topBtn} label="Cancel" onPress={() => { - handleCLose(); + handleClose(); }} /> { - handleCLose() + handleClose() }} /> @@ -85,7 +85,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => { onPress={() => { try { addToDo({ - id: 0, + id: "", title: newTitle, done: false, date: choreDate, @@ -93,8 +93,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => { rotate: rotate, repeatType: repeatType, }); - handleCLose(); - console.log(toDos); + handleClose(); } catch (error) { console.error(error); } diff --git a/components/pages/todos/ToDoItem.tsx b/components/pages/todos/ToDoItem.tsx index 3343995..948d5dd 100644 --- a/components/pages/todos/ToDoItem.tsx +++ b/components/pages/todos/ToDoItem.tsx @@ -10,9 +10,10 @@ import { ButtonSize, } from "react-native-ui-lib"; import React, { useEffect, useState } from "react"; -import { IToDo, useToDosContext } from "@/contexts/ToDosContext"; +import { useToDosContext } from "@/contexts/ToDosContext"; import { Ionicons } from "@expo/vector-icons"; import PointsSlider from "@/components/shared/PointsSlider"; +import {IToDo} from "@/hooks/firebase/types/todoData"; const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => { const { updateToDo } = useToDosContext(); @@ -61,7 +62,7 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => { value={props.item.title} text70R onChangeText={(text) => { - updateToDo(props.item.id, { title: text }); + updateToDo({id: props.item.id, title: text }); }} onSubmitEditing={() => { setEditing(false); @@ -74,7 +75,7 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => { { - updateToDo(props.item.id, { done: !props.item.done }); + updateToDo({id: props.item.id, done: !props.item.done }); }} /> @@ -151,7 +152,7 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => { backgroundColor="#fd1775" style={{height: 60, width: 150}} onPress={() => { - updateToDo(props.item.id, { points: points }); + updateToDo({id: props.item.id, points: points }); setPointsModalVisible(false); }} /> diff --git a/components/pages/todos/ToDosList.tsx b/components/pages/todos/ToDosList.tsx index 923701d..3492785 100644 --- a/components/pages/todos/ToDosList.tsx +++ b/components/pages/todos/ToDosList.tsx @@ -1,10 +1,10 @@ import { View, Text, TouchableOpacity, Icon } from "react-native-ui-lib"; import React, { useState } from "react"; -import { IToDo, useToDosContext } from "@/contexts/ToDosContext"; +import { useToDosContext } from "@/contexts/ToDosContext"; import ToDoItem from "./ToDoItem"; import { format, isToday, isTomorrow } from "date-fns"; -import DropModalIcon from "@/assets/svgs/DropModalIcon"; import { AntDesign } from "@expo/vector-icons"; +import {IToDo} from "@/hooks/firebase/types/todoData"; const groupToDosByDate = (toDos: IToDo[]) => { return toDos.reduce((groups, toDo) => { diff --git a/contexts/ToDosContext.tsx b/contexts/ToDosContext.tsx index a919956..55b1740 100644 --- a/contexts/ToDosContext.tsx +++ b/contexts/ToDosContext.tsx @@ -1,14 +1,88 @@ import { createContext, FC, ReactNode, useContext, useState } from "react"; +import {IToDo} from "@/hooks/firebase/types/todoData"; +import {useGetGroceries} from "@/hooks/firebase/useGetGroceries"; +import {useGetTodos} from "@/hooks/firebase/useGetTodos"; +import {useCreateGrocery} from "@/hooks/firebase/useCreateGrocery"; +import {useCreateTodo} from "@/hooks/firebase/useCreateTodo"; +import {useUpdateTodo} from "@/hooks/firebase/useUpdateTodo"; -export interface IToDo { - id: number; - title: string; - done: boolean; - date: Date | null; - points?: number; - rotate: boolean; - repeatType: string; -} +const initialTodosList = [ + { + id: 0, + title: "Pay: Credit card", + done: false, + date: new Date(), + rotate: true, + repeatType: "Every week", + }, + { + id: 1, + title: "Monthly Log story", + done: false, + date: new Date(), + rotate: false, + repeatType: "Every week", + }, + { + id: 2, + title: "Write: Arcade Highlights", + done: false, + date: new Date(), + rotate: true, + repeatType: "Every week", + }, + { + id: 3, + title: "Dressup: Cat", + done: false, + date: new Date(Date.now() + 86400000), + points: 40, + rotate: false, + repeatType: "Every week", + }, + { + id: 4, + title: "Trim: Nails", + done: false, + date: new Date(Date.now() + 86400000), + rotate: false, + repeatType: "Once a Month", + }, + { + id: 5, + title: "Monthly Log", + done: false, + date: new Date(Date.now() + 2 * 86400000), + rotate: true, + repeatType: "Once a Month", + }, + { + id: 6, + title: "Do it", + done: false, + date: new Date(Date.now() + 3 * 86400000), + rotate: false, + repeatType: "Once a year", + points: 50, + }, + { + id: 7, + title: "Buy Nautica Voyage", + done: false, + date: null, + rotate: false, + repeatType: "None", + }, + { + id: 8, + title: "Sell Dan's Xbox", + done: false, + date: null, + rotate: false, + repeatType: "None", + points: 10, + }, +]; export const repeatOptions = [ { label: "None", value: "None" }, @@ -19,7 +93,7 @@ export const repeatOptions = [ interface IToDosContext { toDos: IToDo[]; - updateToDo: (id: number, changes: Partial) => void; + updateToDo: (changes: Partial) => void; addToDo: (newToDo: IToDo) => void; maxPoints: number; } @@ -29,118 +103,36 @@ const ToDosContext = createContext(undefined!); export const ToDosContextProvider: FC<{ children: ReactNode }> = ({ children, }) => { - const [toDos, setToDos] = useState([ - { - id: 0, - title: "Pay: Credit card", - done: false, - date: new Date(), - rotate: true, - repeatType: "Every week", - }, - { - id: 1, - title: "Monthly Log story", - done: false, - date: new Date(), - rotate: false, - repeatType: "Every week", - }, - { - id: 2, - title: "Write: Arcade Highlights", - done: false, - date: new Date(), - rotate: true, - repeatType: "Every week", - }, - { - id: 3, - title: "Dressup: Cat", - done: false, - date: new Date(Date.now() + 86400000), - points: 40, - rotate: false, - repeatType: "Every week", - }, - { - id: 4, - title: "Trim: Nails", - done: false, - date: new Date(Date.now() + 86400000), - rotate: false, - repeatType: "Once a Month", - }, - { - id: 5, - title: "Monthly Log", - done: false, - date: new Date(Date.now() + 2 * 86400000), - rotate: true, - repeatType: "Once a Month", - }, - { - id: 6, - title: "Do it", - done: false, - date: new Date(Date.now() + 3 * 86400000), - rotate: false, - repeatType: "Once a year", - points: 50, - }, - { - id: 7, - title: "Buy Nautica Voyage", - done: false, - date: null, - rotate: false, - repeatType: "None", - }, - { - id: 8, - title: "Sell Dan's Xbox", - done: false, - date: null, - rotate: false, - repeatType: "None", - points: 10, - }, - ]); + const { data: toDos } = useGetTodos(); + const { mutateAsync: createTodo } = useCreateTodo(); + const { mutateAsync: updateTodo } = useUpdateTodo(); const initCalc = (): number => { - return toDos.reduce( + return (toDos ?? []).reduce( (sum, todo) => sum + (todo.points ? todo.points : 0), 50 ); }; const calculateMaxPoints = () => { - const totalPoints = toDos.reduce( - (sum, todo) => sum + (todo.points ? todo.points : 0), - 0 - ); + let totalPoints = 0; + if (toDos) { + totalPoints = toDos.reduce( + (sum, todo) => sum + (todo.points ? todo.points : 0), + 0 + ); + } setMaxPoints(totalPoints); }; const [maxPoints, setMaxPoints] = useState(initCalc); - const updateToDo = (id: number, changes: Partial) => { - setToDos((prevToDos) => - prevToDos.map((toDo) => (toDo.id === id ? { ...toDo, ...changes } : toDo)) - ); - - calculateMaxPoints(); + const updateToDo = (changes: Partial) => { + updateTodo(changes).then(calculateMaxPoints); }; const addToDo = (newToDo: IToDo) => { - setToDos((prevToDos) => [ - ...prevToDos, - { - ...newToDo, - id: prevToDos.length ? prevToDos[prevToDos.length - 1].id + 1 : 0, - }, - ]); - calculateMaxPoints(); + createTodo(newToDo).then(calculateMaxPoints); }; return ( diff --git a/hooks/firebase/types/todoData.ts b/hooks/firebase/types/todoData.ts new file mode 100644 index 0000000..069319d --- /dev/null +++ b/hooks/firebase/types/todoData.ts @@ -0,0 +1,11 @@ +export interface IToDo { + id: string; + title: string; + done: boolean; + date: Date | null; + points?: number; + rotate: boolean; + repeatType: string; + creatorId?: string, + familyId?: string +} \ No newline at end of file diff --git a/hooks/firebase/useCreateTodo.ts b/hooks/firebase/useCreateTodo.ts new file mode 100644 index 0000000..d065fc5 --- /dev/null +++ b/hooks/firebase/useCreateTodo.ts @@ -0,0 +1,26 @@ +import { useMutation, useQueryClient } from "react-query"; +import firestore from "@react-native-firebase/firestore"; +import { useAuthContext } from "@/contexts/AuthContext"; +import { IToDo } from "@/hooks/firebase/types/todoData"; + +export const useCreateTodo = () => { + const { user: currentUser, profileData } = useAuthContext(); + const queryClients = useQueryClient(); + + return useMutation({ + mutationKey: ["createTodo"], + mutationFn: async (todoData: Partial) => { + try { + const newDoc = firestore().collection('Todos').doc(); + await firestore() + .collection("Todos") + .add({...todoData, id: newDoc.id, familyId: profileData?.familyId, creatorId: currentUser?.uid}) + } catch (e) { + console.error(e) + } + }, + onSuccess: () => { + queryClients.invalidateQueries("todos") + } + }) +} \ No newline at end of file diff --git a/hooks/firebase/useGetTodos.ts b/hooks/firebase/useGetTodos.ts new file mode 100644 index 0000000..d5821b9 --- /dev/null +++ b/hooks/firebase/useGetTodos.ts @@ -0,0 +1,32 @@ +import { useQuery } from "react-query"; +import firestore from "@react-native-firebase/firestore"; +import { useAuthContext } from "@/contexts/AuthContext"; + +export const useGetTodos = () => { + const { user, profileData } = useAuthContext(); + + return useQuery({ + queryKey: ["todos", user?.uid], + queryFn: async () => { + const snapshot = await firestore() + .collection("Todos") + .where("familyId", "==", profileData?.familyId) + .get(); + + return snapshot.docs.map((doc) => { + const data = doc.data(); + + return { + id: doc.id, + title: data.title, + done: data.done, + date: data.date ? new Date(data.date.seconds * 1000) : null, + points: data.points, + rotate: data.points, + repeatType: data.repeatType, + creatorId: data.creatorId + }; + }); + } + }) +}; \ No newline at end of file diff --git a/hooks/firebase/useUpdateTodo.ts b/hooks/firebase/useUpdateTodo.ts new file mode 100644 index 0000000..b1b985f --- /dev/null +++ b/hooks/firebase/useUpdateTodo.ts @@ -0,0 +1,24 @@ +import { useMutation, useQueryClient } from "react-query"; +import firestore from "@react-native-firebase/firestore"; +import { IToDo } from "@/hooks/firebase/types/todoData"; + +export const useUpdateTodo = () => { + const queryClients = useQueryClient() + + return useMutation({ + mutationKey: ["updateTodo"], + mutationFn: async (todoData: Partial) => { + try { + await firestore() + .collection("Todos") + .doc(todoData.id) + .update(todoData); + } catch (e) { + console.error(e) + } + }, + onSuccess: () => { + queryClients.invalidateQueries("todos") + } + }) +} \ No newline at end of file