Merge branch 'dev'

# Conflicts:
#	app/_layout.tsx
#	firebase/functions/index.js
This commit is contained in:
Milan Paunovic
2025-02-15 16:44:45 +01:00
13 changed files with 2548 additions and 690 deletions

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;
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",
@ -153,4 +159,4 @@ const styles = StyleSheet.create({
},
});
export default AddFeedback;
export default AddFeedback;

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>
);
};
export default FeedbackList;
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%",
}}