mirror of
https://github.com/urosran/cally.git
synced 2025-11-26 08:24:55 +00:00
Implementation of fetching, adding and updating todos to db
This commit is contained in:
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -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) => {
|
||||||
|
|||||||
@ -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 (
|
||||||
|
|||||||
11
hooks/firebase/types/todoData.ts
Normal file
11
hooks/firebase/types/todoData.ts
Normal 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
|
||||||
|
}
|
||||||
26
hooks/firebase/useCreateTodo.ts
Normal file
26
hooks/firebase/useCreateTodo.ts
Normal 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")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
32
hooks/firebase/useGetTodos.ts
Normal file
32
hooks/firebase/useGetTodos.ts
Normal 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
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
24
hooks/firebase/useUpdateTodo.ts
Normal file
24
hooks/firebase/useUpdateTodo.ts
Normal 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")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user