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
This commit is contained in:
Carl Dungca
2020-10-14 13:00:46 -07:00
committed by GitHub
parent ef9fc03843
commit 9f2a822a54
9 changed files with 149 additions and 6 deletions

View File

@@ -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 }) => (
<GlobalState>{element}</GlobalState>
)

View File

@@ -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 }) => (
<GlobalState>{element}</GlobalState>
)

View File

@@ -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 }) => (
<Card style={{ width: "44rem", height: "18rem", marginBottom: "15px" }}>
<Card style={{ width: "44rem", height: "23rem", marginBottom: "15px" }}>
<Row aria-label={book.title}>
<Col xs={3}>
<Card.Img
@@ -44,6 +45,7 @@ const BookCard = ({ book }) => (
<div style= {{ width: "30px", height: "30px" }}>
<a href={book.url} ><GoodReadsImage /></a>
</div>
<Bookmark book={book} />
</div>
</Card.Subtitle>
<p style={{ color: "gray", fontSize: "0.8rem", paddingTop: "1rem" }}>

View File

@@ -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 (
<div onClick={() => updateReadingList({ type: 'bookmark', retrievedBook: book })}>
<Button style={{
height: "30px",
width: "30px",
marginLeft: "0.25rem",
display: "grid",
justifyContent: "center",
alignContent: "center" }}
variant={ readingListIds.includes(book.id) ? "success" : "light"
}>
<span>
🔖
</span>
</Button>
</div>
)
}

View File

@@ -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 (
<StaticQuery
query={graphql`
@@ -26,6 +29,9 @@ export default () => {
activeKey="/home"
>
<div className="sidebar-sticky" role="navigation" aria-label="Sidebar">
<div style={{position: "relative", left: "0.9rem", paddingBottom: "0.2rem"}}>
<Link to="/readingList">🔖 Reading List ({readingList.bookIds.length})</Link>
</div>
{data.allCategoriesJson.edges.map(function(x, index) {
return (
<Nav.Item key={x.node.name}>

View File

@@ -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
}
}
}

View File

@@ -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 (
<BookmarkContext.Provider value={{readingList, updateReadingList}}>
{children}
</BookmarkContext.Provider>
)
}

View File

@@ -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 (
<Layout>
<SEO title="Home" />

View File

@@ -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 (
<Layout>
<SEO title="Reading list" />
<Container fluid>
<Row>
<Col xs={2}>
<SideBar />
</Col>
<Col>
<h2>Your reading list</h2>
<Link to="/">Go back to the homepage</Link>
{
readingList.bookIds.map(bookId => {
return <Bookcard book={readingList.books[bookId]} key={bookId} />
})
}
</Col>
</Row>
</Container>
<p>Reading List</p>
</Layout>
)}
export default ReadingList