From 9f2a822a54359b8060f633eb6bb54254603d597b Mon Sep 17 00:00:00 2001 From: Carl Dungca Date: Wed, 14 Oct 2020 13:00:46 -0700 Subject: [PATCH] Feature added: Mark to read (#252) * Rendered a static reading list button to each bookcard component * Attached click listener to bookmark button to log book data * Clicking bookmark button saves book information to a localStorage array * Updated card height to account for button * Updated saveBookToLocalStorage function to use objects instead of arrays * Can now toggle books in and out of localStorage * Reverted previous 4 commits so that data isn't directly written to localStorage * Created a sidebar link to reading list * Added placeholder bookmarks page * Bookmark button now updates state in index.js * Initialized context API * Wrote a reducer function to handle bookmark state changes * Configured reducer to add books in and out of state * Reading list is now preserved between state AND localStorage when changing categories * Fixed some code format issues * Rendered saved books in reading list component * Toggle apperance of bookmark button * Hacky fix for positioning of reading list sidebar link * Adjusted style and alignment of bookmark button * Added check to determine if window is defined in useEffect * Exported the gatsby-ssr API --- app/gatsby-browser.js | 8 +++++++- app/gatsby-ssr.js | 7 +++++++ app/src/components/bookcard.js | 4 +++- app/src/components/bookmark.js | 26 ++++++++++++++++++++++++ app/src/components/sidebar.js | 10 ++++++++-- app/src/context/bookReducer.js | 33 +++++++++++++++++++++++++++++++ app/src/context/globalState.js | 25 +++++++++++++++++++++++ app/src/pages/index.js | 6 ++++-- app/src/pages/readingList.js | 36 ++++++++++++++++++++++++++++++++++ 9 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 app/src/components/bookmark.js create mode 100644 app/src/context/bookReducer.js create mode 100644 app/src/context/globalState.js create mode 100644 app/src/pages/readingList.js diff --git a/app/gatsby-browser.js b/app/gatsby-browser.js index 5657199..e4f35ec 100644 --- a/app/gatsby-browser.js +++ b/app/gatsby-browser.js @@ -5,4 +5,10 @@ */ // You can delete this file if you're not using it -import "./src/styles/global.css" +import React from "react" + +import GlobalState from "./src/context/globalState" + +export const wrapRootElement = ({ element }) => ( + {element} +) diff --git a/app/gatsby-ssr.js b/app/gatsby-ssr.js index b17b8fc..679c9f2 100644 --- a/app/gatsby-ssr.js +++ b/app/gatsby-ssr.js @@ -5,3 +5,10 @@ */ // You can delete this file if you're not using it +import React from "react" + +import GlobalState from "./src/context/globalState" + +export const wrapRootElement = ({ element }) => ( + {element} +) diff --git a/app/src/components/bookcard.js b/app/src/components/bookcard.js index be16d3c..30292e8 100644 --- a/app/src/components/bookcard.js +++ b/app/src/components/bookcard.js @@ -4,6 +4,7 @@ import StarRatings from "react-star-ratings" import { Card, Row, Col } from "react-bootstrap" import AmazonURL from "../components/amazonurl" +import Bookmark from "../components/bookmark" import GoodReadsImage from "../components/goodreadsimage" const truncateContent = (content) => { @@ -14,7 +15,7 @@ const truncateContent = (content) => { }; const BookCard = ({ book }) => ( - + (
+

diff --git a/app/src/components/bookmark.js b/app/src/components/bookmark.js new file mode 100644 index 0000000..ac4d0d5 --- /dev/null +++ b/app/src/components/bookmark.js @@ -0,0 +1,26 @@ +import React, { useContext } from 'react' +import { Button } from "react-bootstrap" +import { BookmarkContext } from '../context/globalState' + +export default ({ book }) => { + const { updateReadingList, readingList } = useContext(BookmarkContext) + const readingListIds = readingList.bookIds + + return ( +

updateReadingList({ type: 'bookmark', retrievedBook: book })}> + +
+ ) +} diff --git a/app/src/components/sidebar.js b/app/src/components/sidebar.js index 9f6930a..69f69cb 100644 --- a/app/src/components/sidebar.js +++ b/app/src/components/sidebar.js @@ -1,10 +1,13 @@ -import React from "react" +import React, { useContext } from "react" import { Nav } from "react-bootstrap" -import { StaticQuery, graphql } from "gatsby" +import { StaticQuery, graphql, Link } from "gatsby" import "../styles/sidebar.css" +import { BookmarkContext } from '../context/globalState' var slugify = require('slugify') export default () => { + const { readingList } = useContext(BookmarkContext) + return ( { activeKey="/home" >
+
+ 🔖 Reading List ({readingList.bookIds.length}) +
{data.allCategoriesJson.edges.map(function(x, index) { return ( diff --git a/app/src/context/bookReducer.js b/app/src/context/bookReducer.js new file mode 100644 index 0000000..b61da1f --- /dev/null +++ b/app/src/context/bookReducer.js @@ -0,0 +1,33 @@ +export default function bookReducer(state, action) { + let readingListCopy = {...state} + + switch (action.type) { + case 'init': { + if (action.content) { + return action.content + } + return readingListCopy + } + case 'bookmark': { + let { bookIds, books } = readingListCopy + const { retrievedBook } = action + const retrievedBookId = retrievedBook.id + // Delete existing bookmark + if (bookIds.includes(retrievedBookId)) { + readingListCopy.bookIds = bookIds.filter(id => id !== retrievedBookId) + delete books[retrievedBookId] + if (typeof window !== undefined) { + localStorage.setItem('Bookmarks', JSON.stringify(readingListCopy)) + } + // Add new bookmark + } else { + books[retrievedBookId] = retrievedBook + bookIds.push(retrievedBookId) + if (typeof window !== undefined) { + localStorage.setItem('Bookmarks', JSON.stringify(readingListCopy)) + } + } + return readingListCopy + } + } + } \ No newline at end of file diff --git a/app/src/context/globalState.js b/app/src/context/globalState.js new file mode 100644 index 0000000..e5e7f8a --- /dev/null +++ b/app/src/context/globalState.js @@ -0,0 +1,25 @@ +import React, { useReducer, useEffect } from 'react' +import bookReducer from './bookReducer' + +export const BookmarkContext = React.createContext() + +export default function GlobalState({children}) { + let [readingList, updateReadingList] = useReducer(bookReducer, { + books: {}, + bookIds: [] + }) + + useEffect(() => { + if (typeof window !== undefined) { + const retrievedBooks = JSON.parse(localStorage.getItem('Bookmarks')) + console.log(retrievedBooks) + updateReadingList({type: 'init', content: retrievedBooks}) + } + }, []) + + return ( + + {children} + + ) +} diff --git a/app/src/pages/index.js b/app/src/pages/index.js index 4a51d2d..3ee57ed 100644 --- a/app/src/pages/index.js +++ b/app/src/pages/index.js @@ -18,11 +18,13 @@ function myFunction(setMaximumBooksToShow, maximumBooksToShow) { } export default ({ data }) => { - let [maximumBooksToShow, setMaximumBooksToShow] = useState(12) + let [maximumBooksToShow, setMaximumBooksToShow] = useState(12) + useEffect(() => { window.document.onscroll = () => myFunction(setMaximumBooksToShow, maximumBooksToShow) - }) + }) + return ( diff --git a/app/src/pages/readingList.js b/app/src/pages/readingList.js new file mode 100644 index 0000000..8818c0b --- /dev/null +++ b/app/src/pages/readingList.js @@ -0,0 +1,36 @@ +import React, { useContext } from "react" +import { Link } from "gatsby" +import { Container, Row, Col } from "react-bootstrap" +import SideBar from "../components/sidebar" +import Layout from "../components/layout" +import SEO from "../components/seo" +import Bookcard from "../components/bookcard" +import { BookmarkContext } from "../context/globalState" + +const ReadingList = () => { + const { readingList } = useContext(BookmarkContext) + + return ( + + + + + + + + +

Your reading list

+ Go back to the homepage + { + readingList.bookIds.map(bookId => { + return + }) + } + +
+
+

Reading List

+
+)} + +export default ReadingList