diff --git a/.gitignore b/.gitignore
index 705ca36..19d6a6b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
build/
node_modules/
-/.idea
\ No newline at end of file
+/.idea
+.env
\ No newline at end of file
diff --git a/TODO.md b/TODO.md
index 1fcf4a3..895dfe8 100644
--- a/TODO.md
+++ b/TODO.md
@@ -14,3 +14,4 @@
- [ ] Use Css-in-Js
- [ ] Redirect to 404
- [x] Typescript
+- [ ] strict typing
diff --git a/package.json b/package.json
index 2fcae70..212fa14 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,7 @@
"devDependencies": {
"@types/node": "^14.14.37",
"@types/react": "^17.0.3",
+ "@types/react-router-dom": "^5.1.7",
"typescript": "^4.2.3"
}
}
diff --git a/src/App.tsx b/src/App.tsx
index a4d84be..f364926 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -5,13 +5,16 @@ import MainLayout from "./layouts/MainLayout";
import { AppRouter } from "./router";
import { Router } from "./router/Router";
import { getData } from "./services/api";
+import { MealSummary } from "./types/meal";
import { useAuth0 } from "./utils/auth0-spa";
export const App: FC = () => {
const { loading } = useAuth0();
const [searchString, setSearchString] = useState("");
- const [searchResults, setSearchResults] = useState({ meals: [] });
- const [meal, setMeal] = useState(null);
+ const [searchResults, setSearchResults] = useState({
+ meals: [] as MealSummary[],
+ });
+ const [_, setMeal] = useState(null);
const getRandomMeal = () => {
getData("random", setMeal);
diff --git a/src/components/ContactForm.tsx b/src/components/ContactForm.tsx
deleted file mode 100644
index 0023d31..0000000
--- a/src/components/ContactForm.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-import React, { useState } from "react";
-// import { notificationMail, confirmationMail } from "../utils/mail";
-
-export const ContactForm = ({ setIsSubmitted }) => {
- const [firstName, setFirstName] = useState("");
- const [lastName, setLastName] = useState("");
- const [email, setEmail] = useState("");
- const [phone, setPhone] = useState("");
- const [message, setMessage] = useState("");
-
- const handleSubmit = (e) => {
- e.preventDefault();
- // confirmationMail(email);
- // const body = `Sender: ${firstName} ${lastName}\nPhone: ${phone}\nMessage: ${message}`;
- // notificationMail(email, `New message from ${firstName} ${lastName}`, body);
- setIsSubmitted(true);
- };
-
- return (
-
- );
-};
-
-const ContactFormInput = ({ id, type = "text", value, dispatch }) => {
- const handleChange = (e) => {
- e.preventDefault();
- dispatch(e.target.value);
- };
-
- return (
-
- {/* account_circle */}
-
-
-
- );
-};
-
-const ContactFormTextArea = ({ id, value, dispatch }) => {
- const handleChange = (e) => {
- e.preventDefault();
- dispatch(e.target.value);
- };
-
- return (
-
-
-
-
- );
-};
-
-const ContactFormSubmit = ({ text, color }) => {
- return (
-
- );
-};
diff --git a/src/components/CopyrightText.tsx b/src/components/CopyrightText.tsx
index b80463d..70d0c54 100644
--- a/src/components/CopyrightText.tsx
+++ b/src/components/CopyrightText.tsx
@@ -1,12 +1,10 @@
-import React from "react";
+import { FC } from "react";
-export const CopyrightText = () => {
- return (
-
- © 2020 - Chef's - Made with{" "}
-
- ❤️
-
+export const CopyrightText: FC = () => (
+
+ © 2020 - Chef's - Made with{" "}
+
+ ❤️
- );
-};
+
+);
diff --git a/src/components/FooterLink.tsx b/src/components/FooterLink.tsx
index 1cf344a..3fcf1ad 100644
--- a/src/components/FooterLink.tsx
+++ b/src/components/FooterLink.tsx
@@ -1,8 +1,13 @@
-import React from "react";
+import { FC } from "react";
import { Link } from "react-router-dom";
import { upFirstChar } from "../utils/methods";
-export const FooterLink = ({ link, textColor = "" }) => {
+type Props = {
+ link: string;
+ textColor?: string;
+};
+
+export const FooterLink: FC = ({ link, textColor = "" }) => {
const textColorClass = `${textColor}-text`;
return (
diff --git a/src/components/GitHubLink.tsx b/src/components/GitHubLink.tsx
index 62311dc..9310cd6 100644
--- a/src/components/GitHubLink.tsx
+++ b/src/components/GitHubLink.tsx
@@ -1,14 +1,12 @@
-import React from "react";
+import { FC } from "react";
-export const GitHubLink = () => {
- return (
-
- GitHub
-
- );
-};
+export const GitHubLink: FC = () => (
+
+ GitHub
+
+);
diff --git a/src/components/LogInButton.tsx b/src/components/LogInButton.tsx
index fe48655..501aaf6 100644
--- a/src/components/LogInButton.tsx
+++ b/src/components/LogInButton.tsx
@@ -1,7 +1,11 @@
-import React from "react";
+import { FC } from "react";
import { useAuth0 } from "../utils/auth0-spa";
-export const LogInButton = ({ color }) => {
+type Props = {
+ color: string;
+};
+
+export const LogInButton: FC = ({ color }) => {
const { loginWithRedirect } = useAuth0();
const handleClick = () => {
loginWithRedirect({});
diff --git a/src/components/LogOutButton.tsx b/src/components/LogOutButton.tsx
index 77558aa..94fa701 100644
--- a/src/components/LogOutButton.tsx
+++ b/src/components/LogOutButton.tsx
@@ -1,7 +1,7 @@
-import React from "react";
+import { FC } from "react";
import { useAuth0 } from "../utils/auth0-spa";
-export const LogOutButton = () => {
+export const LogOutButton: FC = () => {
const { logout } = useAuth0();
const handleClick = () => {
logout();
diff --git a/src/components/Logo.tsx b/src/components/Logo.tsx
index c4b629c..410cd87 100644
--- a/src/components/Logo.tsx
+++ b/src/components/Logo.tsx
@@ -1,11 +1,10 @@
-import React from "react";
+import { FC } from "react";
import { Link } from "react-router-dom";
-export const Logo = () => {
+export const Logo: FC = () => {
return (
{
+type Props = {
+ openNavClick: React.MouseEventHandler;
+ handleClick: () => void;
+};
+
+export const Navbar: FC = ({ openNavClick, handleClick }) => {
const { isAuthenticated } = useAuth0();
return (
diff --git a/src/components/PreLoader.tsx b/src/components/PreLoader.tsx
index d50f257..e644c78 100644
--- a/src/components/PreLoader.tsx
+++ b/src/components/PreLoader.tsx
@@ -1,19 +1,17 @@
-import React from "react";
+import { FC } from "react";
-export const PreLoader = () => {
- return (
-
-
-
-
-
+export const PreLoader: FC = () => (
+
+);
diff --git a/src/components/RandomButton.tsx b/src/components/RandomButton.tsx
index 68dfe64..1d10ebf 100644
--- a/src/components/RandomButton.tsx
+++ b/src/components/RandomButton.tsx
@@ -1,7 +1,19 @@
-import React from "react";
+import { FC } from "react";
import { Link } from "react-router-dom";
-export const RandomButton = ({ url, size = "large", handleClick, color }) => {
+type Props = {
+ url: string;
+ size?: string;
+ handleClick: () => void;
+ color?: string;
+};
+
+export const RandomButton: FC
= ({
+ url,
+ size = "large",
+ handleClick,
+ color,
+}) => {
const classString = `waves-effect waves-light btn-${size} ${color}`;
return (
diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx
index 2cf6de1..64598f6 100644
--- a/src/components/SearchBar.tsx
+++ b/src/components/SearchBar.tsx
@@ -1,13 +1,22 @@
-import { ChangeEvent } from "react";
+import React, { ChangeEvent, FC } from "react";
import { Link } from "react-router-dom";
import { getData } from "../services/api";
+import { MealSummary } from "../types/meal";
-export const SearchBar = ({
+type Props = {
+ searchString: string;
+ setSearchString: React.Dispatch>;
+ setSearchResults: React.Dispatch<
+ React.SetStateAction<{ meals: MealSummary[] }>
+ >;
+};
+
+export const SearchBar: FC = ({
searchString,
setSearchString,
setSearchResults,
}) => {
- const getSearchResults = (e) => {
+ const getSearchResults: React.MouseEventHandler = (e) => {
searchString === ""
? e.preventDefault()
: getData(searchString, setSearchResults, "search");
diff --git a/src/components/SideNav.tsx b/src/components/SideNav.tsx
index 30f2193..e661774 100644
--- a/src/components/SideNav.tsx
+++ b/src/components/SideNav.tsx
@@ -1,3 +1,4 @@
+import React, { FC } from "react";
import { Link } from "react-router-dom";
import { buttonURL, links } from "../constants";
import ChefImage from "../images/chef.svg";
@@ -8,7 +9,13 @@ import { LogInButton } from "./LogInButton";
import { LogOutButton } from "./LogOutButton";
import { RandomButton } from "./RandomButton";
-export const SideNav = ({ showNav, closeNavClick, handleClick }) => {
+type Props = {
+ showNav: boolean;
+ closeNavClick: React.MouseEventHandler;
+ handleClick: () => void;
+};
+
+export const SideNav: FC = ({ showNav, closeNavClick, handleClick }) => {
const { isAuthenticated, user } = useAuth0();
let transformStyle = {
transform: showNav ? "translateX(0%)" : "translateX(-105%)",
diff --git a/src/containers/Categories/index.tsx b/src/containers/Categories/index.tsx
index a176a98..5719e81 100644
--- a/src/containers/Categories/index.tsx
+++ b/src/containers/Categories/index.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from "react";
+import { useEffect, useState } from "react";
import { PreLoader } from "../../components/PreLoader";
import { getData } from "../../services/api";
import { CategoriesPage } from "./components/CategoriesPage";
diff --git a/src/containers/Category/index.tsx b/src/containers/Category/index.tsx
index 12c8728..f86e781 100644
--- a/src/containers/Category/index.tsx
+++ b/src/containers/Category/index.tsx
@@ -4,7 +4,7 @@ import { getData } from "../../services/api";
import { CategoryPage } from "./components/CategoryPage";
export const Category: FC = () => {
- const { strCategory } = useParams();
+ const { strCategory } = useParams<{ strCategory: string }>();
const [meals, setMeals] = useState({ meals: [] });
useEffect(() => {
diff --git a/src/containers/Contact/components/ContactForm.tsx b/src/containers/Contact/components/ContactForm.tsx
new file mode 100644
index 0000000..3c22181
--- /dev/null
+++ b/src/containers/Contact/components/ContactForm.tsx
@@ -0,0 +1,71 @@
+import React, { FC, useState } from "react";
+import { ContactFormInput } from "./ContactFormInput";
+import { ContactFormSubmitButton } from "./ContactFormSubmitButton";
+import { ContactFormTextArea } from "./ContactFormTextArea";
+
+type Props = {
+ setIsSubmitted: (value: boolean) => void;
+};
+
+export const ContactForm: FC = ({ setIsSubmitted }) => {
+ const [firstName, setFirstName] = useState("");
+ const [lastName, setLastName] = useState("");
+ const [email, setEmail] = useState("");
+ const [phone, setPhone] = useState("");
+ const [message, setMessage] = useState("");
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ setIsSubmitted(true);
+ };
+
+ return (
+
+ );
+};
diff --git a/src/containers/Contact/components/ContactFormInput.tsx b/src/containers/Contact/components/ContactFormInput.tsx
new file mode 100644
index 0000000..4324879
--- /dev/null
+++ b/src/containers/Contact/components/ContactFormInput.tsx
@@ -0,0 +1,35 @@
+import React, { FC } from "react";
+
+type Props = {
+ id: string;
+ type?: string;
+ value: string;
+ dispatch: React.Dispatch>;
+};
+
+export const ContactFormInput: FC = ({
+ id,
+ type = "text",
+ value,
+ dispatch,
+}) => {
+ const handleChange: React.ChangeEventHandler = (e) => {
+ e.preventDefault();
+ dispatch(e.target.value);
+ };
+
+ return (
+
+ {/* account_circle */}
+
+
+
+ );
+};
diff --git a/src/containers/Contact/components/ContactFormSubmitButton.tsx b/src/containers/Contact/components/ContactFormSubmitButton.tsx
new file mode 100644
index 0000000..f6ca505
--- /dev/null
+++ b/src/containers/Contact/components/ContactFormSubmitButton.tsx
@@ -0,0 +1,16 @@
+import { FC } from "react";
+
+type Props = {
+ text: string;
+ color: string;
+};
+
+export const ContactFormSubmitButton: FC = ({ text, color }) => (
+
+);
diff --git a/src/containers/Contact/components/ContactFormSubmitted.tsx b/src/containers/Contact/components/ContactFormSubmitted.tsx
new file mode 100644
index 0000000..717acd4
--- /dev/null
+++ b/src/containers/Contact/components/ContactFormSubmitted.tsx
@@ -0,0 +1,13 @@
+export function ContactFormSubmitted() {
+ return (
+
+
})
+
Thank you for your message
+
+ );
+}
diff --git a/src/containers/Contact/components/ContactFormTextArea.tsx b/src/containers/Contact/components/ContactFormTextArea.tsx
new file mode 100644
index 0000000..3457745
--- /dev/null
+++ b/src/containers/Contact/components/ContactFormTextArea.tsx
@@ -0,0 +1,28 @@
+import React, { FC } from "react";
+
+type Props = {
+ id: string;
+ value: string;
+ dispatch: React.Dispatch>;
+};
+
+export const ContactFormTextArea: FC = ({ id, value, dispatch }) => {
+ const handleChange: React.ChangeEventHandler = (e) => {
+ e.preventDefault();
+ dispatch(e.target.value);
+ };
+
+ return (
+
+
+
+
+ );
+};
diff --git a/src/containers/Contact/index.tsx b/src/containers/Contact/index.tsx
index beb5600..7ec30dd 100644
--- a/src/containers/Contact/index.tsx
+++ b/src/containers/Contact/index.tsx
@@ -1,20 +1,13 @@
import { FC, useState } from "react";
import PageLayout from "../../layouts/PageLayout";
-import { ContactForm } from "../../components/ContactForm";
+import { ContactForm } from "./components/ContactForm";
+import { ContactFormSubmitted } from "./components/ContactFormSubmitted";
export const Contact: FC = () => {
const [isSubmitted, setIsSubmitted] = useState(false);
return isSubmitted ? (
-
-
})
-
Thank you for your message
-
+
) : (
diff --git a/src/containers/Home/index.tsx b/src/containers/Home/index.tsx
index 1ae5678..6953139 100644
--- a/src/containers/Home/index.tsx
+++ b/src/containers/Home/index.tsx
@@ -1,9 +1,9 @@
-import React from "react";
+import { FC } from "react";
import { RandomButton } from "../../components/RandomButton";
import { buttonURL } from "../../constants";
import HeroImage from "../../images/chef.svg";
-export const Home = () => (
+export const Home: FC = () => (
diff --git a/src/containers/Meal/components/MealIngredientList.tsx b/src/containers/Meal/components/MealIngredientList.tsx
index 6f881c8..d44dd8d 100644
--- a/src/containers/Meal/components/MealIngredientList.tsx
+++ b/src/containers/Meal/components/MealIngredientList.tsx
@@ -1,7 +1,7 @@
import { FC } from "react";
type Props = {
- ingredients: string[];
+ ingredients: string[][];
};
export const MealIngredientList: FC
= ({ ingredients }) => (
diff --git a/src/containers/Meal/components/MealPage.tsx b/src/containers/Meal/components/MealPage.tsx
index df8b48d..9c7ba8b 100644
--- a/src/containers/Meal/components/MealPage.tsx
+++ b/src/containers/Meal/components/MealPage.tsx
@@ -5,7 +5,7 @@ import { MealPresentation } from "./MealPresentation";
import { MealRecipe } from "./MealRecipe";
type Props = {
- ingredients: string[];
+ ingredients: string[][];
recipe: string;
meal: Meal;
handleFavChange: () => void;
diff --git a/src/containers/Meal/index.tsx b/src/containers/Meal/index.tsx
index 658e9dd..853bc12 100644
--- a/src/containers/Meal/index.tsx
+++ b/src/containers/Meal/index.tsx
@@ -1,21 +1,37 @@
import React, { FC, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
-import { NotFound } from "../NotFound";
+import { getData } from "../../services/api";
import { useFirebase } from "../../services/Firebase";
+import { MealApi as MealType } from "../../types/meal";
import { useAuth0 } from "../../utils/auth0-spa";
+import { NotFound } from "../NotFound";
import { MealPage } from "./components/MealPage";
-import { getMeal, getRandomMeal } from "./service";
+import { buildIngredientList, buildMealProps } from "./service";
export const Meal: FC = () => {
// hooks
const { user, isAuthenticated } = useAuth0();
- const { id } = useParams();
+ const { id } = useParams<{ id: string }>();
const fb = useFirebase();
// local state
- const [meal, setMeal] = useState(null);
+ const [meal, setMeal] = useState({ meals: [] as MealType[] });
const [isFav, setIsFav] = useState();
// variables
const mealItem = meal?.meals?.[0];
+
+ const getMeal = (
+ id: string,
+ setMeal: React.Dispatch>
+ ) => {
+ getData(id, setMeal, "lookup");
+ };
+
+ const getRandomMeal = (
+ setMeal: React.Dispatch>
+ ) => {
+ getData("random", setMeal);
+ };
+
// effects
/** Fetch meal from db */
useEffect(() => {
@@ -24,7 +40,7 @@ export const Meal: FC = () => {
/** Updates fav status in db */
useEffect(() => {
if (isAuthenticated) {
- fb.isFav(user.email, mealItem?.idMeal).then((res) => setIsFav(res));
+ fb?.isFav(user.email, mealItem?.idMeal).then((res) => setIsFav(res));
}
}, [user, fb, mealItem?.idMeal, isAuthenticated]);
// other logic
@@ -44,23 +60,8 @@ export const Meal: FC = () => {
}
};
- 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]]);
- }
- }
+ const item = buildMealProps(mealItem, isFav!);
+ const ingredients = buildIngredientList(mealItem);
return !!meal?.meals ? (
{
- getData(id, setMeal, "lookup");
+export const buildIngredientList = (mealItem: MealApi): string[][] => {
+ let ingredients = [];
+ for (let i = 1; i <= 20; i++) {
+ let strIng = `strIngredient${i}`;
+ let strMes = `strMeasure${i}`;
+ // @ts-ignore
+ if (!!mealItem?.[strIng] && !!mealItem?.[strIng]) {
+ // @ts-ignore
+ ingredients.push([mealItem?.[strIng], mealItem?.[strMes]]);
+ }
+ }
+ return ingredients;
};
-export const getRandomMeal = (setMeal) => {
- getData("random", setMeal);
-};
+export const buildMealProps = (mealItem: MealApi, isFav: boolean) => ({
+ mealName: mealItem?.strMeal,
+ imgAddress: mealItem?.strMealThumb,
+ videoAddress: mealItem?.strYoutube,
+ mealCategory: mealItem?.strCategory,
+ mealArea: mealItem?.strArea,
+ isFav,
+});
diff --git a/src/containers/NotFound/index.tsx b/src/containers/NotFound/index.tsx
index 46e8a37..1e95be2 100644
--- a/src/containers/NotFound/index.tsx
+++ b/src/containers/NotFound/index.tsx
@@ -1,6 +1,5 @@
import { FC } from "react";
import { RandomButton } from "../../components/RandomButton";
-import { getRandomMeal } from "../Meal/service";
export const NotFound: FC = () => (
@@ -16,11 +15,7 @@ export const NotFound: FC = () => (
/>
-
+ {}} />
diff --git a/src/containers/Profile/index.tsx b/src/containers/Profile/index.tsx
index cea277b..d95e79d 100644
--- a/src/containers/Profile/index.tsx
+++ b/src/containers/Profile/index.tsx
@@ -1,12 +1,13 @@
import { FC, useEffect, useState } from "react";
import { PreLoader } from "../../components/PreLoader";
import { useFirebase } from "../../services/Firebase";
+import { MealSummary } from "../../types/meal";
import { useAuth0 } from "../../utils/auth0-spa";
import { ProfilePage } from "./components/ProfilePage";
export const Profile: FC = () => {
const { loading, user } = useAuth0();
- const [favs, setFavs] = useState([]);
+ const [favs, setFavs] = useState([] as MealSummary[]);
const db = useFirebase();
useEffect(() => {
diff --git a/src/index.css b/src/index.css
index 3078fdd..077c41f 100644
--- a/src/index.css
+++ b/src/index.css
@@ -29,6 +29,6 @@ nav {
font-family: "Marck Script", cursive;
}
-i.material-icons {
+a>i.material-icons {
color: #ff9800;
}
\ No newline at end of file
diff --git a/src/index.jsx b/src/index.jsx
index b1fcddf..1de121c 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -1,12 +1,11 @@
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
-import {App} from "./App";
+import { App } from "./App";
import * as serviceWorker from "./serviceWorker";
import { Auth0Provider } from "./utils/auth0-spa";
import history from "./utils/history";
import Firebase, { FirebaseContext } from "./services/Firebase";
-import config from "./utils/auth_config.json";
const onRedirectCallBack = (appState) => {
history.push(
@@ -18,14 +17,13 @@ const onRedirectCallBack = (appState) => {
ReactDOM.render(
-
+ {/* todo fix Firebase app*/}
+
,
diff --git a/src/layouts/MainLayout.tsx b/src/layouts/MainLayout.tsx
index d1f30e6..bb28fdf 100644
--- a/src/layouts/MainLayout.tsx
+++ b/src/layouts/MainLayout.tsx
@@ -1,11 +1,19 @@
-import React, { useState } from "react";
+import React, { FC, useState } from "react";
import { Footer } from "../components/Footer";
import { Navbar } from "../components/Navbar";
import { SearchBar } from "../components/SearchBar";
import { SideNav } from "../components/SideNav";
+import { MealSummary } from "../types/meal";
-// TODO FC...
-const MainLayout = ({
+type Props = {
+ getRandomMeal: () => void;
+ searchString: string;
+ setSearchString: React.Dispatch>;
+ setSearchResults: React.Dispatch<
+ React.SetStateAction<{ meals: MealSummary[] }>
+ >;
+};
+const MainLayout: FC = ({
getRandomMeal,
searchString,
setSearchString,
@@ -14,19 +22,19 @@ const MainLayout = ({
}) => {
const [showNav, setShowNav] = useState(false);
- const openNavClick = (e) => {
+ const openNavClick: React.MouseEventHandler = (e) => {
e.preventDefault();
setShowNav(true);
document.addEventListener("keydown", handleEscKey);
};
- const closeNavClick = (e) => {
+ const closeNavClick = (e: React.MouseEvent) => {
e.preventDefault();
setShowNav(false);
document.removeEventListener("keydown", handleEscKey);
};
- const handleEscKey = (e) => {
+ const handleEscKey = (e: KeyboardEvent) => {
if (e.key === "Escape") {
setShowNav(false);
}
diff --git a/src/router/AppRouter.tsx b/src/router/AppRouter.tsx
index a2c5bb2..9061ea5 100644
--- a/src/router/AppRouter.tsx
+++ b/src/router/AppRouter.tsx
@@ -1,3 +1,4 @@
+import { FC } from "react";
import { Redirect, Route, Switch } from "react-router-dom";
import { buttonURL } from "../constants";
import { Categories } from "../containers/Categories";
@@ -8,11 +9,14 @@ import { Profile } from "../containers/Profile";
import { Search } from "../containers/Search";
import { Contact } from "../containers/Contact";
import { NotFound } from "../containers/NotFound";
+import { MealSummary } from "../types/meal";
import { PrivateRoute } from "./PrivateRoute";
-//TODO: remove state from router move to containers
-
-const AppRouter = ({ searchString, searchResults }) => (
+type Props = {
+ searchString: string;
+ searchResults: { meals: MealSummary[] };
+};
+const AppRouter: FC = ({ searchString, searchResults }) => (
diff --git a/src/router/PrivateRoute.tsx b/src/router/PrivateRoute.tsx
index b608d04..443ca68 100644
--- a/src/router/PrivateRoute.tsx
+++ b/src/router/PrivateRoute.tsx
@@ -1,9 +1,16 @@
-import { useEffect } from "react";
-import { Route } from "react-router-dom";
+import { FC, useEffect } from "react";
+import { Route, RouteProps } from "react-router-dom";
import { useAuth0 } from "../utils/auth0-spa";
-// TODO use FC and props
-export const PrivateRoute = ({ component: Component, path, ...rest }) => {
+type Props = {
+ component: FC;
+} & RouteProps;
+
+export const PrivateRoute: FC = ({
+ component: Component,
+ path,
+ ...rest
+}) => {
const { loading, isAuthenticated, loginWithRedirect } = useAuth0();
useEffect(() => {
@@ -18,7 +25,8 @@ export const PrivateRoute = ({ component: Component, path, ...rest }) => {
fn();
}, [loading, isAuthenticated, loginWithRedirect, path]);
- const render = (props) => (isAuthenticated ? : null);
+ const render = (props: any) =>
+ isAuthenticated ? : null;
return ;
};
diff --git a/src/serviceWorker.ts b/src/serviceWorker.ts
index 8703ddb..d94c00e 100644
--- a/src/serviceWorker.ts
+++ b/src/serviceWorker.ts
@@ -11,17 +11,17 @@
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean(
- window.location.hostname === 'localhost' ||
+ window.location.hostname === "localhost" ||
// [::1] is the IPv6 localhost address.
- window.location.hostname === '[::1]' ||
+ window.location.hostname === "[::1]" ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
-export function register(config) {
- if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
+export function register(config: any) {
+ if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
@@ -31,7 +31,7 @@ export function register(config) {
return;
}
- window.addEventListener('load', () => {
+ window.addEventListener("load", () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
@@ -42,8 +42,8 @@ export function register(config) {
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
- 'This web app is being served cache-first by a service ' +
- 'worker. To learn more, visit https://bit.ly/CRA-PWA'
+ "This web app is being served cache-first by a service " +
+ "worker. To learn more, visit https://bit.ly/CRA-PWA"
);
});
} else {
@@ -54,24 +54,24 @@ export function register(config) {
}
}
-function registerValidSW(swUrl, config) {
+function registerValidSW(swUrl: any, config: any) {
navigator.serviceWorker
.register(swUrl)
- .then(registration => {
+ .then((registration) => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
- if (installingWorker.state === 'installed') {
+ if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
- 'New content is available and will be used when all ' +
- 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
+ "New content is available and will be used when all " +
+ "tabs for this page are closed. See https://bit.ly/CRA-PWA."
);
// Execute callback
@@ -82,7 +82,7 @@ function registerValidSW(swUrl, config) {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
- console.log('Content is cached for offline use.');
+ console.log("Content is cached for offline use.");
// Execute callback
if (config && config.onSuccess) {
@@ -93,25 +93,25 @@ function registerValidSW(swUrl, config) {
};
};
})
- .catch(error => {
- console.error('Error during service worker registration:', error);
+ .catch((error) => {
+ console.error("Error during service worker registration:", error);
});
}
-function checkValidServiceWorker(swUrl, config) {
+function checkValidServiceWorker(swUrl: any, config: any) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
- headers: { 'Service-Worker': 'script' }
+ headers: { "Service-Worker": "script" },
})
- .then(response => {
+ .then((response) => {
// Ensure service worker exists, and that we really are getting a JS file.
- const contentType = response.headers.get('content-type');
+ const contentType = response.headers.get("content-type");
if (
response.status === 404 ||
- (contentType != null && contentType.indexOf('javascript') === -1)
+ (contentType != null && contentType.indexOf("javascript") === -1)
) {
// No service worker found. Probably a different app. Reload the page.
- navigator.serviceWorker.ready.then(registration => {
+ navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => {
window.location.reload();
});
@@ -123,14 +123,14 @@ function checkValidServiceWorker(swUrl, config) {
})
.catch(() => {
console.log(
- 'No internet connection found. App is running in offline mode.'
+ "No internet connection found. App is running in offline mode."
);
});
}
export function unregister() {
- if ('serviceWorker' in navigator) {
- navigator.serviceWorker.ready.then(registration => {
+ if ("serviceWorker" in navigator) {
+ navigator.serviceWorker.ready.then((registration) => {
registration.unregister();
});
}
diff --git a/src/services/Firebase/context.js b/src/services/Firebase/context.js
index d0d3bed..f7cdbeb 100644
--- a/src/services/Firebase/context.js
+++ b/src/services/Firebase/context.js
@@ -1,7 +1,8 @@
import { createContext, useContext } from "react";
+import Firebase from "./firebase";
// create a Firebase context to make state available anywhere in the App.
-const FirebaseContext = createContext(null);
+const FirebaseContext = createContext(new Firebase());
export const useFirebase = () => useContext(FirebaseContext);
export default FirebaseContext;
diff --git a/src/services/api.ts b/src/services/api.ts
index f446e2a..8bf47ea 100644
--- a/src/services/api.ts
+++ b/src/services/api.ts
@@ -1,18 +1,34 @@
-export const createURI = (keyword: string, option: string) => {
+import React from "react";
+
+export const createURI = (keyword: string, option?: string) => {
const ROOT = "https://www.themealdb.com/api/json/v1/1/";
if (!option) {
return `${ROOT}${keyword}.php`;
- } else if (option === "filter") {
- return `${ROOT}${option}.php?c=${keyword}`;
- } else if (option === "lookup") {
- return `${ROOT}${option}.php?i=${keyword}`;
- } else if (option === "search") {
- return `${ROOT}${option}.php?s=${keyword}`;
+ }
+
+ switch (option) {
+ case "filter": {
+ return `${ROOT}${option}.php?c=${keyword}`;
+ }
+ case "lookup": {
+ return `${ROOT}${option}.php?i=${keyword}`;
+ }
+ case "search": {
+ return `${ROOT}${option}.php?s=${keyword}`;
+ }
+ default: {
+ throw Error("Unexpected URI");
+ }
}
};
-export const getData = (keyword: string, set, option: string = null) => {
+export const getData = (
+ keyword: string,
+ set: React.Dispatch>,
+ option?: string
+) => {
const URI = createURI(keyword, option);
+
fetch(URI)
.then((response) => response.json())
.catch((error) => console.warn(error + "url:" + URI))
diff --git a/src/types/meal.ts b/src/types/meal.ts
index d0688d9..fe74103 100644
--- a/src/types/meal.ts
+++ b/src/types/meal.ts
@@ -4,7 +4,7 @@ export default interface Meal {
videoAddress: string;
mealCategory: string;
mealArea: string;
- isFav: boolean;
+ isFav?: boolean;
}
export interface MealSummary {
@@ -12,3 +12,13 @@ export interface MealSummary {
strMeal: string;
strMealThumb: string;
}
+
+export interface MealApi {
+ idMeal: string;
+ strMeal: string;
+ strMealThumb: string;
+ strYoutube: string;
+ strCategory: string;
+ strArea: string;
+ strInstructions: string;
+}
diff --git a/src/utils/auth_config.json b/src/utils/auth_config.json
deleted file mode 100644
index 6cce307..0000000
--- a/src/utils/auth_config.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "DOMAIN": "chefs-meal-planner.eu.auth0.com",
- "CLIENT_ID": "EXe8HCfFd0jSSfqzjAvpdk72ce0y2Hh9"
-}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 4a501cc..a583456 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -10,7 +10,10 @@
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
- "strict": false,
+ "strict": true,
+ "noUnusedParameters": true,
+ "noUnusedLocals": true,
+ "noImplicitReturns": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
diff --git a/yarn.lock b/yarn.lock
index 537efc8..b350fa2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2037,6 +2037,11 @@
dependencies:
"@types/node" "*"
+"@types/history@*":
+ version "4.7.8"
+ resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934"
+ integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==
+
"@types/html-minifier-terser@^5.0.0":
version "5.1.1"
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50"
@@ -2129,7 +2134,24 @@
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
-"@types/react@^17.0.3":
+"@types/react-router-dom@^5.1.7":
+ version "5.1.7"
+ resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.7.tgz#a126d9ea76079ffbbdb0d9225073eb5797ab7271"
+ integrity sha512-D5mHD6TbdV/DNHYsnwBTv+y73ei+mMjrkGrla86HthE4/PVvL1J94Bu3qABU+COXzpL23T1EZapVVpwHuBXiUg==
+ dependencies:
+ "@types/history" "*"
+ "@types/react" "*"
+ "@types/react-router" "*"
+
+"@types/react-router@*":
+ version "5.1.13"
+ resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.13.tgz#051c0d229bd48ad90558a1db500708127cc512f7"
+ integrity sha512-ZIuaO9Yrln54X6elg8q2Ivp6iK6p4syPsefEYAhRDAoqNh48C8VYUmB9RkXjKSQAJSJV0mbIFCX7I4vZDcHrjg==
+ dependencies:
+ "@types/history" "*"
+ "@types/react" "*"
+
+"@types/react@*", "@types/react@^17.0.3":
version "17.0.3"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79"
integrity sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==