From 681c281d51bcd09d35e9289da0856ffd79631d83 Mon Sep 17 00:00:00 2001 From: Ruidy Date: Sun, 28 Mar 2021 15:30:59 +0200 Subject: [PATCH] refactor meal container --- src/App.tsx | 83 ++----------------- src/components/IngredientList.tsx | 24 ------ src/components/Recipe.tsx | 11 --- src/containers/CategoryController.tsx | 6 +- src/containers/CategoryListController.tsx | 4 +- src/containers/Meal/MealPage.tsx | 31 +++++++ .../Meal/components/MealIngredientList.tsx | 26 ++++++ .../Meal}/components/MealPresentation.tsx | 11 ++- src/containers/Meal/components/MealRecipe.tsx | 13 +++ src/containers/Meal/index.tsx | 75 +++++++++++++++++ src/containers/Meal/service.ts | 9 ++ src/containers/MealController.tsx | 78 ----------------- src/pages/MealPage.tsx | 20 ----- src/router/AppRouter.tsx | 22 ++--- src/{utils/methods.js => services/api.ts} | 18 ++-- src/types/Meal.ts | 8 ++ src/utils/methods.ts | 2 + 17 files changed, 194 insertions(+), 247 deletions(-) delete mode 100644 src/components/IngredientList.tsx delete mode 100644 src/components/Recipe.tsx create mode 100644 src/containers/Meal/MealPage.tsx create mode 100644 src/containers/Meal/components/MealIngredientList.tsx rename src/{ => containers/Meal}/components/MealPresentation.tsx (89%) create mode 100644 src/containers/Meal/components/MealRecipe.tsx create mode 100644 src/containers/Meal/index.tsx create mode 100644 src/containers/Meal/service.ts delete mode 100644 src/containers/MealController.tsx delete mode 100644 src/pages/MealPage.tsx rename src/{utils/methods.js => services/api.ts} (51%) create mode 100644 src/types/Meal.ts create mode 100644 src/utils/methods.ts diff --git a/src/App.tsx b/src/App.tsx index 4448c5e..bfc36d8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,87 +1,19 @@ -import React, { FC, useState } from "react"; -import { Router } from "./router/Router"; - +import { FC, useState } from "react"; import { PreLoader } from "./components/PreLoader"; - -import { useAuth0 } from "./utils/auth0-spa"; -import { getData } from "./utils/methods"; - import "./index.css"; import MainLayout from "./layouts/MainLayout"; import { AppRouter } from "./router"; +import { Router } from "./router/Router"; +import { getData } from "./services/api"; +import { useAuth0 } from "./utils/auth0-spa"; export const App: FC = () => { const { loading } = useAuth0(); const [searchString, setSearchString] = useState(""); const [searchResults, setSearchResults] = useState({ meals: [] }); - // Default meal object. TODO: Find a better alternative … - const mealDef = { - meals: [ - { - idMeal: "52837", - strMeal: "Chef's meal", - strDrinkAlternate: null, - strCategory: "yummy", - strArea: "Mine", - strInstructions: - "Cook the pasta following pack instructions.\r\n\r\nHeat the oil in a non-stick frying pan and cook the onion, garlic and chilli for 3-4 mins to soften. Stir in the tomato pur\u00e9e and cook for 1 min, then add the pilchards with their sauce. Cook, breaking up the fish with a wooden spoon, then add the olives and continue to cook for a few more mins.\r\n\r\nDrain the pasta and add to the pan with 2-3 tbsp of the cooking water. Toss everything together well, then divide between plates and serve, scattered with Parmesan.", - strMealThumb: require("./images/breakfast.svg"), - // "https://www.themealdb.com/images/media/meals/vvtvtr1511180578.jpg", - strTags: null, - strYoutube: "#", - strIngredient1: "Spaghetti", - strIngredient2: "Olive Oil", - strIngredient3: "Onion", - strIngredient4: "Garlic", - strIngredient5: "Red Chilli", - strIngredient6: "Tomato Puree", - strIngredient7: "Pilchards", - strIngredient8: "Black Olives", - strIngredient9: "Parmesan", - strIngredient10: "", - strIngredient11: "", - strIngredient12: "", - strIngredient13: "", - strIngredient14: "", - strIngredient15: "", - strIngredient16: "", - strIngredient17: "", - strIngredient18: "", - strIngredient19: "", - strIngredient20: "", - strMeasure1: "300g", - strMeasure2: "1 tbls", - strMeasure3: "1 finely chopped ", - strMeasure4: "2 cloves minced", - strMeasure5: "1", - strMeasure6: "1 tbls", - strMeasure7: "425g", - strMeasure8: "70g", - strMeasure9: "Shaved", - strMeasure10: "", - strMeasure11: "", - strMeasure12: "", - strMeasure13: "", - strMeasure14: "", - strMeasure15: "", - strMeasure16: "", - strMeasure17: "", - strMeasure18: "", - strMeasure19: "", - strMeasure20: "", - strSource: "https://www.bbcgoodfood.com/recipes/pilchard-puttanesca", - dateModified: null, - }, - ], - }; - - const [meal, setMeal] = useState(mealDef); + const [meal, setMeal] = useState(null); const buttonUrl = "/random"; - const getMeal = (id) => { - getData(id, setMeal, "lookup"); - }; - const getRandomMeal = () => { getData("random", setMeal); }; @@ -105,11 +37,8 @@ export const App: FC = () => { { > { - return ( -
- - - - - - - - - {ingredients.map((ing, i) => ( - - - - - ))} - -
IngredientQuantity
{ing[0]}{ing[1]}
-
- ); -}; diff --git a/src/components/Recipe.tsx b/src/components/Recipe.tsx deleted file mode 100644 index d4a0e04..0000000 --- a/src/components/Recipe.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; - -export const Recipe = ({ recipe }) => { - return ( -
-
-

Instructions

-

{recipe}

-
- ); -}; diff --git a/src/containers/CategoryController.tsx b/src/containers/CategoryController.tsx index c9e3db2..06a9c8c 100644 --- a/src/containers/CategoryController.tsx +++ b/src/containers/CategoryController.tsx @@ -1,7 +1,7 @@ -import React, { useEffect, useState } from "react"; -import { useParams, Redirect } from "react-router-dom"; +import { useEffect, useState } from "react"; +import { Redirect, useParams } from "react-router-dom"; import { CategoryPage } from "../pages/CategoryPage"; -import { getData } from "../utils/methods"; +import { getData } from "../services/api"; export const CategoryController = () => { const [meals, setMeals] = useState({ meals: [] }); diff --git a/src/containers/CategoryListController.tsx b/src/containers/CategoryListController.tsx index 5575b94..92d6a39 100644 --- a/src/containers/CategoryListController.tsx +++ b/src/containers/CategoryListController.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; -import { getData } from "../utils/methods"; -import { CategoryListPage } from "../pages/CategoryListPage"; import { PreLoader } from "../components/PreLoader"; +import { CategoryListPage } from "../pages/CategoryListPage"; +import { getData } from "../services/api"; export const CategoryListController = () => { const [categories, setCategories] = useState({ categories: [] }); diff --git a/src/containers/Meal/MealPage.tsx b/src/containers/Meal/MealPage.tsx new file mode 100644 index 0000000..e1ce63d --- /dev/null +++ b/src/containers/Meal/MealPage.tsx @@ -0,0 +1,31 @@ +import { FC } from "react"; +import Meal from "../../types/Meal"; +import { MealIngredientList } from "./components/MealIngredientList"; +import { MealPresentation } from "./components/MealPresentation"; +import { MealRecipe } from "./components/MealRecipe"; + +type Props = { + ingredients: string[]; + recipe: string; + meal: Meal; + handleFavChange: () => void; +}; + +export const MealPage: FC = ({ + meal, + ingredients, + recipe, + handleFavChange, +}) => ( +
+
+
+ +
+
+ +
+
+ +
+); diff --git a/src/containers/Meal/components/MealIngredientList.tsx b/src/containers/Meal/components/MealIngredientList.tsx new file mode 100644 index 0000000..6f881c8 --- /dev/null +++ b/src/containers/Meal/components/MealIngredientList.tsx @@ -0,0 +1,26 @@ +import { FC } from "react"; + +type Props = { + ingredients: string[]; +}; + +export const MealIngredientList: FC = ({ ingredients }) => ( +
+ + + + + + + + + {ingredients.map((ing, i) => ( + + + + + ))} + +
IngredientQuantity
{ing[0]}{ing[1]}
+
+); diff --git a/src/components/MealPresentation.tsx b/src/containers/Meal/components/MealPresentation.tsx similarity index 89% rename from src/components/MealPresentation.tsx rename to src/containers/Meal/components/MealPresentation.tsx index cb09b3d..01fe712 100644 --- a/src/components/MealPresentation.tsx +++ b/src/containers/Meal/components/MealPresentation.tsx @@ -1,7 +1,13 @@ -import React from "react"; +import { FC } from "react"; import { Link } from "react-router-dom"; +import Meal from "../../../types/Meal"; -export const MealPresentation = ({ meal }) => { +type Props = { + meal: Meal; + handleFavChange: () => void; +}; + +export const MealPresentation: FC = ({ meal, handleFavChange }) => { const { mealName, imgAddress, @@ -9,7 +15,6 @@ export const MealPresentation = ({ meal }) => { mealCategory, mealArea, isFav, - handleFavChange, } = meal; return ( diff --git a/src/containers/Meal/components/MealRecipe.tsx b/src/containers/Meal/components/MealRecipe.tsx new file mode 100644 index 0000000..4e01fa6 --- /dev/null +++ b/src/containers/Meal/components/MealRecipe.tsx @@ -0,0 +1,13 @@ +import { FC } from "react"; + +type Props = { + recipe: string; +}; + +export const MealRecipe: FC = ({ recipe }) => ( +
+
+

Instructions

+

{recipe}

+
+); diff --git a/src/containers/Meal/index.tsx b/src/containers/Meal/index.tsx new file mode 100644 index 0000000..77c7db6 --- /dev/null +++ b/src/containers/Meal/index.tsx @@ -0,0 +1,75 @@ +import React, { FC, useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import { NotFoundPage } from "../../pages/NotFoundPage"; +import { useFirebase } from "../../services/Firebase"; +import { useAuth0 } from "../../utils/auth0-spa"; +import { MealPage } from "./MealPage"; +import { getMeal, getRandomMeal } from "./service"; + +export const Meal: FC = () => { + // hooks + const { user, isAuthenticated } = useAuth0(); + const { id } = useParams(); + const fb = useFirebase(); + // local state + const [meal, setMeal] = useState(null); + const [isFav, setIsFav] = useState(); + // variables + const mealItem = meal?.meals?.[0]; + // effects + /** Fetch meal from db */ + useEffect(() => { + !id ? getRandomMeal(setMeal) : getMeal(id, setMeal); + }, [id]); + /** Updates fav status in db */ + useEffect(() => { + if (isAuthenticated) { + fb.isFav(user.email, mealItem?.idMeal).then((res) => setIsFav(res)); + } + }, [user, fb, mealItem?.idMeal, isAuthenticated]); + // other logic + const handleFavChange = () => { + if (isAuthenticated) { + setIsFav(!isFav); + // Send !isFav because state is not yet updated + fb.addToFavs( + user?.email, + mealItem?.idMeal, + mealItem?.strMeal, + mealItem?.strMealThumb, + !isFav + ); + } else { + window.alert("You must be authenticated to add to favourites."); + } + }; + + const item = { + mealName: mealItem?.strMeal, + imgAddress: mealItem?.strMealThumb, + videoAddress: mealItem?.strYoutube, + mealCategory: mealItem?.strCategory, + mealArea: mealItem?.strArea, + isFav, + }; + + let ingredients = []; + for (let i = 1; i <= 20; i++) { + let strIng = `strIngredient${i}`; + let strMes = `strMeasure${i}`; + if (!!mealItem?.[strIng] && !!mealItem?.[strIng]) { + ingredients.push([mealItem?.[strIng], mealItem?.[strMes]]); + } + } + + return !!meal?.meals ? ( + + ) : ( + + ); +}; diff --git a/src/containers/Meal/service.ts b/src/containers/Meal/service.ts new file mode 100644 index 0000000..e3ea11e --- /dev/null +++ b/src/containers/Meal/service.ts @@ -0,0 +1,9 @@ +import { getData } from "../../services/api"; + +export const getMeal = (id, setMeal) => { + getData(id, setMeal, "lookup"); +}; + +export const getRandomMeal = (setMeal) => { + getData("random", setMeal); +}; diff --git a/src/containers/MealController.tsx b/src/containers/MealController.tsx deleted file mode 100644 index b8823d8..0000000 --- a/src/containers/MealController.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { useParams } from "react-router-dom"; -import { useAuth0 } from "../utils/auth0-spa"; -import { MealPage } from "../pages/MealPage"; -import { NotFoundPage } from "../pages/NotFoundPage"; -import { useFirebase } from "../services/Firebase"; - -export const MealController = ({ meal, getMeal, getRandomMeal }) => { - const { user, isAuthenticated } = useAuth0(); - const { id } = useParams(); - const fb = useFirebase(); - - useEffect(() => { - id === undefined ? getRandomMeal() : getMeal(id); - // eslint-disable-next-line - }, []); - - const mealItem = meal.meals[0]; - - const { - idMeal, - strMeal, - strMealThumb, - strYoutube, - strCategory, - strArea, - strInstructions, - } = mealItem; - - const [isFav, setIsFav] = useState(); - - /** - * Updates fav status in db - */ - const handleFavChange = (e) => { - e.preventDefault(); - setIsFav(!isFav); - // Send !isFav because state is not yet updated - fb.addToFavs(user.email, idMeal, strMeal, strMealThumb, !isFav); - }; - - useEffect(() => { - // Not update fav status of the placeholder recipe. TODO: it's ugly... - if (idMeal !== "52837" && isAuthenticated) { - fb.isFav(user.email, idMeal).then((res) => setIsFav(res)); - } - }, [user, fb, idMeal, isAuthenticated]); - - const item = { - mealName: strMeal, - imgAddress: strMealThumb, - videoAddress: strYoutube, - mealCategory: strCategory, - mealArea: strArea, - isFav, - handleFavChange, - }; - - let ingredientList = []; - var i; - for (i = 1; i <= 20; i++) { - let strIng = `strIngredient${i}`; - let strMes = `strMeasure${i}`; - if (mealItem[strIng] !== "" && mealItem[strIng] !== null) { - ingredientList.push([mealItem[strIng], mealItem[strMes]]); - } - } - - return meal !== undefined && meal.meals !== null ? ( - - ) : ( - - ); -}; diff --git a/src/pages/MealPage.tsx b/src/pages/MealPage.tsx deleted file mode 100644 index 7de0d9a..0000000 --- a/src/pages/MealPage.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react"; -import { MealPresentation } from "../components/MealPresentation"; -import { IngredientList } from "../components/IngredientList"; -import { Recipe } from "../components/Recipe"; - -export const MealPage = ({ item, ingredientList, strInstructions }) => { - return ( -
-
-
- -
-
- -
-
- -
- ); -}; diff --git a/src/router/AppRouter.tsx b/src/router/AppRouter.tsx index 282600d..4d7344b 100644 --- a/src/router/AppRouter.tsx +++ b/src/router/AppRouter.tsx @@ -1,10 +1,10 @@ -import { Switch, Route, Redirect } from "react-router-dom"; -import { SearchController } from "../containers/SearchController"; -import { Home } from "../containers/Home"; -import { MealController } from "../containers/MealController"; +import { Redirect, Route, Switch } from "react-router-dom"; import { CategoryController } from "../containers/CategoryController"; import { CategoryListController } from "../containers/CategoryListController"; +import { Home } from "../containers/Home"; +import { Meal } from "../containers/Meal"; import { ProfileController } from "../containers/ProfileController"; +import { SearchController } from "../containers/SearchController"; import { ContactPage } from "../pages/Contact"; import { NotFoundPage } from "../pages/NotFoundPage"; import { PrivateRoute } from "./PrivateRoute"; @@ -13,8 +13,6 @@ import { PrivateRoute } from "./PrivateRoute"; const AppRouter = ({ buttonUrl, - meal, - getMeal, getRandomMeal, searchString, searchResults, @@ -27,11 +25,7 @@ const AppRouter = ({ - + @@ -58,11 +52,7 @@ const AppRouter = ({ - + diff --git a/src/utils/methods.js b/src/services/api.ts similarity index 51% rename from src/utils/methods.js rename to src/services/api.ts index 3d3bb5c..f446e2a 100644 --- a/src/utils/methods.js +++ b/src/services/api.ts @@ -1,6 +1,6 @@ -export const createURI = (keyword, option) => { +export const createURI = (keyword: string, option: string) => { const ROOT = "https://www.themealdb.com/api/json/v1/1/"; - if (option === null) { + if (!option) { return `${ROOT}${keyword}.php`; } else if (option === "filter") { return `${ROOT}${option}.php?c=${keyword}`; @@ -11,16 +11,10 @@ export const createURI = (keyword, option) => { } }; -export const getData = (keyword, set, option = null) => { +export const getData = (keyword: string, set, option: string = null) => { const URI = createURI(keyword, option); fetch(URI) - .then(response => response.json()) - .catch(error => console.info(error + "url:" + URI)) - .then(data => set(data)); + .then((response) => response.json()) + .catch((error) => console.warn(error + "url:" + URI)) + .then((data) => set(data)); }; - -export const upFirstChar = lower => { - return lower.replace(/^\w/, c => c.toUpperCase()); -}; - -export const addToFavourites = () => {}; diff --git a/src/types/Meal.ts b/src/types/Meal.ts new file mode 100644 index 0000000..c21089e --- /dev/null +++ b/src/types/Meal.ts @@ -0,0 +1,8 @@ +export default interface Meal { + mealName: string; + imgAddress: string; + videoAddress: string; + mealCategory: string; + mealArea: string; + isFav: boolean; +} diff --git a/src/utils/methods.ts b/src/utils/methods.ts new file mode 100644 index 0000000..271e0a0 --- /dev/null +++ b/src/utils/methods.ts @@ -0,0 +1,2 @@ +export const upFirstChar = (lower: string): string => + lower.replace(/^\w/, (c) => c.toUpperCase());