Added pages persistance (ugly, but temporary)
This commit is contained in:
parent
e13f37923d
commit
b7f5a0eb55
@ -1,10 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useLibrary, UseLibraryReturnTuple } from "./hooks/useLibrary";
|
import { useLibrary, UseLibraryReturnTuple } from "./hooks/useLibrary";
|
||||||
import { IBook } from "./types/book";
|
import { BookT } from "./types/book";
|
||||||
|
|
||||||
export const BookListContext = React.createContext<UseLibraryReturnTuple>([
|
export const BookListContext = React.createContext<UseLibraryReturnTuple>([
|
||||||
{},
|
{},
|
||||||
(book: IBook) => {},
|
(book: BookT) => {},
|
||||||
[],
|
[],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
48
src/hooks/useBookState.ts
Normal file
48
src/hooks/useBookState.ts
Normal file
@ -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<number>
|
||||||
|
): [boolean, BookState | undefined] => {
|
||||||
|
const [state, setState] = useState<BookState>();
|
||||||
|
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];
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { IBook } from "~/types/book";
|
import { BookT } from "~/types/book";
|
||||||
import {
|
import {
|
||||||
getBookHT,
|
getBookHT,
|
||||||
getHashList,
|
getHashList,
|
||||||
@ -7,16 +7,16 @@ import {
|
|||||||
updateHashList,
|
updateHashList,
|
||||||
} from "~/utils/localStorage";
|
} from "~/utils/localStorage";
|
||||||
|
|
||||||
export type AddBookFT = (book: IBook) => void;
|
export type AddBookFT = (book: BookT) => void;
|
||||||
|
|
||||||
export type UseLibraryReturnTuple = [
|
export type UseLibraryReturnTuple = [
|
||||||
Record<string, IBook> | null,
|
Record<string, BookT> | null,
|
||||||
AddBookFT,
|
AddBookFT,
|
||||||
string[]
|
string[]
|
||||||
];
|
];
|
||||||
|
|
||||||
export const useLibrary = (): UseLibraryReturnTuple => {
|
export const useLibrary = (): UseLibraryReturnTuple => {
|
||||||
const [library, setLibrary] = useState<Record<string, IBook> | null>(null);
|
const [library, setLibrary] = useState<Record<string, BookT> | null>(null);
|
||||||
const [hashList, setHashList] = useState<string[]>([]);
|
const [hashList, setHashList] = useState<string[]>([]);
|
||||||
|
|
||||||
const addBook: AddBookFT = (book) => {
|
const addBook: AddBookFT = (book) => {
|
||||||
|
@ -6,6 +6,7 @@ import styles from "./BookView.module.css";
|
|||||||
import { BookListContext } from "~/context";
|
import { BookListContext } from "~/context";
|
||||||
import { usePagination } from "~/hooks/usePagination";
|
import { usePagination } from "~/hooks/usePagination";
|
||||||
import { IPageProps } from "~/types/page";
|
import { IPageProps } from "~/types/page";
|
||||||
|
import { useBookState } from "~/hooks/useBookState";
|
||||||
|
|
||||||
export const BookView = ({ setLoading, loading }: IPageProps) => {
|
export const BookView = ({ setLoading, loading }: IPageProps) => {
|
||||||
useEffect(() => setLoading(true), []);
|
useEffect(() => setLoading(true), []);
|
||||||
@ -17,7 +18,7 @@ export const BookView = ({ setLoading, loading }: IPageProps) => {
|
|||||||
const pageContainerRef = useRef<HTMLDivElement>(null);
|
const pageContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const pageRef = useRef<HTMLDivElement>(null);
|
const pageRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const [ready, goToPage, currentPage, pagesNumber] = usePagination(
|
const [pagesReady, goToPage, currentPage, pagesNumber] = usePagination(
|
||||||
contentRef,
|
contentRef,
|
||||||
pageContainerRef,
|
pageContainerRef,
|
||||||
pageRef,
|
pageRef,
|
||||||
@ -31,8 +32,15 @@ export const BookView = ({ setLoading, loading }: IPageProps) => {
|
|||||||
currentPageRef.current = currentPage;
|
currentPageRef.current = currentPage;
|
||||||
}, [currentPage]);
|
}, [currentPage]);
|
||||||
|
|
||||||
|
const [bookStateReady, bs] = useBookState(
|
||||||
|
pagesReady,
|
||||||
|
params?.hash,
|
||||||
|
goToPage,
|
||||||
|
currentPageRef
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ready) {
|
if (bookStateReady) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
const handleKey = ({ key }: KeyboardEvent) => {
|
const handleKey = ({ key }: KeyboardEvent) => {
|
||||||
@ -48,7 +56,7 @@ export const BookView = ({ setLoading, loading }: IPageProps) => {
|
|||||||
|
|
||||||
window.addEventListener("keydown", handleKey);
|
window.addEventListener("keydown", handleKey);
|
||||||
}
|
}
|
||||||
}, [ready]);
|
}, [bookStateReady]);
|
||||||
|
|
||||||
const goPrev = () => goToPage(currentPage - 1);
|
const goPrev = () => goToPage(currentPage - 1);
|
||||||
const goNext = () => goToPage(currentPage + 1);
|
const goNext = () => goToPage(currentPage + 1);
|
||||||
|
@ -2,10 +2,10 @@ import React from "react";
|
|||||||
|
|
||||||
import styles from "./BookItem.module.css";
|
import styles from "./BookItem.module.css";
|
||||||
|
|
||||||
import { IBook } from "~/types/book";
|
import { BookT } from "~/types/book";
|
||||||
import { Link } from "wouter";
|
import { Link } from "wouter";
|
||||||
|
|
||||||
interface IBookItemProps extends IBook {}
|
interface IBookItemProps extends BookT {}
|
||||||
|
|
||||||
export const BookItem = ({ author, title, cover, hash }: IBookItemProps) => {
|
export const BookItem = ({ author, title, cover, hash }: IBookItemProps) => {
|
||||||
return (
|
return (
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
export const requiredBookProps = ["title", "author", "content"] as const;
|
export const requiredBookProps = ["title", "author", "content"] as const;
|
||||||
export const optionalBookProps = ["cover", "hash"] as const;
|
export const optionalBookProps = ["cover", "hash"] as const;
|
||||||
|
|
||||||
export type IBook = {
|
export type BookT = {
|
||||||
[key in typeof requiredBookProps[number]]: string;
|
[key in typeof requiredBookProps[number]]: string;
|
||||||
} &
|
} &
|
||||||
{
|
{
|
||||||
[key in typeof optionalBookProps[number]]: string | undefined;
|
[key in typeof optionalBookProps[number]]: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type BookState = {
|
||||||
|
currentPage: number;
|
||||||
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { IBook, requiredBookProps } from "~/types/book";
|
import { BookT, requiredBookProps } from "~/types/book";
|
||||||
|
|
||||||
import { API_URL } from "~/constants";
|
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")
|
if (content && typeof content === "object")
|
||||||
for (const key of requiredBookProps)
|
for (const key of requiredBookProps)
|
||||||
if (!(key in content)) {
|
if (!(key in content)) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { IBook } from "~/types/book";
|
import { BookState, BookT } from "~/types/book";
|
||||||
import { isArrOfStr } from "~/types/utils";
|
import { isArrOfStr } from "~/types/utils";
|
||||||
import { validateResponse } from "~/utils/api";
|
import { validateResponse } from "~/utils/api";
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ export const getHashList = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getBookHT = (hashList: string[]) => {
|
export const getBookHT = (hashList: string[]) => {
|
||||||
const bookHT: Record<string, IBook> = {};
|
const bookHT: Record<string, BookT> = {};
|
||||||
|
|
||||||
hashList.forEach((hash) => {
|
hashList.forEach((hash) => {
|
||||||
try {
|
try {
|
||||||
@ -31,7 +31,7 @@ export const getBookHT = (hashList: string[]) => {
|
|||||||
return bookHT;
|
return bookHT;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const saveBook = (key: string, book: IBook) =>
|
export const saveBook = (key: string, book: BookT) =>
|
||||||
localStorage.setItem(key, JSON.stringify(book));
|
localStorage.setItem(key, JSON.stringify(book));
|
||||||
|
|
||||||
export const updateHashList = (hashList: string[]) =>
|
export const updateHashList = (hashList: string[]) =>
|
||||||
@ -78,3 +78,37 @@ export const savePages = (
|
|||||||
width: number,
|
width: number,
|
||||||
pages: number[]
|
pages: number[]
|
||||||
) => localStorage.setItem(hashStr(hash, height, width), JSON.stringify(pages));
|
) => 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));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user