feat: remove login and profile

This commit is contained in:
Ruidy 2021-12-13 12:45:29 -04:00
parent 09b69aeccb
commit 0ce8b1cfc2
15 changed files with 16 additions and 390 deletions

View file

@ -1,21 +1,11 @@
import { PreLoader } from "./components/PreLoader";
import "./index.css";
import MainLayout from "./layouts/MainLayout";
import { AppRouter, Router } from "./router";
import { useAuth0 } from "./utils/auth0-spa";
export const App = () => {
const { loading } = useAuth0();
return loading ? (
<div className="container center-align valign-wrapper">
<PreLoader />
</div>
) : (
<Router>
<MainLayout>
<AppRouter />
</MainLayout>
</Router>
);
};
export const App = () => (
<Router>
<MainLayout>
<AppRouter />
</MainLayout>
</Router>
);

View file

@ -1,9 +1,7 @@
import { MouseEventHandler } from "react";
import { Link } from "react-router-dom";
import { buttonURL, links } from "../constants";
import ChefImage from "../images/chef.svg";
import SpecialEventImage from "../images/special_event.svg";
import { useAuth0 } from "../utils/auth0-spa";
import { FooterLink } from "./FooterLink";
import { RandomButton } from "./RandomButton";
@ -13,7 +11,6 @@ type Props = {
};
export const SideNav = ({ showNav, closeNavClick }: Props) => {
const { isAuthenticated, user } = useAuth0();
let transformStyle = {
transform: showNav ? "translateX(0%)" : "translateX(-105%)",
transition: "0.5s",
@ -44,18 +41,6 @@ export const SideNav = ({ showNav, closeNavClick }: Props) => {
<i className="material-icons right" onClick={closeNavClick}>
close
</i>
<Link to="/profile">
{isAuthenticated ? (
<div>
<img className="circle" src={user.picture} alt="user_avatar" />
<span className="white-text name">{user.name}</span>
<span className="white-text email">{user.email}</span>
</div>
) : (
<img className="circle" src={ChefImage} alt="user_avatar" />
)}
</Link>
</div>
</li>
@ -73,7 +58,6 @@ export const SideNav = ({ showNav, closeNavClick }: Props) => {
{links.map((link, i) => (
<FooterLink key={i} link={link} />
))}
{isAuthenticated && <FooterLink link="profile" />}
</ul>
);
};

View file

@ -7,14 +7,13 @@ type Props = {
ingredients: string[][];
recipe: string;
meal: Meal;
handleFavChange: () => void;
};
export const MealPage = ({ meal, ingredients, recipe, handleFavChange }: Props) => (
export const MealPage = ({ meal, ingredients, recipe }: Props) => (
<section className="container">
<div className="row">
<div className="col s12 l6">
<MealPresentation meal={meal} handleFavChange={handleFavChange} />
<MealPresentation meal={meal} />
</div>
<div className="col s12 l6">
<MealIngredientList ingredients={ingredients} />

View file

@ -3,12 +3,10 @@ import { Meal } from "../../../types/meal";
type Props = {
meal: Meal;
handleFavChange: () => void;
};
export const MealPresentation = ({
meal: { mealName, imgAddress, videoAddress, mealCategory, mealArea, isFav },
handleFavChange,
meal: { mealName, imgAddress, videoAddress, mealCategory, mealArea },
}: Props) => {
return (
<div className="row">
@ -36,17 +34,6 @@ export const MealPresentation = ({
<div className="chip">
<b>Origin:</b> {mealArea}
</div>
<div className="chip">
<b>{isFav ? "Remove from favourites" : "Add to favourites"}:</b>
<Link to="#">
{" "}
<i className="material-icons tiny" onClick={handleFavChange}>
{isFav ? "favorite" : "favorite_border"}
</i>
</Link>
</div>
</li>
</ul>
</div>

View file

@ -1,21 +1,15 @@
import { useEffect, useState } from "react";
import { useEffect } from "react";
import { useParams } from "react-router-dom";
import { useFirebase } from "../../services/Firebase";
import { useMeal } from "../../store/meal";
import { fetchMeal, fetchRandomMeal } from "../../store/meal/async";
import { useAuth0 } from "../../utils/auth0-spa";
import { NotFound } from "../NotFound";
import { MealPage } from "./components/MealPage";
import { buildIngredientList, buildMealProps } from "./service";
export const Meal = () => {
// hooks
const { user, isAuthenticated } = useAuth0();
const { id } = useParams<{ id: string }>();
const { state, dispatch } = useMeal();
const fb = useFirebase();
// local state
const [isFav, setIsFav] = useState<boolean>();
// variables
const mealItem = state.meals?.[0];
// effects
@ -24,30 +18,7 @@ export const Meal = () => {
!id ? fetchRandomMeal(dispatch) : fetchMeal(dispatch, id);
}, [id, dispatch]);
/** 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 = buildMealProps(mealItem, isFav!);
const item = buildMealProps(mealItem);
const ingredients = buildIngredientList(mealItem);
return !!state.meals ? (
@ -55,7 +26,6 @@ export const Meal = () => {
meal={item}
ingredients={ingredients}
recipe={mealItem?.strInstructions}
handleFavChange={handleFavChange}
/>
) : (
<NotFound />

View file

@ -14,11 +14,10 @@ export const buildIngredientList = (mealItem: MealApi): string[][] => {
return ingredients;
};
export const buildMealProps = (mealItem: MealApi, isFav: boolean) => ({
export const buildMealProps = (mealItem: MealApi) => ({
mealName: mealItem?.strMeal,
imgAddress: mealItem?.strMealThumb,
videoAddress: mealItem?.strYoutube,
mealCategory: mealItem?.strCategory,
mealArea: mealItem?.strArea,
isFav,
});

View file

@ -1,26 +0,0 @@
import { CardEntry } from "../../../components/CardEntry";
import { MealSummary } from "../../../types/meal";
type Props = {
user: any;
meals: MealSummary[];
};
export const ProfilePage = ({ user, meals }: Props) => (
<div className="container">
<div className="row valign-wrapper">
<img className="left circle responsive-img" src={user.picture} alt="Avatar" width="15%" />
<h2 className="col s9">{user.name}</h2>
</div>
<div className="row">
<b>Email: </b>
{user.email}
<h3>Favourites meals</h3>
<ul>
{meals?.map((meal) => (
<CardEntry key={meal.idMeal} meal={meal} />
))}
</ul>
</div>
</div>
);

View file

@ -1,26 +0,0 @@
import { 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 = () => {
const { loading, user } = useAuth0();
const [favs, setFavs] = useState([] as MealSummary[]);
const db = useFirebase();
useEffect(() => {
db.getByEmail(user.email).then((res) => {
setFavs(res.favs);
});
}, [db, user.email]);
return loading || !user ? ( // is caught by PrivateRoute
<div className="container center-align">
<PreLoader />
</div>
) : (
<ProfilePage user={user} meals={favs} />
);
};

View file

@ -3,33 +3,12 @@ import ReactDOM from "react-dom";
import "./index.css";
import { App } from "./App";
import * as serviceWorker from "./serviceWorker";
import { Auth0Provider } from "./utils/auth0-spa";
import history from "./utils/history";
import { FirebaseContext } from "./services/Firebase";
import { AppProvider } from "./store/meal";
const onRedirectCallBack = (appState) => {
history.push(
appState && appState.targetUrl
? appState.targetUrl
: window.location.pathname
);
};
ReactDOM.render(
<Auth0Provider
domain={process.env.REACT_APP_DOMAIN}
client_id={process.env.REACT_APP_CLIENT_ID}
redirect_uri={window.location.origin}
onRedirectCallBack={onRedirectCallBack}
>
{/*<FirebaseContext.Provider value={new Firebase()}> todo fix Firebase app*/}
<FirebaseContext.Provider>
<AppProvider>
<App />
</AppProvider>
</FirebaseContext.Provider>
</Auth0Provider>,
<AppProvider>
<App />
</AppProvider>,
document.getElementById("root")
);

View file

@ -6,9 +6,7 @@ import { Contact } from "../containers/Contact";
import { Home } from "../containers/Home";
import { Meal } from "../containers/Meal";
import { NotFound } from "../containers/NotFound";
import { Profile } from "../containers/Profile";
import { Search } from "../containers/Search";
import { PrivateRoute } from "./PrivateRoute";
export const AppRouter = () => (
<Switch>
@ -16,8 +14,6 @@ export const AppRouter = () => (
<Home />
</Route>
<PrivateRoute exact path="/profile" component={Profile} />
<Route exact path={buttonURL}>
<Meal />
</Route>

View file

@ -1,22 +0,0 @@
import { FC, useEffect } from "react";
import { Route, RouteProps } from "react-router-dom";
import { useAuth0 } from "../utils/auth0-spa";
type Props = { component: FC } & RouteProps;
export const PrivateRoute = ({ component: Component, path, ...rest }: Props) => {
const { loading, isAuthenticated, loginWithRedirect } = useAuth0();
useEffect(() => {
// catches infinite loading when no user is logged in
if (loading || isAuthenticated) {
return;
}
const fn = async () => await loginWithRedirect({ appState: { targetUrl: path } });
fn();
}, [loading, isAuthenticated, loginWithRedirect, path]);
const render = (props: any) => (isAuthenticated ? <Component {...props} /> : null);
return <Route path={path} render={render} {...rest} />;
};

View file

@ -1,8 +0,0 @@
import { createContext, useContext } from "react";
import Firebase from "./firebase";
// create a Firebase context to make state available anywhere in the App.
const FirebaseContext = createContext(new Firebase());
export const useFirebase = () => useContext(FirebaseContext);
export default FirebaseContext;

View file

@ -1,96 +0,0 @@
import app from "firebase/app";
import "firebase/firestore";
const CONFIG = {
apiKey: process.env.REACT_APP_API_KEY,
authDomain: process.env.REACT_APP_AUTH_DOMAIN,
databaseURL: process.env.REACT_APP_DB_URL,
projectId: process.env.REACT_APP_PROJECT_ID,
storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_MSG_SENDER_ID,
appId: process.env.REACT_APP_APP_ID,
measurementId: process.env.REACT_APP_MEASUREMENT_ID,
};
const FAVS = "favs";
/**
* Firebase initializes the Application and provides method to interact with
* Firebase services as auth and firestore.
*/
export default class Firebase {
#db: any;
#collection: any;
constructor() {
app.initializeApp(CONFIG);
this.#db = app.firestore();
this.#collection = this.#db.collection("mealPlannerUsers");
}
/**
* Get infos for user 'email'.
*/
getByEmail = async (email: string) => {
const infos = await this.#collection
.where("email", "==", email)
.limit(1)
.get();
const favs = await this.getFavsByEmail(email);
return { infos, favs };
};
/**
* Get user's favourite recipes
* */
getFavsByEmail = async (email: string) => {
let favs = [] as any[];
const query = await this.#collection
.doc(email)
.collection(FAVS)
.where("isFav", "==", true)
.limit(10)
.get();
query.forEach((doc: any) => favs.push(doc.data()));
return favs;
};
isFav = async (email: string, idMeal: string) => {
const query = await this.#collection
.doc(email)
.collection(FAVS)
.doc(idMeal)
.get();
const obj = query.data();
return obj?.isFav;
};
/**
* Create or update favourite status for an authenticated user.
*/
addToFavs = async (
email: string,
idMeal: string,
strMeal: string,
strMealThumb: string,
isFav: boolean
) => {
this.#collection
.doc(email)
.collection(FAVS)
.doc(idMeal)
.set({
email,
idMeal,
strMeal,
strMealThumb,
isFav,
})
.catch((err: any) => console.error("Error adding favs to database", err));
};
}

View file

@ -1,7 +0,0 @@
// This file centralizes all Firebase related exports
import Firebase from "./firebase";
import FirebaseContext, { useFirebase } from "./context";
export default Firebase;
export { FirebaseContext, useFirebase };

View file

@ -1,93 +0,0 @@
// adapted from: https://auth0.com/docs/quickstart/spa/react
import React, { useContext, useState, useEffect } from "react";
import createAuth0Client from "@auth0/auth0-spa-js";
const DEFAULT_REDIRECT_CALLBACK = () => {
window.history.replaceState({}, document.title, window.location.pathname);
};
export const Auth0Context = React.createContext();
export const useAuth0 = () => useContext(Auth0Context);
export const Auth0Provider = ({
children,
onRedirectCallBack = DEFAULT_REDIRECT_CALLBACK,
...initOptions
}) => {
const [isAuthenticated, setIsAuthenticated] = useState();
const [user, setUser] = useState();
const [auth0Client, setAuth0] = useState();
const [loading, setLoading] = useState(true);
const [popUpOpen, setPopUpOpen] = useState(false);
useEffect(() => {
const initAuth0 = async () => {
const auth0FromHook = await createAuth0Client(initOptions);
setAuth0(auth0FromHook);
if (
window.location.search.includes("code=") &&
window.location.search.includes("state=")
) {
const { appState } = await auth0FromHook.handleRedirectCallback();
onRedirectCallBack(appState);
}
const isAuthenticated = await auth0FromHook.isAuthenticated();
setIsAuthenticated(isAuthenticated);
if (isAuthenticated) {
const user = await auth0FromHook.getUser();
setUser(user);
}
setLoading(false);
};
initAuth0();
// eslint-disable-next-line
}, []);
const loginWithPopUp = async (params = {}) => {
setPopUpOpen(true);
try {
await auth0Client.loginWithPopUp(params);
} catch (error) {
console.error(error);
} finally {
setPopUpOpen(false);
}
const user = auth0Client.getUser();
setUser(user);
setIsAuthenticated(true);
};
const handleRedirectCallback = async () => {
setLoading(true);
await auth0Client.handleRedirectCallback();
const user = auth0Client.getUser();
setLoading(false);
setIsAuthenticated(true);
setUser(user);
};
return (
<Auth0Context.Provider
value={{
isAuthenticated,
user,
loading,
popUpOpen,
loginWithPopUp,
handleRedirectCallback,
getIdTokenClaims: (...props) => auth0Client.getIdTokenClaims(...props),
loginWithRedirect: (...props) =>
auth0Client.loginWithRedirect(...props),
getTokenWithPopUp: (...props) =>
auth0Client.getTokenWithPopUp(...props),
logout: (...props) => auth0Client.logout(...props)
}}
>
{children}
</Auth0Context.Provider>
);
};