This commit is contained in:
Ruidy Nemausat 2020-01-30 22:18:39 +01:00
parent df80472fd7
commit fd3362a169
28 changed files with 136 additions and 23 deletions

View file

@ -71,6 +71,8 @@ Free meal planner for cooks short on ideas! (like me …)
- [Materialize](https://materializecss.com) CSS librairy for styling - [Materialize](https://materializecss.com) CSS librairy for styling
- Public API: [TheMealDb](https://www.themealdb.com/api.php) and [TheCocktailDb](https://www.thecocktaildb.com/api.php) - Public API: [TheMealDb](https://www.themealdb.com/api.php) and [TheCocktailDb](https://www.thecocktaildb.com/api.php)
- Hosting: [Render](https://render.com/) - Hosting: [Render](https://render.com/)
- Analytics : Google Analytics & Mixpanel
- Authentication : Firebase or Auth0
## Versions ## Versions
@ -90,7 +92,7 @@ Free meal planner for cooks short on ideas! (like me …)
## TO DO ## TO DO
- add sidenav on mobile - add sidenav on mobile
- accounts v2 - accounts v2 (profile page)
- send message after contact form validation (confirm to sender and msg+info to admin) - send message after contact form validation (confirm to sender and msg+info to admin)
- code cleanup (props and refactoring) - code cleanup (props and refactoring)
- put a preloader - put a preloader
@ -99,3 +101,4 @@ Free meal planner for cooks short on ideas! (like me …)
- https://www.henriksommerfeld.se/error-handling-with-fetch/ - https://www.henriksommerfeld.se/error-handling-with-fetch/
- Use ErrorBoundaries component ? - Use ErrorBoundaries component ?
- Back to top button - Back to top button
- Take a look at some components [here](http://react-materialize.github.io/react-materialize/?path=/story/css-grid--default)

View file

@ -1,6 +1,6 @@
{ {
"short_name": "Meal Planner", "short_name": "Chef's",
"name": "Chef's Meal Planner", "name": "Chef's | Meal Planner",
"icons": [ "icons": [
{ {
"src": "favicon.ico", "src": "favicon.ico",

View file

@ -1,8 +1,8 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Router } from "./utils/router"; import { Router } from "./utils/router";
import { Switch, Route, Redirect } from "react-router-dom"; import { Switch, Route, Redirect } from "react-router-dom";
import { HomePage } from "./pages/Home"; import { Home } from "./pages/Home";
import { MealPage } from "./pages/Meal"; import { Meal } from "./pages/Meal";
import { SearchPage } from "./pages/Search"; import { SearchPage } from "./pages/Search";
import { CategoryListPage } from "./pages/CategoryList"; import { CategoryListPage } from "./pages/CategoryList";
import { CategoryPage } from "./pages/Category"; import { CategoryPage } from "./pages/Category";
@ -113,10 +113,10 @@ export const App = () => {
/> />
<Switch> <Switch>
<Route exact path="/"> <Route exact path="/">
<HomePage handleClick={getRandomMeal} buttonUrl={buttonUrl} /> <Home buttonUrl={buttonUrl} />
</Route> </Route>
<Route exact path={buttonUrl}> <Route exact path={buttonUrl}>
<MealPage meal={meal} getMeal={getRandomMeal} /> <Meal meal={meal} getMeal={getRandomMeal} />
</Route> </Route>
<Route exact path="/categories"> <Route exact path="/categories">
<CategoryListPage <CategoryListPage
@ -145,7 +145,7 @@ export const App = () => {
<NotFoundPage handleClick={getRandomMeal} /> <NotFoundPage handleClick={getRandomMeal} />
</Route> </Route>
<Route path="/:idMeal"> <Route path="/:idMeal">
<MealPage meal={meal} getMeal={getMeal} /> <Meal meal={meal} getMeal={getMeal} />
</Route> </Route>
<Route path="*"> <Route path="*">
<Redirect to="/404" /> <Redirect to="/404" />

View file

@ -4,8 +4,8 @@ import { Link, useRouteMatch } from "react-router-dom";
const CategoryEntry = props => { const CategoryEntry = props => {
const { const {
strCategory, strCategory,
strCategoryThumb, strCategoryThumb
strCategoryDescription // strCategoryDescription
} = props.category; } = props.category;
const { url } = useRouteMatch(); const { url } = useRouteMatch();

View file

@ -16,7 +16,6 @@ export const Navbar = props => {
{links.map((link, i) => ( {links.map((link, i) => (
<FooterLink i={i} link={link} /> <FooterLink i={i} link={link} />
))} ))}
<li> <li>
<RandomButton <RandomButton
handleClick={props.handleClick} handleClick={props.handleClick}

View file

@ -1,14 +1,14 @@
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
export const RandomButton = props => { export const RandomButton = ({ url, size, handleClick }) => {
const classString = `waves-effect waves-light btn-${props.size}`; const classString = `waves-effect waves-light btn-${size}`;
return ( return (
<Link to={props.url}> <Link to={url}>
<button <button
// className="waves-effect waves-light btn-small" // className="waves-effect waves-light btn-small"
className={classString} className={classString}
onClick={props.handleClick} onClick={handleClick}
> >
Random Recipe Random Recipe
</button> </button>

View file

@ -15,10 +15,10 @@ div {
white-space: pre-wrap; white-space: pre-wrap;
} }
.background { /* .background {
background-image: url(./images/Chef.svg); background-image: url(./images/chef.svg);
/* width: 100%; */ width: 100%;
} } */
.logo { .logo {
font-family: "Marck Script", cursive; font-family: "Marck Script", cursive;

View file

@ -1,14 +1,14 @@
import React from "react"; import React from "react";
import { RandomButton } from "../components/RandomButton"; import { RandomButton } from "../components/RandomButton";
export const HomePage = props => { export const Home = ({ buttonUrl }) => {
return ( return (
<div className="section "> <div className="section ">
<div className="container "> <div className="container ">
<div className="row"> <div className="row">
<div className="col s12 m6"> <div className="col s12 m6">
<h1 className="logo">Chef's Online Cookbook</h1> <h1 className="logo">Chef's Online Cookbook</h1>
<RandomButton url={props.buttonUrl} size="large" /> <RandomButton url={buttonUrl} size="large" />
</div> </div>
<div className="col s12 m6"> <div className="col s12 m6">
<img <img

View file

@ -4,7 +4,7 @@ import { IngredientList } from "../components/IngredientList";
import { Recipe } from "../components/Recipe"; import { Recipe } from "../components/Recipe";
import { useParams, Redirect } from "react-router-dom"; import { useParams, Redirect } from "react-router-dom";
export const MealPage = props => { export const Meal = props => {
const { getMeal } = props; const { getMeal } = props;
const { idMeal } = useParams(); const { idMeal } = useParams();

111
src/utils/authentication.js Normal file
View file

@ -0,0 +1,111 @@
import React, { useState, useEffect, useContext, createContext } from "react";
import queryString from "query-string";
import * as firebase from "firebase/app";
import "firebase/auth";
if (!firebase.apps.length) {
// Replace with your own Firebase credentials
firebase.initializeApp({
apiKey: "AIzaSyBkkFF0XhNZeWuDmOfEhsgdfX1VBG7WTas",
authDomain: "divjoy-demo.firebaseapp.com",
projectId: "divjoy-demo",
appID: "divjoy-demo"
});
}
const authContext = createContext();
// Provider component that wraps your app and makes auth object ...
// ... available to any child component that calls useAuth().
export function ProvideAuth({ children }) {
const auth = useProvideAuth();
return <authContext.Provider value={auth}>{children}</authContext.Provider>;
}
// Hook for child components to get the auth object ...
// ... update when it changes.
export const useAuth = () => {
return useContext(authContext);
};
// Provider hook that creates auth object and handles state
function useProvideAuth() {
const [user, setUser] = useState(null);
const signin = (email, password) => {
return firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(response => {
setUser(response.user);
return response.user;
});
};
const signup = (email, password) => {
return firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(response => {
setUser(response.user);
return response.user;
});
};
const signout = () => {
return firebase
.auth()
.signOut()
.then(() => {
setUser(false);
});
};
const sendPasswordResetEmail = email => {
return firebase
.auth()
.sendPasswordResetEmail(email)
.then(() => {
return true;
});
};
const confirmPasswordReset = (password, code) => {
// Get code from query string object
const resetCode = code || getFromQueryString("oobCode");
return firebase
.auth()
.confirmPasswordReset(resetCode, password)
.then(() => {
return true;
});
};
// Subscribe to user on mount
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
setUser(user);
} else {
setUser(false);
}
});
// Subscription unsubscribe function
return () => unsubscribe();
}, []);
return {
user,
signin,
signup,
signout,
sendPasswordResetEmail,
confirmPasswordReset
};
}
const getFromQueryString = key => {
return queryString.parse(window.location.search)[key];
};