From 7c528a59344a0abb1585eba1c47d4f8ab7b136c1 Mon Sep 17 00:00:00 2001
From: Loi Phan
© Copyright by @@ -142,108 +96,6 @@
Start by searching for a recipe or an ingredient. Have fun!
- - - - diff --git a/src/js/controller.js b/src/js/controller.js index edb7699..890d3e2 100644 --- a/src/js/controller.js +++ b/src/js/controller.js @@ -2,7 +2,9 @@ import * as model from './model.js'; import recipeView from './views/recipeView.js'; import searchView from './views/searchView.js'; import resultsView from './views/resultsView.js'; +import bookmarksView from './views/bookmarksView.js'; import paginationView from './views/paginationView.js'; +import addRecipeView from './views/addRecipeView.js'; import 'core-js/stable'; import 'regenerator-runtime/runtime'; @@ -21,10 +23,13 @@ const controlRecipes = async function () { // 0) Update results view to mark selected search results resultsView.update(model.getSearchResultsPage()); - // 1)Loading recipe + // 1) Updating bookmarks view + bookmarksView.update(model.state.bookmarks); + + // 2)Loading recipe await model.loadRecipe(id); - // 2) Rendering recipe + // 3) Rendering recipe recipeView.render(model.state.recipe); } catch (error) { recipeView.renderError(error); @@ -69,11 +74,36 @@ const controlServings = function (newServings) { recipeView.update(model.state.recipe); }; +const controlAddBookmark = function () { + // 1) Add/remove bookmark + if (!model.state.recipe.bookmarked) model.addBookmark(model.state.recipe); + else model.deleteBookmark(model.state.recipe.id); + + // 2) Update recipe view + recipeView.update(model.state.recipe); + + // 3) Render bookmarks + bookmarksView.render(model.state.bookmarks); +}; + +const controlBookmarks = function () { + bookmarksView.render(model.state.bookmarks); +}; + +const controlAddRecipe = function (newRecipe) { + console.log(newRecipe); + + // Upload the new recipe data +}; + const init = function () { + bookmarksView.addHandlerRender(controlBookmarks); recipeView.addHandleRender(controlRecipes); recipeView.addHandlerUpdateServings(controlServings); + recipeView.addHandlerAddBookmark(controlAddBookmark); searchView.addHandlerSearch(controlSearchResults); paginationView.addHandlerClick(controlPagination); + addRecipeView.addHandlerUpload(controlAddRecipe); }; init(); diff --git a/src/js/model.js b/src/js/model.js index 8b25ead..187b588 100644 --- a/src/js/model.js +++ b/src/js/model.js @@ -10,6 +10,7 @@ export const state = { page: 1, resultsPerPage: RES_PER_PAGE, }, + bookmarks: [], }; export const loadRecipe = async function (id) { @@ -28,6 +29,10 @@ export const loadRecipe = async function (id) { cookingTime: recipe.cooking_time, ingredients: recipe.ingredients, }; + + if (state.bookmarks.some(bookmark => bookmark.id === id)) + state.recipe.bookmarked = true; + else state.recipe.bookmarked = false; } catch (error) { throw error; } @@ -47,6 +52,7 @@ export const loadSearchResults = async function (query) { image: rec.image_url, }; }); + state.search.page = 1; } catch (error) { console.error(`${error} 💥💥💥`); throw error; @@ -69,3 +75,36 @@ export const updateServings = function (newServings) { state.recipe.servings = newServings; }; + +const persistBookmarks = function (recipe) { + localStorage.setItem('bookmarks', JSON.stringify(state.bookmarks)); +}; + +export const addBookmark = function (recipe) { + // Add bookmark + state.bookmarks.push(recipe); + + // Mark current recipe as bookmarks + if (recipe.id === state.recipe.id) state.recipe.bookmarked = true; + + persistBookmarks(); +}; + +export const deleteBookmark = function (id) { + // Delete bookmark + const index = state.bookmarks.findIndex(el => el.id === id); + state.bookmarks.splice(index, 1); + + // Mark current recipe as NOT bookmarked + if (id === state.recipe.id) state.recipe.bookmarked = false; + + persistBookmarks(); +}; + +const init = function () { + const storage = localStorage.getItem('bookmarks'); + if (storage) state.bookmarks = JSON.parse(storage); +}; + +init(); +console.log(state.bookmarks); diff --git a/src/js/views/View.js b/src/js/views/View.js index 3b50d4d..ca34071 100644 --- a/src/js/views/View.js +++ b/src/js/views/View.js @@ -3,12 +3,15 @@ import icons from '../../img/icons.svg'; export default class View { _data; - render(data) { + render(data, render = true) { if (!data || (Array.isArray(data) && data.length === 0)) return this.renderError(); this._data = data; const markup = this._generateMarkup(); + + if (!render) return markup; + this._clear(); this._parentElement.insertAdjacentHTML('afterbegin', markup); } @@ -23,12 +26,12 @@ export default class View { newElements.forEach((newEl, i) => { const curEl = curElements[i]; - console.log(curEl, newEl.isEqualNode(curEl)); // Update change TEXT if ( !newEl.isEqualNode(curEl) && - newEl.firstChild.nodeValue.trim() !== '' + newEl.firstChild?.nodeValue.trim() !== '' && + newEl.firstChild?.nodeValue.trim() !== undefined ) { curEl.textContent = newEl.textContent; } diff --git a/src/js/views/addRecipeView.js b/src/js/views/addRecipeView.js new file mode 100644 index 0000000..7d4cae5 --- /dev/null +++ b/src/js/views/addRecipeView.js @@ -0,0 +1,44 @@ +import View from './View.js'; +import icons from '../../img/icons.svg'; + +class AddRecipeView extends View { + _parentElement = document.querySelector('.upload'); + + _window = document.querySelector('.add-recipe-window'); + _overlay = document.querySelector('.overlay'); + _btnOpen = document.querySelector('.nav__btn--add-recipe'); + _btnClose = document.querySelector('.btn--close-modal'); + + constructor() { + super(); + this._addHandlerShowWindow(); + this._addHandlerHideWindow(); + } + + toogleWindow() { + this._overlay.classList.toggle('hidden'); + this._window.classList.toggle('hidden'); + } + + _addHandlerShowWindow() { + this._btnOpen.addEventListener('click', () => this.toogleWindow()); + } + + _addHandlerHideWindow() { + this._overlay.addEventListener('click', this.toogleWindow.bind(this)); + this._btnClose.addEventListener('click', this.toogleWindow.bind(this)); + } + + addHandlerUpload(handler) { + this._parentElement.addEventListener('submit', function (e) { + e.preventDefault(); + const dataArr = [...new FormData(this)]; + const data = Object.fromEntries(dataArr); + handler(data); + }); + } + + _generateMarkup() {} +} + +export default new AddRecipeView(); diff --git a/src/js/views/bookmarksView.js b/src/js/views/bookmarksView.js new file mode 100644 index 0000000..b4acdc2 --- /dev/null +++ b/src/js/views/bookmarksView.js @@ -0,0 +1,21 @@ +import View from './View.js'; +import previewView from './previewView.js'; +import icons from '../../img/icons.svg'; + +class BookmarksView extends View { + _parentElement = document.querySelector('.bookmarks__list'); + _errorMessage = 'No bookmarks yet. Find a nice recipe and bookmark it!'; + _message = ''; + + addHandlerRender(handler) { + window.addEventListener('load', handler); + } + + _generateMarkup() { + return this._data + .map(bookmark => previewView.render(bookmark, false)) + .join(''); + } +} + +export default new BookmarksView(); diff --git a/src/js/views/previewView.js b/src/js/views/previewView.js new file mode 100644 index 0000000..eddd9ec --- /dev/null +++ b/src/js/views/previewView.js @@ -0,0 +1,28 @@ +import View from './View.js'; +import icons from '../../img/icons.svg'; + +class PreviewView extends View { + _parentElement = ''; + + _generateMarkup() { + const id = window.location.hash.slice(1); + + return ` +${this._data.publisher}
+