Implementation of fetching, adding and updating todos to db

This commit is contained in:
Dejan
2024-10-13 13:34:31 +02:00
parent f4be82587c
commit 515b5738bb
8 changed files with 205 additions and 120 deletions

View File

@ -1,5 +1,5 @@
import { View, Text, Button, Switch } from "react-native-ui-lib"; 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 PointsSlider from "@/components/shared/PointsSlider";
import { repeatOptions, useToDosContext } from "@/contexts/ToDosContext"; import { repeatOptions, useToDosContext } from "@/contexts/ToDosContext";
import { Feather, AntDesign, Ionicons } from "@expo/vector-icons"; import { Feather, AntDesign, Ionicons } from "@expo/vector-icons";
@ -27,7 +27,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
const [rotate, setRotate] = useState<boolean>(false); const [rotate, setRotate] = useState<boolean>(false);
const [repeatType, setRepeatType] = useState<string>("Every week"); const [repeatType, setRepeatType] = useState<string>("Every week");
const handleCLose = () => { const handleClose = () => {
setNewTitle(""); setNewTitle("");
setPoints(10); setPoints(10);
setChoreDate(new Date()); setChoreDate(new Date());
@ -50,7 +50,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
bottom={true} bottom={true}
height={"90%"} height={"90%"}
panDirection={PanningDirectionsEnum.DOWN} panDirection={PanningDirectionsEnum.DOWN}
onDismiss={() => handleCLose} onDismiss={() => handleClose}
containerStyle={{ containerStyle={{
borderRadius: 10, borderRadius: 10,
backgroundColor: "white", backgroundColor: "white",
@ -68,13 +68,13 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
style={styles.topBtn} style={styles.topBtn}
label="Cancel" label="Cancel"
onPress={() => { onPress={() => {
handleCLose(); handleClose();
}} }}
/> />
<View marginT-12> <View marginT-12>
<DropModalIcon <DropModalIcon
onPress={() => { onPress={() => {
handleCLose() handleClose()
}} }}
/> />
</View> </View>
@ -85,7 +85,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
onPress={() => { onPress={() => {
try { try {
addToDo({ addToDo({
id: 0, id: "",
title: newTitle, title: newTitle,
done: false, done: false,
date: choreDate, date: choreDate,
@ -93,8 +93,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
rotate: rotate, rotate: rotate,
repeatType: repeatType, repeatType: repeatType,
}); });
handleCLose(); handleClose();
console.log(toDos);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }

View File

@ -10,9 +10,10 @@ import {
ButtonSize, ButtonSize,
} from "react-native-ui-lib"; } from "react-native-ui-lib";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { IToDo, useToDosContext } from "@/contexts/ToDosContext"; import { useToDosContext } from "@/contexts/ToDosContext";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import PointsSlider from "@/components/shared/PointsSlider"; import PointsSlider from "@/components/shared/PointsSlider";
import {IToDo} from "@/hooks/firebase/types/todoData";
const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => { const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => {
const { updateToDo } = useToDosContext(); const { updateToDo } = useToDosContext();
@ -61,7 +62,7 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => {
value={props.item.title} value={props.item.title}
text70R text70R
onChangeText={(text) => { onChangeText={(text) => {
updateToDo(props.item.id, { title: text }); updateToDo({id: props.item.id, title: text });
}} }}
onSubmitEditing={() => { onSubmitEditing={() => {
setEditing(false); setEditing(false);
@ -74,7 +75,7 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => {
<Checkbox <Checkbox
value={props.item.done} value={props.item.done}
onValueChange={(value) => { onValueChange={(value) => {
updateToDo(props.item.id, { done: !props.item.done }); updateToDo({id: props.item.id, done: !props.item.done });
}} }}
/> />
</View> </View>
@ -151,7 +152,7 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => {
backgroundColor="#fd1775" backgroundColor="#fd1775"
style={{height: 60, width: 150}} style={{height: 60, width: 150}}
onPress={() => { onPress={() => {
updateToDo(props.item.id, { points: points }); updateToDo({id: props.item.id, points: points });
setPointsModalVisible(false); setPointsModalVisible(false);
}} }}
/> />

View File

@ -1,10 +1,10 @@
import { View, Text, TouchableOpacity, Icon } from "react-native-ui-lib"; import { View, Text, TouchableOpacity, Icon } from "react-native-ui-lib";
import React, { useState } from "react"; import React, { useState } from "react";
import { IToDo, useToDosContext } from "@/contexts/ToDosContext"; import { useToDosContext } from "@/contexts/ToDosContext";
import ToDoItem from "./ToDoItem"; import ToDoItem from "./ToDoItem";
import { format, isToday, isTomorrow } from "date-fns"; import { format, isToday, isTomorrow } from "date-fns";
import DropModalIcon from "@/assets/svgs/DropModalIcon";
import { AntDesign } from "@expo/vector-icons"; import { AntDesign } from "@expo/vector-icons";
import {IToDo} from "@/hooks/firebase/types/todoData";
const groupToDosByDate = (toDos: IToDo[]) => { const groupToDosByDate = (toDos: IToDo[]) => {
return toDos.reduce((groups, toDo) => { return toDos.reduce((groups, toDo) => {

View File

@ -1,35 +1,12 @@
import { createContext, FC, ReactNode, useContext, useState } from "react"; 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 { const initialTodosList = [
id: number;
title: string;
done: boolean;
date: Date | null;
points?: number;
rotate: boolean;
repeatType: string;
}
export const repeatOptions = [
{ label: "None", value: "None" },
{ label: "Every week", value: "Every week" },
{ label: "Once a month", value: "Once a month" },
{ label: "Once a year", value: "Once a year" },
];
interface IToDosContext {
toDos: IToDo[];
updateToDo: (id: number, changes: Partial<IToDo>) => void;
addToDo: (newToDo: IToDo) => void;
maxPoints: number;
}
const ToDosContext = createContext<IToDosContext>(undefined!);
export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
children,
}) => {
const [toDos, setToDos] = useState<IToDo[]>([
{ {
id: 0, id: 0,
title: "Pay: Credit card", title: "Pay: Credit card",
@ -105,42 +82,57 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
repeatType: "None", repeatType: "None",
points: 10, points: 10,
}, },
]); ];
export const repeatOptions = [
{ label: "None", value: "None" },
{ label: "Every week", value: "Every week" },
{ label: "Once a month", value: "Once a month" },
{ label: "Once a year", value: "Once a year" },
];
interface IToDosContext {
toDos: IToDo[];
updateToDo: (changes: Partial<IToDo>) => void;
addToDo: (newToDo: IToDo) => void;
maxPoints: number;
}
const ToDosContext = createContext<IToDosContext>(undefined!);
export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
children,
}) => {
const { data: toDos } = useGetTodos();
const { mutateAsync: createTodo } = useCreateTodo();
const { mutateAsync: updateTodo } = useUpdateTodo();
const initCalc = (): number => { const initCalc = (): number => {
return toDos.reduce( return (toDos ?? []).reduce(
(sum, todo) => sum + (todo.points ? todo.points : 0), (sum, todo) => sum + (todo.points ? todo.points : 0),
50 50
); );
}; };
const calculateMaxPoints = () => { const calculateMaxPoints = () => {
const totalPoints = toDos.reduce( let totalPoints = 0;
if (toDos) {
totalPoints = toDos.reduce(
(sum, todo) => sum + (todo.points ? todo.points : 0), (sum, todo) => sum + (todo.points ? todo.points : 0),
0 0
); );
}
setMaxPoints(totalPoints); setMaxPoints(totalPoints);
}; };
const [maxPoints, setMaxPoints] = useState<number>(initCalc); const [maxPoints, setMaxPoints] = useState<number>(initCalc);
const updateToDo = (id: number, changes: Partial<IToDo>) => { const updateToDo = (changes: Partial<IToDo>) => {
setToDos((prevToDos) => updateTodo(changes).then(calculateMaxPoints);
prevToDos.map((toDo) => (toDo.id === id ? { ...toDo, ...changes } : toDo))
);
calculateMaxPoints();
}; };
const addToDo = (newToDo: IToDo) => { const addToDo = (newToDo: IToDo) => {
setToDos((prevToDos) => [ createTodo(newToDo).then(calculateMaxPoints);
...prevToDos,
{
...newToDo,
id: prevToDos.length ? prevToDos[prevToDos.length - 1].id + 1 : 0,
},
]);
calculateMaxPoints();
}; };
return ( return (

View File

@ -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
}

View File

@ -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<IToDo>) => {
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")
}
})
}

View File

@ -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
};
});
}
})
};

View File

@ -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<IToDo>) => {
try {
await firestore()
.collection("Todos")
.doc(todoData.id)
.update(todoData);
} catch (e) {
console.error(e)
}
},
onSuccess: () => {
queryClients.invalidateQueries("todos")
}
})
}