diff --git a/src/context.tsx b/src/context.tsx index f5c5243..a1cae71 100644 --- a/src/context.tsx +++ b/src/context.tsx @@ -1,10 +1,10 @@ import React from "react"; import { useLibrary, UseLibraryReturnTuple } from "./hooks/useLibrary"; -import { IBook } from "./types/book"; +import { BookT } from "./types/book"; export const BookListContext = React.createContext([ {}, - (book: IBook) => {}, + (book: BookT) => {}, [], ]); diff --git a/src/hooks/useBookState.ts b/src/hooks/useBookState.ts new file mode 100644 index 0000000..633dd1c --- /dev/null +++ b/src/hooks/useBookState.ts @@ -0,0 +1,48 @@ +import React, { useEffect, useState } from "react"; +import { BookState } from "~/types/book"; +import { loadBookState, saveBookState } from "~/utils/localStorage"; + +export const useBookState = ( + pagesReady: boolean, + hash: string | undefined, + goToPage: (pageNum: number) => void, + currentPage: React.RefObject +): [boolean, BookState | undefined] => { + const [state, setState] = useState(); + const [ready, setReady] = useState(false); + + useEffect(() => { + if (hash) + loadBookState( + hash, + (obj) => setState(obj), + () => setState({ currentPage: 0 }) + ); + }, [hash]); + + useEffect(() => { + console.log(Boolean(!ready && state?.currentPage && goToPage)); + if (!ready && state?.currentPage && pagesReady) { + console.log("Go to", state.currentPage); + goToPage(state.currentPage); + console.log("Ready"); + setReady(true); + } else if (hash && !ready && pagesReady && typeof state === "object") { + saveBookState(hash, { currentPage: 0 }); + setReady(true); + } + }, [ready, state, goToPage]); + + useEffect( + () => () => { + if (hash) { + console.log(currentPage); + if (ready && state) saveBookState(hash, state); + else saveBookState(hash, { currentPage: currentPage.current || 0 }); + } + }, + [] + ); + + return [ready, state]; +}; diff --git a/src/hooks/useLibrary.ts b/src/hooks/useLibrary.ts index db501b3..0c29713 100644 --- a/src/hooks/useLibrary.ts +++ b/src/hooks/useLibrary.ts @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { IBook } from "~/types/book"; +import { BookT } from "~/types/book"; import { getBookHT, getHashList, @@ -7,16 +7,16 @@ import { updateHashList, } from "~/utils/localStorage"; -export type AddBookFT = (book: IBook) => void; +export type AddBookFT = (book: BookT) => void; export type UseLibraryReturnTuple = [ - Record | null, + Record | null, AddBookFT, string[] ]; export const useLibrary = (): UseLibraryReturnTuple => { - const [library, setLibrary] = useState | null>(null); + const [library, setLibrary] = useState | null>(null); const [hashList, setHashList] = useState([]); const addBook: AddBookFT = (book) => { diff --git a/src/pages/BookView/index.tsx b/src/pages/BookView/index.tsx index d4e6c0b..65fb059 100644 --- a/src/pages/BookView/index.tsx +++ b/src/pages/BookView/index.tsx @@ -6,6 +6,7 @@ import styles from "./BookView.module.css"; import { BookListContext } from "~/context"; import { usePagination } from "~/hooks/usePagination"; import { IPageProps } from "~/types/page"; +import { useBookState } from "~/hooks/useBookState"; export const BookView = ({ setLoading, loading }: IPageProps) => { useEffect(() => setLoading(true), []); @@ -17,7 +18,7 @@ export const BookView = ({ setLoading, loading }: IPageProps) => { const pageContainerRef = useRef(null); const pageRef = useRef(null); - const [ready, goToPage, currentPage, pagesNumber] = usePagination( + const [pagesReady, goToPage, currentPage, pagesNumber] = usePagination( contentRef, pageContainerRef, pageRef, @@ -31,8 +32,15 @@ export const BookView = ({ setLoading, loading }: IPageProps) => { currentPageRef.current = currentPage; }, [currentPage]); + const [bookStateReady, bs] = useBookState( + pagesReady, + params?.hash, + goToPage, + currentPageRef + ); + useEffect(() => { - if (ready) { + if (bookStateReady) { setLoading(false); const handleKey = ({ key }: KeyboardEvent) => { @@ -48,7 +56,7 @@ export const BookView = ({ setLoading, loading }: IPageProps) => { window.addEventListener("keydown", handleKey); } - }, [ready]); + }, [bookStateReady]); const goPrev = () => goToPage(currentPage - 1); const goNext = () => goToPage(currentPage + 1); diff --git a/src/pages/Bookshelf/BookItem/index.tsx b/src/pages/Bookshelf/BookItem/index.tsx index f2cb3c4..bc672da 100644 --- a/src/pages/Bookshelf/BookItem/index.tsx +++ b/src/pages/Bookshelf/BookItem/index.tsx @@ -2,10 +2,10 @@ import React from "react"; import styles from "./BookItem.module.css"; -import { IBook } from "~/types/book"; +import { BookT } from "~/types/book"; import { Link } from "wouter"; -interface IBookItemProps extends IBook {} +interface IBookItemProps extends BookT {} export const BookItem = ({ author, title, cover, hash }: IBookItemProps) => { return ( diff --git a/src/types/book.ts b/src/types/book.ts index 3463afe..6f2f4e8 100644 --- a/src/types/book.ts +++ b/src/types/book.ts @@ -1,9 +1,13 @@ export const requiredBookProps = ["title", "author", "content"] as const; export const optionalBookProps = ["cover", "hash"] as const; -export type IBook = { +export type BookT = { [key in typeof requiredBookProps[number]]: string; } & { [key in typeof optionalBookProps[number]]: string | undefined; }; + +export type BookState = { + currentPage: number; +}; diff --git a/src/utils/api.ts b/src/utils/api.ts index b8bb602..13f6fde 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -1,4 +1,4 @@ -import { IBook, requiredBookProps } from "~/types/book"; +import { BookT, requiredBookProps } from "~/types/book"; import { API_URL } from "~/constants"; @@ -39,7 +39,7 @@ export const submitFile = async ( } }; -export const validateResponse = (content: unknown): content is IBook => { +export const validateResponse = (content: unknown): content is BookT => { if (content && typeof content === "object") for (const key of requiredBookProps) if (!(key in content)) { diff --git a/src/utils/localStorage.ts b/src/utils/localStorage.ts index 9f0e71e..5c3ef53 100644 --- a/src/utils/localStorage.ts +++ b/src/utils/localStorage.ts @@ -1,4 +1,4 @@ -import { IBook } from "~/types/book"; +import { BookState, BookT } from "~/types/book"; import { isArrOfStr } from "~/types/utils"; import { validateResponse } from "~/utils/api"; @@ -17,7 +17,7 @@ export const getHashList = () => { }; export const getBookHT = (hashList: string[]) => { - const bookHT: Record = {}; + const bookHT: Record = {}; hashList.forEach((hash) => { try { @@ -31,7 +31,7 @@ export const getBookHT = (hashList: string[]) => { return bookHT; }; -export const saveBook = (key: string, book: IBook) => +export const saveBook = (key: string, book: BookT) => localStorage.setItem(key, JSON.stringify(book)); export const updateHashList = (hashList: string[]) => @@ -78,3 +78,37 @@ export const savePages = ( width: number, pages: number[] ) => localStorage.setItem(hashStr(hash, height, width), JSON.stringify(pages)); + +export const validateBookState = (obj: unknown): obj is BookState => + Boolean( + obj && + typeof obj === "object" && + !Array.isArray(obj) && + "currentPage" in obj + ); + +export const loadBookState = ( + hash: string, + cb: (bookState: BookState) => void, + ecb: () => void +) => { + const str = localStorage.getItem(hash + "-state"); + + if (str) { + try { + const obj: unknown = JSON.parse(str); + + if (validateBookState(obj)) { + cb(obj); + return true; + } + } catch (e) { + console.error(e); + } + } + + ecb(); +}; + +export const saveBookState = (hash: string, state: BookState) => + localStorage.setItem(hash + "-state", JSON.stringify(state));