diff --git a/screens/Read.tsx b/screens/Read.tsx index 1c852f7..7ff0216 100644 --- a/screens/Read.tsx +++ b/screens/Read.tsx @@ -233,7 +233,11 @@ export default function Read({ route }: Props) { /> {/* button: EDIT nutritable */} - navigation.navigate('Update')}> + + navigation.navigate('Update', { food, nutritable: selectedNutritable }) + }> ; +import React, { useEffect, useState } from 'react'; +import { StyleSheet, TextInput } from 'react-native'; +import { Button, Colors, KeyboardAwareScrollView, Text, View } from 'react-native-ui-lib'; +import { Portal } from 'react-native-paper'; +import { useQuery } from '@tanstack/react-query'; +import { getAllUnits } from 'database/queries/unitsQueries'; +import { SQLiteDatabase, useSQLiteContext } from 'expo-sqlite'; + +import { useColors } from 'context/ColorContext'; + +// components +import Dialogs from 'components/Shared/Dialogs'; +import IconSVG from 'components/Shared/icons/IconSVG'; +import UnitPicker from 'components/Shared/UnitPicker'; +import MacrosBarChart from 'components/Screens/Create/MacrosBarChart'; +import MacroInputField from 'components/Screens/Create/MacroInputField'; + +import { Food, Nutritable, Unit } from 'database/types'; +import { getAllFoodNames } from 'database/queries/foodsQueries'; + +import calculateCalories from 'utils/calculateCalories'; +import { validateFoodInputs } from 'utils/validation/validateFood'; +import { Validation, ValidationStatus } from 'utils/validation/types'; +import { StaticScreenProps } from '@react-navigation/native'; + +type Props = StaticScreenProps<{ + food: Food; + nutritable: Nutritable; +}>; + +// (!!!) This screen produces a warning due to nesting UnitPicker inside a KeyboardAwareScrollView. +// Nevertheless, this is strictly necessary to avoid the keyboard from covering the bottom-most input fields. +export default function Update({ route }: Props) { + const { food, nutritable } = route.params; + + const colors = useColors(); + const database: SQLiteDatabase = useSQLiteContext(); + + // Fetches the names of all existing foods + const { data: names = [], isFetched: namesFetched } = useQuery({ + queryKey: ['allNames'], + queryFn: () => getAllFoodNames(database), + initialData: [], + }); + + // Stateful nutritional data + // (!) Attention: whenever kcals would be used, first check whether it has any input. + // If it doesn't use expectedKcals instead for a smoother user experience. + const [name, setName] = useState(food.name); + const [kcals, setKcals] = useState(nutritable.kcals.toString()); + const [measure, setMeasure] = useState(nutritable.baseMeasure.toString()); + const [fat, setFat] = useState(nutritable.fats.toString()); + const [carbs, setCarbs] = useState(nutritable.carbs.toString()); + const [protein, setProtein] = useState(nutritable.protein.toString()); + + // Calculates expected kcals + const [expectedKcals, setExpectedKcals] = useState(''); + useEffect(() => { + setExpectedKcals( + calculateCalories(Number(protein), Number(carbs), Number(fat)).toFixed(2).toString() + ); + }, [protein, carbs, fat]); + + // Initializes the validation status + const [validationAttempted, setValidationAttempted] = useState(false); + const [validation, setValidation] = useState({ + status: ValidationStatus.Valid, + errors: [], + }); + + // Resets the validation status + const resetValidation = () => { + setValidationAttempted(false); + setValidation({ + status: ValidationStatus.Valid, + errors: [], + }); + }; + + // Resets the validation status every time a field changes + useEffect(resetValidation, [name, kcals, measure, fat, protein, carbs]); + + // Controls the showing of warnings and errors + const [showDialogs, setShowDialogs] = useState(false); + useEffect(() => { + setShowDialogs(validation.status !== ValidationStatus.Valid); + }, [validation]); + + return ( + <> + {/* Using a portal is needed because the nested scrollViews mess up the Dialog component */} + + + {/* Show the warnings here component is done */} + + + + + setName(text.length === 0 ? food.name : text)} + placeholderTextColor={Colors.violet40} + style={styles.nameInput} + /> + + {/* Graph */} + + {/* Macros & Unit */} + + {/* Macros */} + + {/* Fat */} + setFat(text)} + color={colors.get('fat')} + unitSymbol={'g'} + iconName={'bacon-solid'} + maxLength={7} + /> + {/* Carbs */} + setCarbs(text)} + color={colors.get('carbs')} + unitSymbol={'g'} + iconName={'wheat-solid'} + maxLength={7} + /> + {/* Protein */} + setProtein(text)} + color={colors.get('protein')} + unitSymbol={'g'} + iconName={'meat-solid'} + maxLength={7} + /> + + {/* Unit */} + + + + + + { + /* does nothing */ + }} + /> + + + {/* Quantity & Calories */} + + {/* Calories */} + setKcals(text)} + unitSymbol={'kcal'} + unitIndicatorWidth={60} + iconName={'ball-pile-solid'} + maxLength={9} + placeholder={expectedKcals} + /> + {/* Measure */} + setMeasure(text)} + unitSymbol={nutritable.unit.symbol} + unitIndicatorWidth={60} + iconName={'scale-unbalanced-solid'} + maxLength={7} + /> + + {/* Submit Button */} +