- Added loading indicators when on groceries and todos, fixed margin issues in the todo page

This commit is contained in:
Dejan
2024-12-17 23:33:59 +01:00
parent 8b802f492d
commit b7fd8daddf
4 changed files with 237 additions and 208 deletions

View File

@ -1,17 +1,17 @@
import {FlatList, StyleSheet} from "react-native"; import { ActivityIndicator, Dimensions, FlatList, StyleSheet } from "react-native";
import React, {useEffect, useState} from "react"; import React, { useEffect, useState } from "react";
import {Text, TouchableOpacity, View} from "react-native-ui-lib"; import { Text, TouchableOpacity, View } from "react-native-ui-lib";
import GroceryItem from "./GroceryItem"; import GroceryItem from "./GroceryItem";
import {GroceryCategory, GroceryFrequency, useGroceryContext,} from "@/contexts/GroceryContext"; import { GroceryCategory, GroceryFrequency, useGroceryContext } from "@/contexts/GroceryContext";
import HeaderTemplate from "@/components/shared/HeaderTemplate"; import HeaderTemplate from "@/components/shared/HeaderTemplate";
import {AntDesign} from "@expo/vector-icons"; import { AntDesign } from "@expo/vector-icons";
import EditGroceryItem from "./EditGroceryItem"; import EditGroceryItem from "./EditGroceryItem";
import {ProfileType, useAuthContext} from "@/contexts/AuthContext"; import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
import {IGrocery} from "@/hooks/firebase/types/groceryData"; import { IGrocery } from "@/hooks/firebase/types/groceryData";
import Ionicons from '@expo/vector-icons/Ionicons'; import Ionicons from '@expo/vector-icons/Ionicons';
import AddChoreDialog from "@/components/pages/todos/AddChoreDialog"; import AddChoreDialog from "@/components/pages/todos/AddChoreDialog";
import {REPEAT_TYPE} from "@/hooks/firebase/types/todoData"; import { REPEAT_TYPE } from "@/hooks/firebase/types/todoData";
import {ToDosContextProvider} from "@/contexts/ToDosContext"; import { ToDosContextProvider } from "@/contexts/ToDosContext";
const shoppingTodo = { const shoppingTodo = {
id: "", id: "",
@ -32,7 +32,8 @@ const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
setIsAddingGrocery, setIsAddingGrocery,
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)
); );
@ -105,205 +106,212 @@ const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
} }
return ( return (
<View marginH-20 marginB-45> <>
<HeaderTemplate {!groceries &&
message={"Welcome to your grocery list"} <View style={styles.loaderContainer}>
isWelcome={false} <ActivityIndicator size="large" color="grey" />
isGroceries={true}
>
<View row centerV>
<View
paddingH-15
paddingV-8
centerV
style={{borderRadius: 50}}
>
<Text text70BL color="#46a80a" style={styles.counterText}>
{approvedGroceries?.length} list{" "}
{approvedGroceries?.length === 1 ? (
<Text text70BL color="#46a80a" style={styles.counterText}>
item
</Text>
) : (
<Text text70BL color="#46a80a" style={styles.counterText}>
items
</Text>
)}
</Text>
</View>
<View
padding-8
paddingH-12
marginR-15
style={{borderRadius: 50}}
>
<Text text70 style={styles.counterText} color="#e28800">
{pendingGroceries?.length} pending
</Text>
</View>
<TouchableOpacity onPress={() => setChoreDialogVisible(true)}>
<Ionicons name="person-add-outline" size={24} color="grey" />
</TouchableOpacity>
<ToDosContextProvider>
{choreDialogVisible &&
<AddChoreDialog
isVisible={choreDialogVisible}
setIsVisible={setChoreDialogVisible}
selectedTodo={shoppingTodo}
isShoppingTodo
/>
}
</ToDosContextProvider>
</View> </View>
</HeaderTemplate> }
<View marginH-20 marginB-45>
{/* Pending Approval Section */} <HeaderTemplate
<View row spread marginT-40 marginB-10 centerV> message={"Welcome to your grocery list"}
<View row centerV> isWelcome={false}
<TouchableOpacity row centerV onPress={() => {setPendingVisible(!pendingVisible)}}> isGroceries={true}
<Text style={styles.subHeader}>Pending Approval</Text>
{pendingVisible && (
<AntDesign
name="down"
size={17}
style={styles.dropIcon}
color="#9f9f9f"
/>
)}
{!pendingVisible && (
<AntDesign
name="right"
size={15}
style={styles.dropIcon}
color="#9f9f9f"
/>
)}
</TouchableOpacity>
</View>
<View
centerV
style={{
aspectRatio: 1,
width: 35,
backgroundColor: "#faead2",
borderRadius: 50,
}}
> >
<Text style={styles.counterNr} center color="#e28800"> <View row centerV>
{pendingGroceries?.length.toString()} <View
</Text> paddingH-15
</View> paddingV-8
</View> centerV
{pendingGroceries?.length > 0 style={{borderRadius: 50}}
? pendingVisible && ( >
<FlatList <Text text70BL color="#46a80a" style={styles.counterText}>
data={pendingGroceries} {approvedGroceries?.length} list{" "}
renderItem={({item}) => ( {approvedGroceries?.length === 1 ? (
<GroceryItem <Text text70BL color="#46a80a" style={styles.counterText}>
item={item} item
handleItemApproved={(id, changes) => </Text>
updateGroceryItem({...changes, id: id}) ) : (
<Text text70BL color="#46a80a" style={styles.counterText}>
items
</Text>
)}
</Text>
</View>
<View
padding-8
paddingH-12
marginR-15
style={{borderRadius: 50}}
>
<Text text70 style={styles.counterText} color="#e28800">
{pendingGroceries?.length} pending
</Text>
</View>
<TouchableOpacity onPress={() => setChoreDialogVisible(true)}>
<Ionicons name="person-add-outline" size={24} color="grey" />
</TouchableOpacity>
<ToDosContextProvider>
{choreDialogVisible &&
<AddChoreDialog
isVisible={choreDialogVisible}
setIsVisible={setChoreDialogVisible}
selectedTodo={shoppingTodo}
isShoppingTodo
/>
} }
onInputFocus={onInputFocus} </ToDosContextProvider>
/> </View>
)} </HeaderTemplate>
keyExtractor={(item) => item.id.toString()}
/>
)
: pendingVisible && (
<Text style={styles.noItemTxt}>No items pending approval.</Text>
)}
{/* Approved Section */} {/* Pending Approval Section */}
<View row spread marginT-40 marginB-0 centerV> <View row spread marginT-40 marginB-10 centerV>
<View row centerV> <View row centerV>
<TouchableOpacity row centerV onPress={() => {setApprovedVisible(!approvedVisible)}}> <TouchableOpacity row centerV onPress={() => {setPendingVisible(!pendingVisible)}}>
<Text style={styles.subHeader}>Shopping List</Text> <Text style={styles.subHeader}>Pending Approval</Text>
{approvedVisible && ( {pendingVisible && (
<AntDesign <AntDesign
name="down" name="down"
size={17} size={17}
style={styles.dropIcon} style={styles.dropIcon}
color="#9f9f9f" color="#9f9f9f"
/> />
)} )}
{!approvedVisible && ( {!pendingVisible && (
<AntDesign <AntDesign
name="right" name="right"
size={15} size={15}
style={styles.dropIcon} style={styles.dropIcon}
color="#9f9f9f" color="#9f9f9f"
/> />
)}
</TouchableOpacity>
</View>
<View
centerV
style={{
aspectRatio: 1,
width: 35,
backgroundColor: "#e2eed8",
borderRadius: 50,
}}
>
<Text style={styles.counterNr} center color="#46a80a">
{approvedGroceries?.length.toString()}
</Text>
</View>
</View>
<View style={{marginTop: 8}}>
<EditGroceryItem
editGrocery={{
title: title,
setCategory: setCategory,
category: category,
setTitle: setTitle,
setSubmit: setSubmitted,
closeEdit: handleCancelAddGrocery
}}
onInputFocus={onInputFocus}
/>
</View>
{/* Render Approved Groceries Grouped by Category */}
{approvedGroceries?.length > 0
? approvedVisible && (
<FlatList
data={Object.keys(approvedGroceriesByCategory).sort((a, b) => {
if (a !== "Done") return -1;
if (b === "Done") return 1;
return 0;
})}
renderItem={({item: category}) => (
<View key={category}>
{/* Render Category Header */}
<Text text80M style={{marginTop: 10}} color="#666">
{category}
</Text>
{/* Render Grocery Items for this Category */}
{approvedGroceriesByCategory[category].map(
(grocery: IGrocery) => (
<GroceryItem
key={grocery.id}
item={grocery}
handleItemApproved={(id, changes) =>
updateGroceryItem({...changes, id: id})
}
onInputFocus={onInputFocus}
approvedGroceries={approvedGroceries}
setApprovedGroceries={setApprovedGroceries}
/>
)
)} )}
</View> </TouchableOpacity>
)} </View>
keyExtractor={(category) => category} <View
/> centerV
) style={{
: approvedVisible && ( aspectRatio: 1,
<Text style={styles.noItemTxt}>No approved items.</Text> width: 35,
)} backgroundColor: "#faead2",
</View> borderRadius: 50,
}}
>
<Text style={styles.counterNr} center color="#e28800">
{pendingGroceries?.length.toString()}
</Text>
</View>
</View>
{pendingGroceries?.length > 0
? pendingVisible && (
<FlatList
data={pendingGroceries}
renderItem={({item}) => (
<GroceryItem
item={item}
handleItemApproved={(id, changes) =>
updateGroceryItem({...changes, id: id})
}
onInputFocus={onInputFocus}
/>
)}
keyExtractor={(item) => item.id.toString()}
/>
)
: pendingVisible && (
<Text style={styles.noItemTxt}>No items pending approval.</Text>
)}
{/* Approved Section */}
<View row spread marginT-40 marginB-0 centerV>
<View row centerV>
<TouchableOpacity row centerV onPress={() => {setApprovedVisible(!approvedVisible)}}>
<Text style={styles.subHeader}>Shopping List</Text>
{approvedVisible && (
<AntDesign
name="down"
size={17}
style={styles.dropIcon}
color="#9f9f9f"
/>
)}
{!approvedVisible && (
<AntDesign
name="right"
size={15}
style={styles.dropIcon}
color="#9f9f9f"
/>
)}
</TouchableOpacity>
</View>
<View
centerV
style={{
aspectRatio: 1,
width: 35,
backgroundColor: "#e2eed8",
borderRadius: 50,
}}
>
<Text style={styles.counterNr} center color="#46a80a">
{approvedGroceries?.length.toString()}
</Text>
</View>
</View>
<View style={{marginTop: 8}}>
<EditGroceryItem
editGrocery={{
title: title,
setCategory: setCategory,
category: category,
setTitle: setTitle,
setSubmit: setSubmitted,
closeEdit: handleCancelAddGrocery
}}
onInputFocus={onInputFocus}
/>
</View>
{/* Render Approved Groceries Grouped by Category */}
{approvedGroceries?.length > 0
? approvedVisible && (
<FlatList
data={Object.keys(approvedGroceriesByCategory).sort((a, b) => {
if (a !== "Done") return -1;
if (b === "Done") return 1;
return 0;
})}
renderItem={({item: category}) => (
<View key={category}>
{/* Render Category Header */}
<Text text80M style={{marginTop: 10}} color="#666">
{category}
</Text>
{/* Render Grocery Items for this Category */}
{approvedGroceriesByCategory[category].map(
(grocery: IGrocery) => (
<GroceryItem
key={grocery.id}
item={grocery}
handleItemApproved={(id, changes) =>
updateGroceryItem({...changes, id: id})
}
onInputFocus={onInputFocus}
approvedGroceries={approvedGroceries}
setApprovedGroceries={setApprovedGroceries}
/>
)
)}
</View>
)}
keyExtractor={(category) => category}
/>
)
: approvedVisible && (
<Text style={styles.noItemTxt}>No approved items.</Text>
)}
</View>
</>
); );
}; };
@ -326,7 +334,16 @@ const styles = StyleSheet.create({
counterNr: { counterNr: {
fontFamily: "PlusJakartaSans_600SemiBold", fontFamily: "PlusJakartaSans_600SemiBold",
fontSize: 14 fontSize: 14
} },
loaderContainer: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center',
height: Dimensions.get("window").height,
width: Dimensions.get("window").width,
backgroundColor: 'rgba(218,217,217,0.6)',
zIndex: 999,
},
}); });
export default GroceryList; export default GroceryList;

View File

@ -223,6 +223,7 @@ const ToDoItem = (props: {
{selectedMembers?.map((member) => { {selectedMembers?.map((member) => {
return member?.pfp ? ( return member?.pfp ? (
<ImageBackground <ImageBackground
key={member?.uid}
source={{ uri: member.pfp }} source={{ uri: member.pfp }}
style={{ style={{
height: 24.64, height: 24.64,

View File

@ -8,7 +8,7 @@ import {IToDo} from "@/hooks/firebase/types/todoData";
import DropdownIcon from "@/assets/svgs/DropdownIcon"; import DropdownIcon from "@/assets/svgs/DropdownIcon";
import {Dropdown} from "react-native-element-dropdown"; import {Dropdown} from "react-native-element-dropdown";
import {ProfileType, useAuthContext} from "@/contexts/AuthContext"; import {ProfileType, useAuthContext} from "@/contexts/AuthContext";
import {StyleSheet} from "react-native"; import {ActivityIndicator, Dimensions, StyleSheet} from "react-native";
import {UserProfile} from "@/hooks/firebase/types/profileTypes"; import {UserProfile} from "@/hooks/firebase/types/profileTypes";
const FILTER_OPTIONS = { const FILTER_OPTIONS = {
@ -296,8 +296,14 @@ const ToDosList = ({ isSettings, members }: { isSettings?: boolean, members?: Ar
); );
}; };
const containerHeight = Dimensions.get('screen').height;
return ( return (
<View marginB-402> <View style={{ marginBottom: 110 }} >
{!toDos &&
<View style={styles.loaderContainer}>
<ActivityIndicator size="large" color="blue" />
</View>
}
<Dropdown <Dropdown
style={{ marginBottom: 15 }} style={{ marginBottom: 15 }}
data={filterOptions} data={filterOptions}
@ -379,6 +385,12 @@ const styles = StyleSheet.create({
}, },
dropdownStyle: { borderRadius: 6.61, minHeight: 10, maxHeight: "100%", width: 187 }, dropdownStyle: { borderRadius: 6.61, minHeight: 10, maxHeight: "100%", width: 187 },
itemStyle: { padding: 0, margin: 0 }, itemStyle: { padding: 0, margin: 0 },
loaderContainer: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center',
zIndex: 999,
},
}); });
export default ToDosList; export default ToDosList;

View File

@ -1,17 +1,17 @@
import { Button, Text, View } from "react-native-ui-lib"; import { Button, Text, View } from "react-native-ui-lib";
import React, { useState } from "react"; import React from "react";
import HeaderTemplate from "@/components/shared/HeaderTemplate"; import HeaderTemplate from "@/components/shared/HeaderTemplate";
import AddChore from "./AddChore"; import AddChore from "./AddChore";
import ProgressCard from "./ProgressCard"; import ProgressCard from "./ProgressCard";
import ToDosList from "./ToDosList"; import ToDosList from "./ToDosList";
import { Dimensions, ScrollView, StyleSheet } from "react-native"; import { Dimensions, ScrollView, StyleSheet} from "react-native";
import { TouchableOpacity } from "react-native-gesture-handler"; import { TouchableOpacity } from "react-native-gesture-handler";
import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
import FamilyChoresProgress from "./family-chores/FamilyChoresProgress"; import FamilyChoresProgress from "./family-chores/FamilyChoresProgress";
import UserChoresProgress from "./user-chores/UserChoresProgress"; import UserChoresProgress from "./user-chores/UserChoresProgress";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { toDosPageIndex } from "../calendar/atoms"; import { toDosPageIndex } from "../calendar/atoms";
import {useGetFamilyMembers} from "@/hooks/firebase/useGetFamilyMembers"; import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
const ToDosPage = () => { const ToDosPage = () => {
const { profileData } = useAuthContext(); const { profileData } = useAuthContext();
@ -19,7 +19,7 @@ const ToDosPage = () => {
const { data: members } = useGetFamilyMembers(); const { data: members } = useGetFamilyMembers();
const { width, height } = Dimensions.get("screen"); const { width } = Dimensions.get("screen");
const pageLink = ( const pageLink = (
<TouchableOpacity onPress={() => setPageIndex(1)}> <TouchableOpacity onPress={() => setPageIndex(1)}>
<Text color="#ea156d" style={{ fontSize: 14 }}> <Text color="#ea156d" style={{ fontSize: 14 }}>
@ -35,7 +35,6 @@ const ToDosPage = () => {
> >
<View <View
paddingH-25 paddingH-25
height={"100%"}
width={width} width={width}
> >
{pageIndex == 0 && ( {pageIndex == 0 && (