- Implemented optimistic update when completing todos and groceries to avoid delays

This commit is contained in:
Dejan
2024-12-17 20:13:51 +01:00
parent 79ac1efe63
commit 4b67033aa8
4 changed files with 30 additions and 10 deletions

View File

@ -1,12 +1,11 @@
import { Checkbox, Text, TouchableOpacity, View } from "react-native-ui-lib"; import { Checkbox, Text, TouchableOpacity, View } from "react-native-ui-lib";
import React, { useEffect, useState } from "react"; import React, { useState } from "react";
import { AntDesign } from "@expo/vector-icons"; import { AntDesign } from "@expo/vector-icons";
import { GroceryCategory, useGroceryContext } from "@/contexts/GroceryContext"; import { GroceryCategory, useGroceryContext } from "@/contexts/GroceryContext";
import EditGroceryFrequency from "./EditGroceryFrequency"; import EditGroceryFrequency from "./EditGroceryFrequency";
import EditGroceryItem from "./EditGroceryItem"; import EditGroceryItem from "./EditGroceryItem";
import { ImageBackground, StyleSheet } from "react-native"; import { ImageBackground, StyleSheet } from "react-native";
import { IGrocery } from "@/hooks/firebase/types/groceryData"; import { IGrocery } from "@/hooks/firebase/types/groceryData";
import { UserProfile } from "@/hooks/firebase/types/profileTypes";
import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
import { useGetUserById } from "@/hooks/firebase/useGetUserById"; import { useGetUserById } from "@/hooks/firebase/useGetUserById";
import { useDeleteGrocery } from "@/hooks/firebase/useDeleteGrocery"; import { useDeleteGrocery } from "@/hooks/firebase/useDeleteGrocery";
@ -15,10 +14,14 @@ const GroceryItem = ({
item, item,
handleItemApproved, handleItemApproved,
onInputFocus, onInputFocus,
approvedGroceries,
setApprovedGroceries
}: { }: {
item: IGrocery; item: IGrocery;
handleItemApproved: (id: string, changes: Partial<IGrocery>) => void; handleItemApproved: (id: string, changes: Partial<IGrocery>) => void;
onInputFocus: (y: number) => void; onInputFocus: (y: number) => void;
approvedGroceries?: Array<IGrocery>,
setApprovedGroceries?: Function
}) => { }) => {
const { updateGroceryItem } = useGroceryContext(); const { updateGroceryItem } = useGroceryContext();
const { mutateAsync: deleteGrocery } = useDeleteGrocery(); const { mutateAsync: deleteGrocery } = useDeleteGrocery();
@ -150,9 +153,11 @@ const GroceryItem = ({
borderRadius={50} borderRadius={50}
color="#fd1575" color="#fd1575"
hitSlop={20} hitSlop={20}
onValueChange={() => onValueChange={() => {
const updatedApprovedGroceries = approvedGroceries.map((grocery) => grocery.id === item.id ? {...grocery, bought: !item.bought} : grocery);
setApprovedGroceries(updatedApprovedGroceries);
updateGroceryItem({ id: item.id, bought: !item.bought }) updateGroceryItem({ id: item.id, bought: !item.bought })
} }}
/> />
</View> </View>
) )

View File

@ -33,7 +33,7 @@ const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
addGrocery, addGrocery,
} = useGroceryContext(); } = useGroceryContext();
const {profileData} = useAuthContext(); const {profileData} = useAuthContext();
const [approvedGroceries, setapprovedGroceries] = useState<IGrocery[]>( const [approvedGroceries, setApprovedGroceries] = useState<IGrocery[]>(
groceries?.filter((item) => item.approved) groceries?.filter((item) => item.approved)
); );
const [pendingGroceries, setPendingGroceries] = useState<IGrocery[]>( const [pendingGroceries, setPendingGroceries] = useState<IGrocery[]>(
@ -94,7 +94,7 @@ const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
}, [submit]); }, [submit]);
useEffect(() => { useEffect(() => {
setapprovedGroceries(groceries?.filter((item) => item.approved)); setApprovedGroceries(groceries?.filter((item) => item.approved));
setPendingGroceries(groceries?.filter((item) => !item.approved)); setPendingGroceries(groceries?.filter((item) => !item.approved));
}, [groceries]); }, [groceries]);
@ -290,6 +290,8 @@ const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
updateGroceryItem({...changes, id: id}) updateGroceryItem({...changes, id: id})
} }
onInputFocus={onInputFocus} onInputFocus={onInputFocus}
approvedGroceries={approvedGroceries}
setApprovedGroceries={setApprovedGroceries}
/> />
) )
)} )}

View File

@ -25,6 +25,8 @@ const ToDoItem = (props: {
item: IToDo; item: IToDo;
isSettings?: boolean; isSettings?: boolean;
is7Days?: boolean; is7Days?: boolean;
localTodos: Array<IToDo>;
setLocalTodos: Function
}) => { }) => {
const { updateToDo } = useToDosContext(); const { updateToDo } = useToDosContext();
const { data: members } = useGetFamilyMembers(); const { data: members } = useGetFamilyMembers();
@ -131,6 +133,8 @@ const ToDoItem = (props: {
borderRadius={50} borderRadius={50}
color="#fd1575" color="#fd1575"
onValueChange={() => { onValueChange={() => {
const updatedTodos = props.localTodos?.map((item) => item.id === props.item.id ? { ...item, done: !props.item.done } : item);
props.setLocalTodos(updatedTodos);
updateToDo({ id: props.item.id, done: !props.item.done }); updateToDo({ id: props.item.id, done: !props.item.done });
if (!props.item.done && isChild) { if (!props.item.done && isChild) {
handleTodoClickAnimation(); handleTodoClickAnimation();

View File

@ -110,6 +110,11 @@ const resolveFilterOptions = (members, user) => {
const ToDosList = ({ isSettings, members }: { isSettings?: boolean, members?: Array<UserProfile> }) => { const ToDosList = ({ isSettings, members }: { isSettings?: boolean, members?: Array<UserProfile> }) => {
const { toDos } = useToDosContext(); const { toDos } = useToDosContext();
const [localTodos, setLocalTodos] = useState([]);
useEffect(() => {
setLocalTodos(toDos);
}, [toDos]);
const [groupedToDos, setGroupedToDos] = useState([]); const [groupedToDos, setGroupedToDos] = useState([]);
const { user } = useAuthContext() const { user } = useAuthContext()
@ -131,18 +136,18 @@ const ToDosList = ({ isSettings, members }: { isSettings?: boolean, members?: Ar
}, [JSON.stringify(members)]); }, [JSON.stringify(members)]);
useEffect(() => { useEffect(() => {
if (toDos && selectedFilter) { if (localTodos && selectedFilter) {
let resolvedGroupedTodos; let resolvedGroupedTodos;
if (selectedFilter?.value === FILTER_OPTIONS.EVERYONE) { if (selectedFilter?.value === FILTER_OPTIONS.EVERYONE) {
resolvedGroupedTodos = groupToDosByDate(toDos ?? []); resolvedGroupedTodos = groupToDosByDate(toDos ?? []);
} else { } else {
let filtered = toDos?.filter((todo) => todo.assignees?.includes(selectedFilter.value)) || []; let filtered = localTodos?.filter((todo) => todo.assignees?.includes(selectedFilter.value)) || [];
resolvedGroupedTodos = groupToDosByDate(filtered || []); resolvedGroupedTodos = groupToDosByDate(filtered || []);
} }
setGroupedToDos(resolvedGroupedTodos || []); setGroupedToDos(resolvedGroupedTodos || []);
} }
}, [selectedFilter, JSON.stringify(toDos)]); }, [selectedFilter, JSON.stringify(localTodos)]);
const toggleExpand = (dateKey: string) => { const toggleExpand = (dateKey: string) => {
setExpandedGroups((prev) => ({ setExpandedGroups((prev) => ({
@ -231,6 +236,8 @@ const ToDosList = ({ isSettings, members }: { isSettings?: boolean, members?: Ar
key={item.id} key={item.id}
item={item} item={item}
is7Days={false} is7Days={false}
localTodos={localTodos}
setLocalTodos={setLocalTodos}
/> />
))} ))}
</View> </View>
@ -281,6 +288,8 @@ const ToDosList = ({ isSettings, members }: { isSettings?: boolean, members?: Ar
key={item.id} key={item.id}
item={item} item={item}
is7Days={dateKey === "Next 7 Days"} is7Days={dateKey === "Next 7 Days"}
localTodos={localTodos}
setLocalTodos={setLocalTodos}
/> />
))} ))}
</View> </View>
@ -347,7 +356,7 @@ const ToDosList = ({ isSettings, members }: { isSettings?: boolean, members?: Ar
noDateToDos noDateToDos
.sort((a, b) => Number(a.done) - Number(b.done)) .sort((a, b) => Number(a.done) - Number(b.done))
.map((item) => ( .map((item) => (
<ToDoItem isSettings={isSettings} key={item.id} item={item} /> <ToDoItem isSettings={isSettings} key={item.id} item={item} localTodos={localTodos} setLocalTodos={setLocalTodos} />
))} ))}
</View> </View>
)} )}