mirror of
https://github.com/rjNemo/meal_planner
synced 2026-06-12 13:26:45 +00:00
added FireBase firestore
This commit is contained in:
parent
0967f5f2ad
commit
47c9d5f4d9
13 changed files with 1157 additions and 106 deletions
26
.gitignore
vendored
26
.gitignore
vendored
|
|
@ -1,26 +0,0 @@
|
||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.js
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
/src/utils/auth_config.json
|
|
||||||
/src/utils/secret.js
|
|
||||||
1
TODO.md
1
TODO.md
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
- [ ] 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)
|
||||||
- [ ] Local storage of prefeernces
|
- [ ] Local storage of prefeernces
|
||||||
|
- [ ] Firebase
|
||||||
- [ ] Breadcrumb
|
- [ ] Breadcrumb
|
||||||
- [ ] Cookie bar
|
- [ ] Cookie bar
|
||||||
- [ ] code cleanup (props and refactoring)
|
- [ ] code cleanup (props and refactoring)
|
||||||
|
|
|
||||||
1055
package-lock.json
generated
1055
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -7,6 +7,8 @@
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.4.0",
|
"@testing-library/react": "^9.4.0",
|
||||||
"@testing-library/user-event": "^7.2.1",
|
"@testing-library/user-event": "^7.2.1",
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"firebase": "^7.13.2",
|
||||||
"react": "^16.12.0",
|
"react": "^16.12.0",
|
||||||
"react-dom": "^16.12.0",
|
"react-dom": "^16.12.0",
|
||||||
"react-router-dom": "^5.1.2",
|
"react-router-dom": "^5.1.2",
|
||||||
|
|
@ -33,4 +35,4 @@
|
||||||
"last 1 safari version"
|
"last 1 safari version"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
src/App.jsx
20
src/App.jsx
|
|
@ -81,13 +81,13 @@ export const App = () => {
|
||||||
strMeasure19: "",
|
strMeasure19: "",
|
||||||
strMeasure20: "",
|
strMeasure20: "",
|
||||||
strSource: "https://www.bbcgoodfood.com/recipes/pilchard-puttanesca",
|
strSource: "https://www.bbcgoodfood.com/recipes/pilchard-puttanesca",
|
||||||
dateModified: null
|
dateModified: null,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
const [meal, setMeal] = useState(mealDef);
|
const [meal, setMeal] = useState(mealDef);
|
||||||
|
|
||||||
const getMeal = id => {
|
const getMeal = (id) => {
|
||||||
getData(id, setMeal, "lookup");
|
getData(id, setMeal, "lookup");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -95,13 +95,13 @@ export const App = () => {
|
||||||
getData("random", setMeal);
|
getData("random", setMeal);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSearchResults = e => {
|
const getSearchResults = (e) => {
|
||||||
searchString === ""
|
searchString === ""
|
||||||
? e.preventDefault()
|
? e.preventDefault()
|
||||||
: getData(searchString, setSearchResults, "search");
|
: getData(searchString, setSearchResults, "search");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = ev => {
|
const handleChange = (ev) => {
|
||||||
const { value } = ev.target;
|
const { value } = ev.target;
|
||||||
setSearchString(value);
|
setSearchString(value);
|
||||||
};
|
};
|
||||||
|
|
@ -109,18 +109,18 @@ export const App = () => {
|
||||||
const buttonUrl = "/random";
|
const buttonUrl = "/random";
|
||||||
|
|
||||||
const [showNav, setShowNav] = useState(false);
|
const [showNav, setShowNav] = useState(false);
|
||||||
const openNavClick = ev => {
|
const openNavClick = (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
setShowNav(true);
|
setShowNav(true);
|
||||||
document.addEventListener("keydown", handleEscKey);
|
document.addEventListener("keydown", handleEscKey);
|
||||||
// document.addEventListener("click", handleOutsideClick);
|
// document.addEventListener("click", handleOutsideClick);
|
||||||
};
|
};
|
||||||
const closeNavClick = ev => {
|
const closeNavClick = (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
setShowNav(false);
|
setShowNav(false);
|
||||||
document.removeEventListener("keydown", handleEscKey);
|
document.removeEventListener("keydown", handleEscKey);
|
||||||
};
|
};
|
||||||
const handleEscKey = ev => {
|
const handleEscKey = (ev) => {
|
||||||
if (ev.key === "Escape") {
|
if (ev.key === "Escape") {
|
||||||
setShowNav(false);
|
setShowNav(false);
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +201,7 @@ export const App = () => {
|
||||||
<NotFoundPage handleClick={getRandomMeal} />
|
<NotFoundPage handleClick={getRandomMeal} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route path="/:idMeal">
|
<Route path="/:id">
|
||||||
<MealController
|
<MealController
|
||||||
meal={meal}
|
meal={meal}
|
||||||
getMeal={getMeal}
|
getMeal={getMeal}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
export const MealPresentation = props => {
|
export const MealPresentation = (props) => {
|
||||||
const {
|
const {
|
||||||
mealName,
|
mealName,
|
||||||
imgAddress,
|
imgAddress,
|
||||||
|
|
@ -9,8 +9,9 @@ export const MealPresentation = props => {
|
||||||
mealCategory,
|
mealCategory,
|
||||||
mealArea,
|
mealArea,
|
||||||
isFav,
|
isFav,
|
||||||
setIsFav
|
setIsFav,
|
||||||
} = props.meal;
|
} = props.meal;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col s12">
|
<div className="col s12">
|
||||||
|
|
@ -22,40 +23,41 @@ export const MealPresentation = props => {
|
||||||
<li>
|
<li>
|
||||||
<div className="chip">
|
<div className="chip">
|
||||||
<b>Video:</b>
|
<b>Video:</b>
|
||||||
<a href={videoAddress} target="blank">
|
<a href={videoAddress} target="blank" rel="noopener">
|
||||||
<i className="close material-icons">video_library</i>
|
<i className="close material-icons">video_library</i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{/* </li>
|
|
||||||
|
|
||||||
<li> */}
|
|
||||||
<div className="chip">
|
<div className="chip">
|
||||||
<b>Category: </b> {mealCategory}
|
<b>Category: </b> {mealCategory}
|
||||||
<Link to={`/categories/${mealCategory}`}>
|
<Link to={`/categories/${mealCategory}`}>
|
||||||
<i className="close material-icons">call_made</i>
|
<i className="close material-icons">call_made</i>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{/* </li>
|
|
||||||
<li> */}
|
|
||||||
<div className="chip">
|
<div className="chip">
|
||||||
<b>Origin:</b> {mealArea}
|
<b>Origin:</b> {mealArea}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="chip">
|
<div className="chip">
|
||||||
<b>
|
<b>
|
||||||
{isFav ? "Remove from favourites" : "Add to favourites"}:
|
{isFav ? "Remove from favourites" : "Add to favourites"}:
|
||||||
</b>
|
</b>
|
||||||
|
|
||||||
<Link to="#">
|
<Link to="#">
|
||||||
|
{" "}
|
||||||
<i
|
<i
|
||||||
className="close material-icons"
|
className="material-icons tiny"
|
||||||
onClick={() => setIsFav(!isFav)}
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsFav(!isFav);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{isFav ? "favorite" : "favorite_border"}
|
{isFav ? "favorite" : "favorite_border"}
|
||||||
</i>
|
</i>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,76 +1,48 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
import { useAuth0 } from "../utils/auth0-spa";
|
||||||
import { MealPage } from "../pages/MealPage";
|
import { MealPage } from "../pages/MealPage";
|
||||||
import { NotFoundPage } from "../pages/NotFoundPage";
|
import { NotFoundPage } from "../pages/NotFoundPage";
|
||||||
|
import { useFirebase } from "../services/Firebase";
|
||||||
|
// import Firebase from "../data/Firebase";
|
||||||
|
|
||||||
export const MealController = ({ meal, getMeal, getRandomMeal }) => {
|
export const MealController = ({ meal, getMeal, getRandomMeal }) => {
|
||||||
const { idMeal } = useParams();
|
const { user, isAuthenticated } = useAuth0();
|
||||||
|
const { id } = useParams();
|
||||||
|
const fb = useFirebase();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
idMeal === undefined ? getRandomMeal() : getMeal(idMeal);
|
id === undefined ? getRandomMeal() : getMeal(id);
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const mealItem = meal.meals[0];
|
const mealItem = meal.meals[0];
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
idMeal,
|
||||||
strMeal,
|
strMeal,
|
||||||
strMealThumb,
|
strMealThumb,
|
||||||
strYoutube,
|
strYoutube,
|
||||||
strCategory,
|
strCategory,
|
||||||
strArea,
|
strArea,
|
||||||
strInstructions
|
strInstructions,
|
||||||
} = mealItem;
|
} = mealItem;
|
||||||
|
|
||||||
// const setDbPromise = () => {
|
const [isFav, setIsFav] = useState(false);
|
||||||
// //check for support
|
|
||||||
// if (!("indexedDB" in window)) {
|
|
||||||
// console.log("This browser doesn't support IndexedDB");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var dbPromise = indexedDB.open("chefs-db", 1, function(upgradeDb) {
|
|
||||||
// if (!upgradeDb.objectStoreNames.contains("favourites")) {
|
|
||||||
// var favOS = upgradeDb.createObjectStore("favourites", {
|
|
||||||
// keyPath: "mealName"
|
|
||||||
// });
|
|
||||||
// favOS.createIndex("isFav", "isFav", { unique: true });
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// return dbPromise;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const [isFav, setIsFav] = useState(false);
|
|
||||||
|
|
||||||
// var dbPromise = setDbPromise();
|
|
||||||
|
|
||||||
// dbPromise
|
|
||||||
// .then(db => {
|
|
||||||
// var tx = db.transaction("favourites", "readwrite");
|
|
||||||
// var store = tx.objectStore("favourites");
|
|
||||||
// var item = {
|
|
||||||
// mealName: strMeal,
|
|
||||||
// isFav: isFav
|
|
||||||
// };
|
|
||||||
// store.add(item);
|
|
||||||
// return tx.complete;
|
|
||||||
// })
|
|
||||||
// .then(function() {
|
|
||||||
// console.log("added item to the favourite os!");
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const initState = Boolean(localStorage.getItem(strMeal));
|
|
||||||
const [isFav, setIsFav] = useState(localStorage.getItem(strMeal) === "fav");
|
|
||||||
|
|
||||||
// console.log(isFav);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
isFav
|
// console.log(user.email);
|
||||||
? localStorage.setItem(strMeal, "fav")
|
// console.log(idMeal);
|
||||||
: localStorage.removeItem(strMeal);
|
|
||||||
// console.log(localStorage.getItem(strMeal));
|
|
||||||
// console.log(isFav);
|
// console.log(isFav);
|
||||||
}, [isFav, strMeal]);
|
console.log(fb);
|
||||||
|
|
||||||
|
// const add2Fav = async (user, idMeal, isFav) => {
|
||||||
|
if (isAuthenticated) {
|
||||||
|
fb.add(user.email, idMeal, isFav);
|
||||||
|
}
|
||||||
|
// };
|
||||||
|
// add2Fav(user, idMeal, isFav).then((data) => console.log(data));
|
||||||
|
}, [user, idMeal, isFav]);
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
mealName: strMeal,
|
mealName: strMeal,
|
||||||
|
|
@ -79,7 +51,7 @@ export const MealController = ({ meal, getMeal, getRandomMeal }) => {
|
||||||
mealCategory: strCategory,
|
mealCategory: strCategory,
|
||||||
mealArea: strArea,
|
mealArea: strArea,
|
||||||
isFav: isFav,
|
isFav: isFav,
|
||||||
setIsFav: setIsFav
|
setIsFav: setIsFav,
|
||||||
};
|
};
|
||||||
|
|
||||||
let ingredientList = [];
|
let ingredientList = [];
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import { ProfilePage } from "../pages/ProfilePage";
|
||||||
export const ProfileController = () => {
|
export const ProfileController = () => {
|
||||||
const { loading, user } = useAuth0();
|
const { loading, user } = useAuth0();
|
||||||
|
|
||||||
|
// const fn = async () => await fire.getByEmail(user.email);
|
||||||
|
|
||||||
return loading || !user ? ( // is catched by PrivateRoute
|
return loading || !user ? ( // is catched by PrivateRoute
|
||||||
<div className="container center-align">
|
<div className="container center-align">
|
||||||
<PreLoader />
|
<PreLoader />
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,9 @@ import * as serviceWorker from "./serviceWorker";
|
||||||
import { Auth0Provider } from "./utils/auth0-spa";
|
import { Auth0Provider } from "./utils/auth0-spa";
|
||||||
import history from "./utils/history";
|
import history from "./utils/history";
|
||||||
import config from "./utils/auth_config.json"; // for safety reasons this file is not tracked by git
|
import config from "./utils/auth_config.json"; // for safety reasons this file is not tracked by git
|
||||||
|
import Firebase, { FirebaseContext } from "./services/Firebase";
|
||||||
|
|
||||||
const onRedirectCallBack = appState => {
|
const onRedirectCallBack = (appState) => {
|
||||||
history.push(
|
history.push(
|
||||||
appState && appState.targetUrl
|
appState && appState.targetUrl
|
||||||
? appState.targetUrl
|
? appState.targetUrl
|
||||||
|
|
@ -22,7 +23,9 @@ ReactDOM.render(
|
||||||
redirect_uri={window.location.origin}
|
redirect_uri={window.location.origin}
|
||||||
onRedirectCallBack={onRedirectCallBack}
|
onRedirectCallBack={onRedirectCallBack}
|
||||||
>
|
>
|
||||||
<App />
|
<FirebaseContext.Provider value={new Firebase()}>
|
||||||
|
<App />
|
||||||
|
</FirebaseContext.Provider>
|
||||||
</Auth0Provider>,
|
</Auth0Provider>,
|
||||||
document.getElementById("root")
|
document.getElementById("root")
|
||||||
);
|
);
|
||||||
|
|
|
||||||
5
src/services/Firebase/context.js
Normal file
5
src/services/Firebase/context.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { createContext, useContext } from "react";
|
||||||
|
|
||||||
|
const FirebaseContext = createContext(null);
|
||||||
|
export const useFirebase = () => useContext(FirebaseContext);
|
||||||
|
export default FirebaseContext;
|
||||||
40
src/services/Firebase/firebase.js
Normal file
40
src/services/Firebase/firebase.js
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import app from "firebase/app";
|
||||||
|
import "firebase/firestore";
|
||||||
|
import config from "./config.json";
|
||||||
|
|
||||||
|
const CONFIG = {
|
||||||
|
apiKey: config.apiKey,
|
||||||
|
authDomain: config.authDomain,
|
||||||
|
databaseURL: config.databaseURL,
|
||||||
|
projectId: config.projectId,
|
||||||
|
storageBucket: config.storageBucket,
|
||||||
|
messagingSenderId: config.messagingSenderId,
|
||||||
|
appId: config.appId,
|
||||||
|
measurementId: config.measurementId,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class Firebase {
|
||||||
|
constructor() {
|
||||||
|
app.initializeApp(CONFIG);
|
||||||
|
this.db = app.firestore();
|
||||||
|
}
|
||||||
|
|
||||||
|
add = async (email, id, fav) => {
|
||||||
|
await this.db
|
||||||
|
.collection("MealPlannerFavs")
|
||||||
|
.add({ email: email, idMeal: id, isfav: fav })
|
||||||
|
.then((ref) => {
|
||||||
|
console.log("Added document with ID: ", ref.id);
|
||||||
|
})
|
||||||
|
.catch((error) => console.error("Error adding document: ", error));
|
||||||
|
};
|
||||||
|
|
||||||
|
getByEmail = async (email) => {
|
||||||
|
const query = await this.db
|
||||||
|
.collection("MealPlannerFavs")
|
||||||
|
.where("email", "==", email)
|
||||||
|
.get();
|
||||||
|
const snapshot = query.docs[0];
|
||||||
|
return snapshot.data();
|
||||||
|
};
|
||||||
|
}
|
||||||
5
src/services/Firebase/index.js
Normal file
5
src/services/Firebase/index.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Firebase from "./firebase";
|
||||||
|
import FirebaseContext, { useFirebase } from "./context";
|
||||||
|
|
||||||
|
export default Firebase;
|
||||||
|
export { FirebaseContext, useFirebase };
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"domain": "chefs-meal-planner.eu.auth0.com",
|
|
||||||
"clientId": "EXe8HCfFd0jSSfqzjAvpdk72ce0y2Hh9"
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue