diff --git a/src/context.tsx b/src/context.tsx index 24ff9af..f5c5243 100644 --- a/src/context.tsx +++ b/src/context.tsx @@ -3,8 +3,9 @@ import { useLibrary, UseLibraryReturnTuple } from "./hooks/useLibrary"; import { IBook } from "./types/book"; export const BookListContext = React.createContext([ - [], + {}, (book: IBook) => {}, + [], ]); export const BookListContextProvider: React.FC = ({ children }) => { diff --git a/src/hooks/useLibrary.ts b/src/hooks/useLibrary.ts index a0a8d34..ca295f5 100644 --- a/src/hooks/useLibrary.ts +++ b/src/hooks/useLibrary.ts @@ -1,38 +1,46 @@ import { useEffect, useState } from "react"; import { IBook } from "~/types/book"; import { - getBookList, - getTitleList, - setBook, - updateTitleList, + getBookHT, + getHashList, + saveBook, + updateHashList, } from "~/utils/localStorage"; export type AddBookFT = (book: IBook) => void; -export type UseLibraryReturnTuple = [IBook[], AddBookFT]; +export type UseLibraryReturnTuple = [ + Record, + AddBookFT, + string[] +]; export const useLibrary = (): UseLibraryReturnTuple => { - const [bookList, setBookList] = useState([]); - const [titleList, setTitleList] = useState([]); + const [library, setLibrary] = useState>({}); + const [hashList, setHashList] = useState([]); const addBook: AddBookFT = (book) => { const key = book.hash || Date.now().toString(); - if (key && !titleList.includes(key)) { - setTitleList([key, ...titleList]); - setBookList([book, ...bookList]); + if (key && !hashList.includes(key)) { + setHashList([key, ...hashList]); + setLibrary((prev) => ({ [key]: book, ...prev })); - setBook(key, book); + saveBook(key, book); } }; useEffect(() => { - const receivedTitleList = getTitleList(); + const receivedHashList = getHashList(); - setTitleList(receivedTitleList); - setBookList(getBookList(receivedTitleList)); + setHashList(receivedHashList); + setLibrary(getBookHT(receivedHashList)); }, []); - useEffect(() => updateTitleList(titleList), [titleList]); + useEffect(() => updateHashList(hashList), [hashList]); - return [bookList, addBook]; + useEffect(() => { + console.log(library); + }, [library]); + + return [library, addBook, hashList]; }; diff --git a/src/pages/Bookshelf/index.tsx b/src/pages/Bookshelf/index.tsx index 697fe4f..e337957 100644 --- a/src/pages/Bookshelf/index.tsx +++ b/src/pages/Bookshelf/index.tsx @@ -13,7 +13,7 @@ export const Bookshelf = () => {
- {books.map((book, index) => ( + {Object.values(books).map((book, index) => ( ))}
diff --git a/src/pages/UploadForm/index.tsx b/src/pages/UploadForm/index.tsx index 7c5f5fc..824211a 100644 --- a/src/pages/UploadForm/index.tsx +++ b/src/pages/UploadForm/index.tsx @@ -3,7 +3,7 @@ import { useLocation } from "wouter"; import plusIcon from "~/assets/plus.svg"; import styles from "./UploadForm.module.css"; -import { submitFile, validateResponse, validState } from "~/api"; +import { submitFile, validateResponse, validState } from "~/utils/api"; import { BookListContext } from "~/context"; export const UploadForm = () => { diff --git a/src/api.ts b/src/utils/api.ts similarity index 84% rename from src/api.ts rename to src/utils/api.ts index 37e03c0..b8bb602 100644 --- a/src/api.ts +++ b/src/utils/api.ts @@ -1,6 +1,6 @@ import { IBook, requiredBookProps } from "~/types/book"; -import { API_URL } from "./constants"; +import { API_URL } from "~/constants"; export const validState = (file: File | undefined): file is File => { if (!file) throw new Error("Book file is required. Please, attach one"); @@ -42,8 +42,11 @@ export const submitFile = async ( export const validateResponse = (content: unknown): content is IBook => { if (content && typeof content === "object") for (const key of requiredBookProps) - if (!(key in content)) - throw new Error(`${key} is not specified in server response`); + if (!(key in content)) { + if (import.meta.env.NODE_ENV === "development") + console.log(`${key} is not specified in server response`); + return false; + } return true; }; diff --git a/src/utils/localStorage.ts b/src/utils/localStorage.ts index d454a84..a5ffd91 100644 --- a/src/utils/localStorage.ts +++ b/src/utils/localStorage.ts @@ -1,33 +1,31 @@ import { IBook } from "~/types/book"; import { isArrOfStr } from "~/types/utils"; -import { validateResponse } from "~/api"; +import { validateResponse } from "~/utils/api"; -export const getTitleList = () => { - const titleListStr = localStorage.getItem("list") || "[]"; - const titleList: unknown = JSON.parse(titleListStr); +export const getHashList = () => { + const hashListStr = localStorage.getItem("list") || "[]"; + const hashList: unknown = JSON.parse(hashListStr); - if (isArrOfStr(titleList)) return titleList; + if (isArrOfStr(hashList)) return hashList; else { localStorage.setItem("list", "[]"); return []; } }; -export const getBookList = (titleList: string[]) => - titleList - .map((hash) => JSON.parse(localStorage.getItem(hash) || "{}")) - .filter((obj): obj is IBook => { - try { - return validateResponse(obj); - } catch (err) { - if (import.meta.env.NODE_ENV === "development") - console.log(err.message); - return false; - } - }); +export const getBookHT = (hashList: string[]) => { + const bookHT: Record = {}; -export const setBook = (key: string, book: IBook) => + hashList.forEach((hash) => { + const obj: unknown = JSON.parse(localStorage.getItem(hash) || "{}"); + if (validateResponse(obj)) bookHT[hash] = obj; + }); + + return bookHT; +}; + +export const saveBook = (key: string, book: IBook) => localStorage.setItem(key, JSON.stringify(book)); -export const updateTitleList = (titleList: string[]) => - localStorage.setItem("list", JSON.stringify(titleList)); +export const updateHashList = (hashList: string[]) => + localStorage.setItem("list", JSON.stringify(hashList));