From 95f1bc989ccf6c9fe21c2b727da26ffa87c635c0 Mon Sep 17 00:00:00 2001 From: Scott Houghton <94723197+EdwardHoughton@users.noreply.github.com> Date: Wed, 10 May 2023 11:09:04 -0500 Subject: [PATCH 1/2] Comments --- src/screens/Home/HomeScreen.js | 12 ++ .../IngredientSearch/IngredientItem.js | 7 + .../IngredientSearchScreen.js | 24 +++ src/screens/MealPlan/MealPlanScreen.js | 18 ++ src/screens/Pantry/PantryScreen.js | 169 +++--------------- 5 files changed, 88 insertions(+), 142 deletions(-) diff --git a/src/screens/Home/HomeScreen.js b/src/screens/Home/HomeScreen.js index 2da77f3..b7d4cd4 100644 --- a/src/screens/Home/HomeScreen.js +++ b/src/screens/Home/HomeScreen.js @@ -81,10 +81,22 @@ const HomeScreen = () => { }); }, []); // Remove sortBy dependency to disable sorting + + + //function called handleRecipePress that takes in a parameter recipe. + //When this function is called, it navigates to a screen called "Recipe" and passes in the recipe parameter as a prop. + //The navigation object is assumed to be available in the current context. const handleRecipePress = (recipe) => { navigation.navigate("Recipe", { recipe }); }; + + + //function called renderRecipe that takes an object as input with an item property. + //The function returns a TouchableOpacity component with some styles and a callback function that is executed + //when the component is pressed. Inside the TouchableOpacity, there is a View component that contains + //an Image component and a Text component. The Text components display some information about the recipe item, + //such as the recipe title, servings, and price per serving if available. const renderRecipe = ({ item }) => { return ( { return ( diff --git a/src/screens/IngredientSearch/IngredientSearchScreen.js b/src/screens/IngredientSearch/IngredientSearchScreen.js index 82555c9..43dd220 100644 --- a/src/screens/IngredientSearch/IngredientSearchScreen.js +++ b/src/screens/IngredientSearch/IngredientSearchScreen.js @@ -6,6 +6,19 @@ import styles from "./styles"; import { searchIngredientsByName } from "../../utils/APICalls/Spoonacular/ingredients"; import { addIngredientToPantry } from "../../utils/APICalls/SimplKitchen/pantry"; + + +//a functional component called IngredientSearchScreen. +//The component takes a navigation prop and uses the useState hook to define state variables search and ingredients. +//The fetchData function is defined using the async/await syntax, which fetches a list of ingredients based on the search +//query from an external API, logs the first ingredient's possible units to the console, and sets the ingredients state +//to the fetched list. The handleAddIngredient function is defined to add an ingredient to a pantry and navigate to a +//screen called "Pantry" with a callback function fetchData passed as a parameter. The onPressIngredient function +//navigates to a screen called "Ingredient" with an ingredient prop passed as a parameter. +//Finally, the renderIngredient function takes an object with an item property as its argument +//returns an React component with the item property passed as a prop along with onPressIngredient +//and handleAddIngredient functions. The component returns a view that displays a search bar and a list of ingredients +//with the FlatList component. const IngredientSearchScreen = ({ navigation }) => { const [search, setSearch] = useState(""); const [ingredients, setIngredients] = useState([]); @@ -21,6 +34,12 @@ const IngredientSearchScreen = ({ navigation }) => { } }; + + + //function called handleAddIngredient that takes in an ingredient parameter. + //The function first tries to add the ingredient to a pantry by calling an asynchronous function addIngredientToPantry. + //If successful, the function navigates to a screen called "Pantry" and passes a callback function fetchData as a parameter. + //If an error occurs, it is logged to the console. const handleAddIngredient = async (ingredient) => { try { await addIngredientToPantry(ingredient); @@ -36,6 +55,11 @@ const IngredientSearchScreen = ({ navigation }) => { navigation.navigate("Ingredient", { ingredient }); }; + + + //function called renderIngredient that takes an object with an item property as its argument. + //The function returns an React component with the item property passed as a prop along with + //two functions onPressIngredient and handleAddIngredient. The key prop is set to item.id. const renderIngredient = ({ item }) => ( { const [mealPlan, setMealPlan] = useState(null); const [selectedDay, setSelectedDay] = useState("monday"); @@ -44,10 +51,17 @@ const MealPlanScreen = () => { } }; + + //function called saveMealPlans, which takes in a parameter called meals. + //This function uses the AsyncStorage API to store the meals parameter as a JSON string in local storage. + //The async keyword in the function signature means that the function returns a promise and can be awaited. const saveMealPlans = async (meals) => { await AsyncStorage.setItem("mealPlans", JSON.stringify(meals)); }; + + //uses AsyncStorage to get stored meal plans. + //If there are any stored meal plans, they are parsed into JSON and set with setMealPlan. const loadMealPlans = async () => { const storedMealPlans = await AsyncStorage.getItem("mealPlans"); if (storedMealPlans !== null) { @@ -55,6 +69,10 @@ const MealPlanScreen = () => { } }; + + // function called getMealPlans. It tries to generate a meal plan for a week using the generateMealPlanWeek() function + //and sets the fetched meal plan using setMealPlan(). It also saves the meal plan using saveMealPlans(). + //If an error occurs in fetching the meal plan, it logs the error message to the console using console.error(). const getMealPlans = async () => { try { const fetchedMealPlan = await generateMealPlanWeek(); diff --git a/src/screens/Pantry/PantryScreen.js b/src/screens/Pantry/PantryScreen.js index cc81280..98c61ec 100644 --- a/src/screens/Pantry/PantryScreen.js +++ b/src/screens/Pantry/PantryScreen.js @@ -1,146 +1,4 @@ -// import React, { useState, useEffect } from "react"; -// import axios from "axios"; -// import { -// View, -// Text, -// FlatList, -// TouchableOpacity, -// Image, -// StyleSheet, -// } from "react-native"; -// import MenuButton from "../../components/MenuButton/MenuButton"; -// import IconButton from "../../components/IconButton/IconButton"; -// import styles from "./styles"; - -// const { -// getUsersIngredients, -// removeIngredientFromPantry, -// updateIngredientAmount, -// } = require("../../utils/APICalls/SimplKitchen/pantry"); - - - -// const PantryScreen = ({ navigation, route }) => { -// const [pantryIngredients, setPantryIngredients] = useState([]); - -// useEffect(() => { -// fetchData(); -// }, []); - -// useEffect(() => { -// fetchData(); -// navigation.setOptions({ -// drawerLockMode: "locked-closed", -// headerLeft: () => ( -// { -// navigation.openDrawer(); -// }} -// /> -// ), -// }); -// }, []); - - - -// const fetchData = async () => { -// try { -// const results = await getUsersIngredients(); -// setPantryIngredients(results.ingredients); -// } catch (error) { -// console.log(error); -// } -// }; - -// const handleAddIngredient = (ingredient) => { -// setPantryIngredients([...pantryIngredients, ingredient]); - -// }; - - -// const onRemoveIngredient = async (index) => { -// try { -// if (pantryIngredients[index].amount > 1) { -// pantryIngredients[index].amount -= 1; -// await updateIngredientAmount(pantryIngredients[index]); -// } else { -// await removeIngredientFromPantry(pantryIngredients[index]); -// } -// fetchData(); -// } catch (error) { -// console.log(error); -// } -// }; - - -// //If any errors occur, they are logged to the console. -// const onAddIngredient = async (index) => { -// try { -// pantryIngredients[index].amount += 1; -// await updateIngredientAmount(pantryIngredients[index]); -// fetchData(); -// } catch (error) { -// console.log(error); -// } -// }; - - - -// const renderItem = ({ item, index }) => ( -// -// -// -// {item.ingredientName} -// -// onRemoveIngredient(index)} -// /> -// {item.amount} -// onAddIngredient(index)} /> -// -// -// onRemoveIngredient(index)} -// style={styles.removeButton} -// /> -// -// ); - -// return ( -// -// index.toString()} -// ListEmptyComponent={() => ( -// -// Your pantry is empty -// -// )} -// /> -// -// navigation.navigate("IngredientSearch", { screen: "IngredientSearch" }) -// } -// > -// Add Ingredient -// -// -// ); -// }; - -// export default PantryScreen; import React, { useState, useEffect } from "react"; import axios from "axios"; import { @@ -163,10 +21,22 @@ const { updateIngredientAmount, } = require("../../utils/APICalls/SimplKitchen/pantry"); + + //function called wait that takes a timeout parameter. + //The function returns a Promise that resolves after the specified timeout period in milliseconds. + //Essentially, this function can be used to delay the execution of a piece of code. const wait = (timeout) => { return new Promise(resolve => setTimeout(resolve, timeout)); } + + + //defines a screen component called PantryScreen for a mobile app that allows users to manage items in their pantry. + //The component uses React hooks to manage state and make asynchronous calls to a backend API to retrieve, update + //and delete pantry items. The component also defines a FlatList to display the pantry items and provides user + //interface elements such as buttons and icons to add, remove, and update pantry items. + //Finally, the component returns a view with the FlatList and a button to navigate to a screen to search for + //new ingredients to add to the pantr const PantryScreen = ({ navigation, route }) => { const [pantryIngredients, setPantryIngredients] = useState([]); const [refreshing, setRefreshing] = useState(false); @@ -204,12 +74,27 @@ const PantryScreen = ({ navigation, route }) => { } }; + + + //function named handleAddIngredient that takes an ingredient parameter. + //When called, it adds the ingredient to an array of pantryIngredients using the setPantryIngredients function. + //The setPantryIngredients function updates the state of the component while spreading the existing pantryIngredients + //array and adding the new ingredient at the end. const handleAddIngredient = (ingredient) => { setPantryIngredients([...pantryIngredients, ingredient]); }; + + + //asynchronous function called onRemoveIngredient that takes an index parameter. + //Within the function, it checks if the amount property of an ingredient object at the specified index + //in an array called pantryIngredients is greater than 1. If it is, it subtracts 1 from the amount property + //and updates the ingredient amount using an asynchronous function called updateIngredientAmount. + //If the amount property is 1 or less, it removes the ingredient from the pantry using another asynchronous + //function called removeIngredientFromPantry. Finally, the function calls fetchData, which is presumably another + //asynchronous function that fetches some data. If any errors occur within the try block, they are caught and logged to the console. const onRemoveIngredient = async (index) => { try { if (pantryIngredients[index].amount > 1) { From 9f69f83ef8f5a8b9ff0daab18cfa1e0f272b03d5 Mon Sep 17 00:00:00 2001 From: Scott Houghton <94723197+EdwardHoughton@users.noreply.github.com> Date: Wed, 10 May 2023 11:23:24 -0500 Subject: [PATCH 2/2] Comments --- src/screens/Profile/ProfileScreen.js | 18 ++++++++ src/screens/Recipe/RecipeScreen.js | 41 +++++++++++++++++++ .../RecipeGenerator/RecipeGeneratorScreen.js | 21 ++++++++++ .../ShoppingList/ShoppingListScreen.js | 26 ++++++++++++ .../APICalls/SimplKitchen/generateRecipes.js | 12 ++++++ 5 files changed, 118 insertions(+) diff --git a/src/screens/Profile/ProfileScreen.js b/src/screens/Profile/ProfileScreen.js index 1e96a56..7a6d588 100644 --- a/src/screens/Profile/ProfileScreen.js +++ b/src/screens/Profile/ProfileScreen.js @@ -12,6 +12,12 @@ import { import styles from "./styles"; + +//component called ProfileScreen which renders a user profile page. +//The page has two multiple select lists of dietary preferences and intolerances. +//The component uses the useState and useEffect hooks to manage component state and fetch user information when the component mounts. +//The handleSave function is executed when the user saves their selections, and it calls two functions to update the user's preferences. +//The handleLogout function logs the user out. export default function ProfileScreen(props) { const [selected1, setSelected1] = useState([]); const [selected2, setSelected2] = useState([]); @@ -46,6 +52,11 @@ export default function ProfileScreen(props) { { key: "10", value: "Whole30" }, ]; + + + //function called handleSave that makes two API calls to update userIntolerances and userDiets with selected values. + //If either API call fails, an error is logged to the console. After both API calls complete successfully, an alert message + //is shown to the user indicating that the selected items have been saved. const handleSave = async () => { try { await updateUserIntolerences(selected1); @@ -56,6 +67,13 @@ export default function ProfileScreen(props) { alert("Saved selected items"); }; + + + //defines a function called handleLogout. + //The function is an asynchronous function, denoted by the async keyword. When the function is called, + //it logs a message to the console saying "Logout clicked". Then it tries to call a function called logoutSimplKitchen() + //using the await keyword, which means it will wait for that function to finish before continuing. + //If an error occurs during the execution of logoutSimplKitchen(), the function catches the error and does nothing. const handleLogout = async () => { console.log("Logout clicked"); try { diff --git a/src/screens/Recipe/RecipeScreen.js b/src/screens/Recipe/RecipeScreen.js index 226f8d3..dbeb91e 100644 --- a/src/screens/Recipe/RecipeScreen.js +++ b/src/screens/Recipe/RecipeScreen.js @@ -14,6 +14,14 @@ import { FontAwesome } from "@expo/vector-icons"; import styles from "./styles"; + + +// functional component called RecipeScreen that displays the details of a recipe. +//It receives a route prop that contains the recipe information. +//The component fetches additional recipe information using two API calls and updates the state variables accordingly. +//The component also allows the user to edit the servings, ingredients, and instructions of the recipe +//and save the changes to AsyncStorage. Finally, the component displays the recipe details and +//allows the user to switch between the ingredients and instructions tabs. const RecipeScreen = ({ route }) => { const { recipe } = route.params; const [recipeDetails, setRecipeDetails] = useState(null); @@ -28,6 +36,13 @@ const RecipeScreen = ({ route }) => { const [editedInstructions, setEditedInstructions] = useState([]); const navigation = useNavigation(); + + + //function called fetchRecipeDetails that makes two API calls using the fetch function. + //The first call retrieves recipe information from the Spoonacular API using an API key and sets the recipe details, + //edited ingredients, and edited instructions using the response data. The second call retrieves the recipe summary and + //sets additional recipe details using the response data. If there is an error in either API call, it will be caught and + // logged to the console. const fetchRecipeDetails = async () => { try { const response = await fetch( @@ -58,6 +73,10 @@ const RecipeScreen = ({ route }) => { fetchRecipeDetails(); }, []); + + // function called handleServingsChange that takes in a parameter called newServings. + //It then parses newServings as an integer using parseInt() and checks if the result is not NaN. + //If it is a valid integer, it sets the Servings state to the parsed value using setServings(). const handleServingsChange = (newServings) => { const parsedValue = parseInt(newServings, 10); if (!isNaN(parsedValue)) { @@ -65,10 +84,21 @@ const RecipeScreen = ({ route }) => { } }; + + + const toggleEditServings = () => { setEditServings(!editServings); }; + + + //function called calculateNewAmount that takes an object ingredient as an argument. + //It calculates the new amount of the ingredient needed based on the number of servings specified. + //If no editedServings value is provided, it either uses the servings value or the default recipeDetails.servings value. + //If the number of servings is the same as the default, it returns the original ingredient.amount. + //Otherwise, it calculates the new amount by dividing the original amount by the default servings + //and multiplying it by the new number of servings. The final result is rounded to two decimal places using the toFixed method. const calculateNewAmount = (ingredient) => { const servingsToUse = editedServings || (servings ? servings : recipeDetails.servings); @@ -80,7 +110,13 @@ const RecipeScreen = ({ route }) => { (ingredient.amount / recipeDetails.servings) * servingsToUse; return newAmount.toFixed(2); }; + + // function onSave that saves recipe information to local storage. + //It retrieves existing recipe data from storage, parses it as JSON if it exists, + //and then creates a new object with the edited servings, ingredients, and instructions. + //It then saves the new object to storage and displays a success message using Alert.alert(). + //If there is an error, it logs the error to the console and displays an error message. const onSave = async () => { try { const existingData = await AsyncStorage.getItem(`recipe-${recipe.id}`); @@ -104,6 +140,11 @@ const RecipeScreen = ({ route }) => { const [activeTab, setActiveTab] = useState("ingredients"); + + + //functional component called RecipeTab. + //The component takes a single prop called children and returns a view that consists of a card with a scrollable tab content. + //The view is styled using CSS-in-JS syntax. const RecipeTab = ({ children }) => ( diff --git a/src/screens/RecipeGenerator/RecipeGeneratorScreen.js b/src/screens/RecipeGenerator/RecipeGeneratorScreen.js index 4febd3e..aed567b 100644 --- a/src/screens/RecipeGenerator/RecipeGeneratorScreen.js +++ b/src/screens/RecipeGenerator/RecipeGeneratorScreen.js @@ -18,6 +18,12 @@ import MenuButton from "../../components/MenuButton/MenuButton"; import { addRecipe } from "../../utils/APICalls/SimplKitchen/userRecipes"; import styles from "./styles"; + + + //defines a functional component called RecipeCard. It takes in two props: recipe and onSaveRecipe. + //The component renders a recipe card UI with an image, title, ingredient count, and a "Save Recipe" button. + //When the button is pressed, it calls the onSaveRecipe function with the recipe object as an argument. + //The component uses styles defined elsewhere in the codebase. const RecipeCard = ({ recipe, onSaveRecipe }) => { const handleSaveRecipe = () => { onSaveRecipe(recipe); @@ -48,6 +54,15 @@ const RecipeCard = ({ recipe, onSaveRecipe }) => { ); }; + + + //component called RecipeGeneratorScreen, which generates and displays a list of recipes. + //It maintains state for the list of recipes (recipes) and a loading indicator (loading). + //It defines functions handleGenerateRecipes and handleSaveRecipe for generating new recipes + //and saving a recipe to a list of saved recipes, respectively. + //The component uses the useEffect hook to call handleGenerateRecipes and set navigation options when the component is mounted. + //Finally, the component renders a ScrollView containing a button to generate new recipes and a list of recipe cards. + //If loading is true, it displays an activity indicator, otherwise it displays the list of recipe cards. const RecipeGeneratorScreen = () => { const [recipes, setRecipes] = useState([]); const [loading, setLoading] = useState(false); @@ -66,6 +81,12 @@ const RecipeGeneratorScreen = () => { } }; + + // function called handleSaveRecipe that takes in a recipe object as a parameter and saves it using the addRecipe function. + //If the save is successful, it navigates to the "SavedRecipes" page. + //If there's an error, it logs an error message to the console. + //The function is defined as asynchronous, which means that it can use the await keyword to wait for the + //addRecipe function to finish before continuing. The navigation object is likely from a React Native or React Router library. const handleSaveRecipe = async (recipe) => { try { await addRecipe(recipe); diff --git a/src/screens/ShoppingList/ShoppingListScreen.js b/src/screens/ShoppingList/ShoppingListScreen.js index 945a940..4e697b6 100644 --- a/src/screens/ShoppingList/ShoppingListScreen.js +++ b/src/screens/ShoppingList/ShoppingListScreen.js @@ -13,6 +13,13 @@ import { useNavigation } from "@react-navigation/native"; import MenuButton from "../../components/MenuButton/MenuButton"; import styles from "./styles"; + + + //It sets up state for the input text and shopping list, + //uses useEffect to load and save the shopping list to local storage + //defines functions to add, remove, and toggle items on the list. + //It also sets up the navigation options for the screen and returns a view containing an input for adding items, + //a list of items with checkboxes and remove buttons, and a button to add new items. const ShoppingListScreen = () => { const [inputText, setInputText] = useState(""); const [shoppingList, setShoppingList] = useState([]); @@ -35,6 +42,13 @@ const ShoppingListScreen = () => { saveItems(); }, [shoppingList]); + + + //function called handleAddItem. + //The function first checks if the inputText variable is not empty by removing any leading/trailing white + //spaces with the trim() function. If it's not empty, the function adds a new item to the shoppingList state + //variable using the spread operator (...) to copy the existing array and adding a new object with a unique id, + //the inputText, and a checked property set to false. Finally, it clears the inputText variable by setting it to an empty string. const handleAddItem = () => { if (inputText.trim() !== "") { setShoppingList([ @@ -45,11 +59,23 @@ const ShoppingListScreen = () => { } }; + + //a function handleRemoveItem that takes in an itemId. + //It then uses the filter method to create a new array updatedList that excludes the item with the matching itemId. + //Finally, it calls the setShoppingList function with the updated list as an argument. const handleRemoveItem = (itemId) => { const updatedList = shoppingList.filter((item) => item.id !== itemId); setShoppingList(updatedList); }; + + + //function handleToggleCheck that takes an itemId as an argument. + //It then creates a new list called updatedList by mapping over the shoppingList array. + //For each item in the shoppingList, if the item's id matches the itemId argument, + //it returns a new object that is a copy of the original item with the checked property negated. + //Otherwise, it returns the original item unchanged. Finally, the state of shoppingList is updated + //with the updatedList using the setShoppingList function. const handleToggleCheck = (itemId) => { const updatedList = shoppingList.map((item) => { if (item.id === itemId) { diff --git a/src/utils/APICalls/SimplKitchen/generateRecipes.js b/src/utils/APICalls/SimplKitchen/generateRecipes.js index 383296d..9b48bb0 100644 --- a/src/utils/APICalls/SimplKitchen/generateRecipes.js +++ b/src/utils/APICalls/SimplKitchen/generateRecipes.js @@ -2,6 +2,13 @@ import axios from "axios"; const { getToken } = require("../../AsyncStorage/userToken"); import { SIMPLKITCHEN_API_URL } from "@env"; + + + //function called generateUserRecipes. + //It first logs a message saying "Generating recipes…" to the console. + //Next, it awaits a call to the getToken() function, which presumably returns an authentication token for the user. + //It then creates an options object with the HTTP method, URL, and headers needed to make a request to a recipe API endpoint. + //Finally, it sends the request using axios and returns the response data. const generateUserRecipes = async () => { console.log("Generating recipes...") @@ -22,6 +29,11 @@ const generateUserRecipes = async () => { return response.data; } + + // function called getGeneratedRecipes that retrieves generated recipes from an API. + // It first logs a message to the console, then retrieves an authorization token using the getToken function. + //Next, it creates an options object with the HTTP method, URL, and headers required to make a GET request to the API endpoint. + //Finally, it makes the request using the axios library and returns the response data. const getGeneratedRecipes = async () => { console.log("Getting generated recipes...");