fixes, deleteFamily function, household signup

This commit is contained in:
ivic00
2025-02-15 00:34:42 +01:00
parent a8957c7ac7
commit f649828d80
14 changed files with 2559 additions and 698 deletions

View File

@ -0,0 +1,87 @@
import { SafeAreaView } from "react-native-safe-area-context";
import { Button, Text, View, TextField } from "react-native-ui-lib";
import React, { useState } from "react";
import { useRouter } from "expo-router";
import { StyleSheet } from "react-native";
import { useAuthContext } from "@/contexts/AuthContext";
import { useUpdateHouseholdName } from "@/hooks/firebase/useUpdateHouseholdName";
export default function NewHouseholdScreen() {
const router = useRouter();
const { user, profileData } = useAuthContext();
const [householdName, setHouseholdName] = useState("");
const { mutateAsync: newHousehold } = useUpdateHouseholdName();
const handleContinue = async () => {
try {
if(profileData?.familyId)
newHousehold({familyId: profileData?.familyId, name: householdName}).then(() => router.push("/(unauth)/cal_sync"));
} catch (error) {
console.error("Error saving household name:", error);
}
};
return (
<SafeAreaView style={{ flex: 1 }}>
<View
style={{
flex: 1,
padding: 21,
paddingBottom: 45,
paddingTop: "20%",
alignItems: "center",
}}
>
<View gap-13 width={"100%"} marginB-20>
<Text style={{ fontSize: 40, fontFamily: "Manrope_600SemiBold" }}>
Name your household
</Text>
<Text color={"#919191"} style={{ fontSize: 20 }}>
Give your family group a unique name!
</Text>
</View>
<View width={"100%"} flexG>
<TextField
value={householdName}
onChangeText={setHouseholdName}
placeholder="Enter household name"
style={styles.textfield}
textAlign="center"
/>
</View>
<View flexG />
<View width={"100%"}>
<Button
label="Continue"
onPress={handleContinue}
style={{
height: 50,
}}
backgroundColor="#fd1775"
labelStyle={{
fontFamily: "PlusJakartaSans_600SemiBold",
fontSize: 16,
}}
/>
</View>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
textfield: {
backgroundColor: "white",
marginVertical: 100,
padding: 30,
height: 44,
borderRadius: 50,
fontFamily: "PlusJakartaSans_300Light",
fontSize: 15,
color: "#919191",
alignContent: "center",
},
});

View File

@ -57,6 +57,8 @@ import KeyboardManager from 'react-native-keyboard-manager';
import {enableScreens} from 'react-native-screens';
import {PersistQueryClientProvider} from "@/contexts/PersistQueryClientProvider";
import auth from "@react-native-firebase/auth";
import firestore from '@react-native-firebase/firestore';
import functions from '@react-native-firebase/functions';
enableScreens(true)

View File

@ -13,8 +13,8 @@ interface IAddBrainDumpProps {
}
const AddBrainDump = ({
addBrainDumpProps,
}: {
addBrainDumpProps,
}: {
addBrainDumpProps: IAddBrainDumpProps;
}) => {
const {addBrainDump} = useBrainDumpContext();
@ -22,11 +22,11 @@ const AddBrainDump = ({
const [dumpDesc, setDumpDesc] = useState<string>("");
const {width} = Dimensions.get("screen");
// Refs for the two TextFields
const descriptionRef = useRef<TextFieldRef>(null);
const titleRef = useRef<TextFieldRef>(null);
const isTitleValid = dumpTitle.trim().length >= 3;
useEffect(() => {
setDumpDesc("");
setDumpTitle("");
@ -40,9 +40,9 @@ const AddBrainDump = ({
}
}, [addBrainDumpProps.isVisible]);
useEffect(() => {
if (Platform.OS === "ios") KeyboardManager.setEnableAutoToolbar(false);
}, []);
useEffect(() => {
if (Platform.OS === "ios") KeyboardManager.setEnableAutoToolbar(false);
}, []);
return (
<Dialog
@ -69,18 +69,17 @@ const AddBrainDump = ({
<Button
color="#05a8b6"
label="Save"
style={styles.topBtn}
style={[styles.topBtn, !isTitleValid && styles.disabledBtn]}
disabled={!isTitleValid}
onPress={() => {
addBrainDump({
id: '99',
title: dumpTitle.trimEnd().trimStart(),
description: dumpDesc.trimEnd().trimStart(),
});
addBrainDumpProps.setIsVisible(false);
if (isTitleValid) {
addBrainDump({
id: '99',
title: dumpTitle.trim(),
description: dumpDesc.trim(),
});
addBrainDumpProps.setIsVisible(false);
}
}}
/>
</View>
@ -94,11 +93,10 @@ const AddBrainDump = ({
setDumpTitle(text);
}}
onSubmitEditing={() => {
// Move focus to the description field
descriptionRef.current?.focus();
}}
style={styles.title}
blurOnSubmit={false} // Keep the keyboard open when moving focus
blurOnSubmit={false}
returnKeyType="next"
/>
<View height={2} backgroundColor="#b3b3b3" width={"100%"} marginB-20/>
@ -125,28 +123,31 @@ const AddBrainDump = ({
};
const styles = StyleSheet.create({
dialogContainer: {
borderTopRightRadius: 15,
borderTopLeftRadius: 15,
backgroundColor: "white",
padding: 0,
paddingTop: 3,
margin: 0,
},
topBtns: {},
topBtn: {
backgroundColor: "white",
color: "#05a8b6",
},
title: {
fontSize: 22,
fontFamily: "Manrope_500Medium",
},
description: {
fontFamily: "Manrope_400Regular",
fontSize: 14,
textAlignVertical: "top",
},
dialogContainer: {
borderTopRightRadius: 15,
borderTopLeftRadius: 15,
backgroundColor: "white",
padding: 0,
paddingTop: 3,
margin: 0,
},
topBtns: {},
topBtn: {
backgroundColor: "white",
color: "#05a8b6",
},
disabledBtn: {
opacity: 0.2,
},
title: {
fontSize: 22,
fontFamily: "Manrope_500Medium",
},
description: {
fontFamily: "Manrope_400Regular",
fontSize: 14,
textAlignVertical: "top",
},
});
export default AddBrainDump;

View File

@ -20,14 +20,10 @@ const DumpList = (props: { searchText: string }) => {
return (
<View marginB-70>
{brainDumps?.length ? <FlatList
style={{ zIndex: -1 }}
data={sortedDumps}
keyExtractor={(item) => item.title}
renderItem={({ item }) => (
<BrainDumpItem key={item.title} item={item} />
)}
/> : <Text marginT-20 center style={styles.alert}>You have no notes</Text>}
{sortedDumps?.length ? (
sortedDumps.map((item) => (
<BrainDumpItem key={item.id} item={item} />
))) : <Text marginT-20 center style={styles.alert}>You have no notes</Text>}
</View>
);
};

View File

@ -32,8 +32,11 @@ const AddFeedback = ({
const descriptionRef = useRef<TextFieldRef>(null);
const titleRef = useRef<TextFieldRef>(null);
const isTitleValid = feedbackTitle.trim().length >= 3;
useEffect(() => {
setFeedback("");
setFeedbackTitle("");
}, [addFeedbackProps.isVisible]);
useEffect(() => {
@ -46,9 +49,8 @@ const AddFeedback = ({
useEffect(() => {
if (Platform.OS === "ios") KeyboardManager.setEnableAutoToolbar(false);
setFeedbackTitle("");
setFeedback("");
setFeedbackTitle('');
setFeedback('');
}, []);
return (
@ -76,16 +78,17 @@ const AddFeedback = ({
<Button
color="#05a8b6"
label="Save"
style={styles.topBtn}
style={[styles.topBtn, !isTitleValid && styles.disabledBtn]}
disabled={!isTitleValid}
onPress={() => {
addFeedback({
id: 99,
title: feedbackTitle.trimEnd().trimStart(),
text: feedback.trimEnd().trimStart(),
});
addFeedbackProps.setIsVisible(false);
if (isTitleValid) {
addFeedback({
id: 99,
title: feedbackTitle.trim(),
text: feedback.trim(),
});
addFeedbackProps.setIsVisible(false);
}
}}
/>
</View>
@ -142,6 +145,9 @@ const styles = StyleSheet.create({
backgroundColor: "white",
color: "#05a8b6",
},
disabledBtn: {
opacity: 0.2,
},
title: {
fontSize: 22,
fontFamily: "Manrope_500Medium",

View File

@ -10,7 +10,7 @@ const Feedback = (props: { item: IFeedback }) => {
const [isVisible, setIsVisible] = useState<boolean>(false);
return (
<View>
<View key={props.item.id}>
<TouchableWithoutFeedback onPress={() => setIsVisible(true)}>
<View
backgroundColor="white"

View File

@ -1,13 +1,13 @@
import { View } from "react-native-ui-lib";
import { View, Text } from "react-native-ui-lib";
import React from "react";
import { FlatList } from "react-native";
import { StyleSheet } from "react-native";
import { useFeedbackContext } from "@/contexts/FeedbackContext";
import Feedback from "./Feedback";
const FeedbackList = (props: { searchText: string }) => {
const { feedbacks } = useFeedbackContext();
const filteredBrainDumps =
const filteredFeedbacks =
props.searchText.trim() === ""
? feedbacks
: feedbacks.filter(
@ -20,16 +20,24 @@ const FeedbackList = (props: { searchText: string }) => {
return (
<View marginB-70>
<FlatList
style={{ zIndex: -1 }}
data={filteredBrainDumps}
keyExtractor={(item) => item.title}
renderItem={({ item }) => (
<Feedback key={item.title} item={item} />
)}
/>
{filteredFeedbacks?.length ? (
filteredFeedbacks.map((item) => (
<Feedback key={item.id} item={item} />
))
) : (
<Text marginT-20 center style={styles.alert}>
You have no Feedbacks
</Text>
)}
</View>
);
};
const styles = StyleSheet.create({
alert: {
fontFamily: "PlusJakartaSans_300Light",
fontSize: 20
}
});
export default FeedbackList;

View File

@ -165,21 +165,18 @@ const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
</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 && (
pendingGroceries.map((item) => (
<GroceryItem
key={item.id.toString()}
item={item}
handleItemApproved={(id, changes) =>
updateGroceryItem({...changes, id: id})
}
onInputFocus={onInputFocus}
/>
))
)
: pendingVisible && (
<Text style={styles.noItemTxt}>No items pending approval.</Text>
)}
@ -230,38 +227,34 @@ const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
{/* Render Approved Groceries Grouped by Category */}
{approvedGroceries?.length > 0
? approvedVisible && (
<FlatList
data={Object.keys(approvedGroceriesByCategory).sort((a, b) => {
Object.keys(approvedGroceriesByCategory)
.sort((a, b) => {
if (a !== "Done") return -1;
if (b === "Done") return 1;
return 0;
})}
renderItem={({item: category}) => (
})
.map((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}
/>
)
)}
{/* 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 File

@ -41,14 +41,9 @@ const GroceryWrapper = () => {
const handleInputFocus = (y: number) => {
if (scrollViewRef.current) {
// Get the window height
const windowHeight = Dimensions.get('window').height;
// Calculate the space we want to leave at the top
const topSpacing = 20;
// Calculate the target scroll position:
// y (position of input) - topSpacing (space we want at top)
// if keyboard is shown, we need to account for its height
const scrollPosition = Math.max(0, y - topSpacing);
scrollViewRef.current.scrollTo({

View File

@ -22,14 +22,14 @@ const DeleteProfileDialogs: React.FC<ConfirmationDialogProps> = ({
}) => {
const [confirmationDialog, setConfirmationDialog] = useState<boolean>(false);
const [input, setInput] = useState<string>("");
const [isCorrect, setIsCorrect] = useState<boolean>(true);
const [isCorrect, setIsCorrect] = useState<boolean>(false);
useEffect(() => {
setInput("");
}, [onDismiss, onConfirm])
useEffect(() => {
setIsCorrect(input === householdName);
setIsCorrect(input !== "" && input === householdName);
}, [input])

View File

@ -246,6 +246,7 @@ const ToDoItem = (props: {
/>
) : (
<View
key={member.uid}
style={{
position: "relative",
width: 24.64,
@ -260,7 +261,7 @@ const ToDoItem = (props: {
backgroundColor: member.eventColor || "#ccc",
justifyContent: "center",
alignItems: "center",
borderRadius: 100, // Circular shape
borderRadius: 100,
width: "100%",
height: "100%",
}}

View File

@ -1813,7 +1813,8 @@ exports.updateHouseholdTimestampOnEventUpdate = functions.firestore
'BrainDumps',
'Groceries',
'Todos',
'Events'
'Events',
//'Feedbacks'
];
for (const collectionName of collections) {
@ -1829,13 +1830,16 @@ exports.updateHouseholdTimestampOnEventUpdate = functions.firestore
batch.delete(profile.ref);
}
const householdDoc = await db.collection('Households')
.doc(familyId)
.get();
const householdSnapshot = await db.collection('Households')
.where('familyId', '==', familyId)
.get();
if (householdDoc.exists) {
batch.delete(householdDoc.ref);
}
if (!householdSnapshot.empty) {
const householdDoc = householdSnapshot.docs[0];
batch.delete(householdDoc.ref);
} else {
console.log('Household not found for familyId:', familyId);
}
await batch.commit();

File diff suppressed because it is too large Load Diff

View File

@ -2,9 +2,11 @@ import { useAuthContext } from "@/contexts/AuthContext";
import { useMutation } from "@tanstack/react-query";
import functions from '@react-native-firebase/functions';
import { Alert } from 'react-native';
import { useSignOut } from './useSignOut';
export const useDeleteFamily = () => {
const { user } = useAuthContext();
const signOut = useSignOut();
return useMutation({
mutationKey: ["deleteFamily"],
@ -16,6 +18,11 @@ export const useDeleteFamily = () => {
try {
const deleteFamilyFunction = functions().httpsCallable('deleteFamily');
const result = await deleteFamilyFunction({ familyId });
if (result.data.success) {
await signOut.mutateAsync();
}
return result.data;
} catch (error: any) {
if (error.code === 'permission-denied') {