Added global loading indicator
This commit is contained in:
parent
3677abb2a7
commit
f47c150146
@ -1,6 +1,6 @@
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "SNOWPACK_PUBLIC_API_URL=http://localhost:5000 SNOWPACK_PUBLIC_BASE_URL=http://localhost:8080 snowpack dev",
|
||||
"dev": "SNOWPACK_PUBLIC_API_URL=https://publitebackend.dmitriy.icu SNOWPACK_PUBLIC_BASE_URL=http://localhost:8080 snowpack dev",
|
||||
"build": "snowpack build",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
|
@ -3,3 +3,15 @@
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.loadingIndicator {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
@ -1,23 +1,42 @@
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import { Route, Switch } from "wouter";
|
||||
import { BookListContextProvider } from "~/context";
|
||||
|
||||
import { Bookshelf } from "~/pages/Bookshelf";
|
||||
import { BookView } from "~/pages/BookView";
|
||||
import { UploadForm } from "~/pages/UploadForm";
|
||||
import { Dots } from "~/utils/Dots";
|
||||
|
||||
import styles from "./App.module.css";
|
||||
|
||||
export const App = () => (
|
||||
<div className={styles.container}>
|
||||
<BookListContextProvider>
|
||||
<Switch>
|
||||
<Route path="/upload" component={UploadForm} />
|
||||
<Route path="/" component={Bookshelf} />
|
||||
<Route path="/:hash" component={BookView} />
|
||||
</Switch>
|
||||
</BookListContextProvider>
|
||||
</div>
|
||||
);
|
||||
export const App = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{loading && (
|
||||
<div className={styles.loadingIndicator}>
|
||||
<h1>
|
||||
Loading
|
||||
<Dots />
|
||||
</h1>
|
||||
</div>
|
||||
)}
|
||||
<BookListContextProvider>
|
||||
<Switch>
|
||||
<Route path="/upload">
|
||||
<UploadForm setLoading={setLoading} loading={loading} />
|
||||
</Route>
|
||||
<Route path="/">
|
||||
<Bookshelf setLoading={setLoading} loading={loading} />
|
||||
</Route>
|
||||
<Route path="/:hash">
|
||||
<BookView setLoading={setLoading} loading={loading} />
|
||||
</Route>
|
||||
</Switch>
|
||||
</BookListContextProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
@ -10,13 +10,13 @@ import {
|
||||
export type AddBookFT = (book: IBook) => void;
|
||||
|
||||
export type UseLibraryReturnTuple = [
|
||||
Record<string, IBook>,
|
||||
Record<string, IBook> | null,
|
||||
AddBookFT,
|
||||
string[]
|
||||
];
|
||||
|
||||
export const useLibrary = (): UseLibraryReturnTuple => {
|
||||
const [library, setLibrary] = useState<Record<string, IBook>>({});
|
||||
const [library, setLibrary] = useState<Record<string, IBook> | null>(null);
|
||||
const [hashList, setHashList] = useState<string[]>([]);
|
||||
|
||||
const addBook: AddBookFT = (book) => {
|
||||
|
@ -25,7 +25,7 @@ export const usePagination = (
|
||||
const [pages, setPages] = useState<number[]>([]);
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
|
||||
const computeStartPositionsOfElements = (root: HTMLDivElement) => {
|
||||
const computeStartPositionsOfElements = async (root: HTMLDivElement) => {
|
||||
const positionToElement: PositionElement[] = [];
|
||||
const idPositions: IdPositions = {};
|
||||
|
||||
@ -56,7 +56,7 @@ export const usePagination = (
|
||||
setIdPositions(idPositions);
|
||||
};
|
||||
|
||||
const findPages = (page: HTMLElement, pageContainer: HTMLElement) => {
|
||||
const findPages = async (page: HTMLElement, pageContainer: HTMLElement) => {
|
||||
const pages = [];
|
||||
pages.push(0);
|
||||
let jump = 100;
|
||||
|
@ -17,16 +17,4 @@
|
||||
img {
|
||||
max-height: 98vh;
|
||||
max-width: 80vw;
|
||||
}
|
||||
|
||||
.loadingIndicator {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
@ -5,9 +5,12 @@ import styles from "./BookView.module.css";
|
||||
|
||||
import { BookListContext } from "~/context";
|
||||
import { usePagination } from "~/hooks/usePagination";
|
||||
import { IPageProps } from "~/types/page";
|
||||
|
||||
export const BookView = () => {
|
||||
const [match, params] = useRoute("/:hash");
|
||||
export const BookView = ({ setLoading, loading }: IPageProps) => {
|
||||
useEffect(() => setLoading(true), []);
|
||||
|
||||
const [_, params] = useRoute("/:hash");
|
||||
const [books] = useContext(BookListContext);
|
||||
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
@ -18,7 +21,7 @@ export const BookView = () => {
|
||||
contentRef,
|
||||
pageContainerRef,
|
||||
pageRef,
|
||||
params?.hash ? books[params.hash]?.content : undefined
|
||||
params?.hash && books && loading ? books[params.hash]?.content : undefined
|
||||
);
|
||||
|
||||
const currentPageRef = useRef(currentPage);
|
||||
@ -29,6 +32,8 @@ export const BookView = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (ready) {
|
||||
setLoading(false);
|
||||
|
||||
const handleKey = ({ key }: KeyboardEvent) => {
|
||||
switch (key) {
|
||||
case "ArrowLeft":
|
||||
@ -44,19 +49,16 @@ export const BookView = () => {
|
||||
}
|
||||
}, [ready]);
|
||||
|
||||
if (params?.hash && params.hash in books)
|
||||
return (
|
||||
<>
|
||||
{!ready && (
|
||||
<div className={styles.loadingIndicator}>
|
||||
<h1>Loading</h1>
|
||||
if (books) {
|
||||
if (params?.hash && params.hash in books)
|
||||
return (
|
||||
<>
|
||||
<div className={styles.content} ref={contentRef} />
|
||||
<div className={styles.pageContainer} ref={pageContainerRef}>
|
||||
<div ref={pageRef} />
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.content} ref={contentRef} />
|
||||
<div className={styles.pageContainer} ref={pageContainerRef}>
|
||||
<div ref={pageRef} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
return <Redirect to="/" />;
|
||||
</>
|
||||
);
|
||||
return <Redirect to="/" />;
|
||||
} else return <></>;
|
||||
};
|
||||
|
@ -5,18 +5,27 @@ import styles from "./Bookshelf.module.css";
|
||||
import { BookItem } from "./BookItem";
|
||||
import { AddBook } from "./AddBook";
|
||||
import { BookListContext } from "~/context";
|
||||
import { IPageProps } from "~/types/page";
|
||||
|
||||
export const Bookshelf = ({ setLoading }: IPageProps) => {
|
||||
useEffect(() => setLoading(true), []);
|
||||
|
||||
export const Bookshelf = () => {
|
||||
const [books] = useContext(BookListContext);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.scrollContainer}>
|
||||
<AddBook />
|
||||
{Object.values(books).map((book, index) => (
|
||||
<BookItem key={book.hash} {...book} />
|
||||
))}
|
||||
useEffect(() => {
|
||||
if (books) setLoading(false);
|
||||
}, [books]);
|
||||
|
||||
if (books)
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.scrollContainer}>
|
||||
<AddBook />
|
||||
{Object.values(books).map((book, index) => (
|
||||
<BookItem key={book.hash} {...book} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
else return <></>;
|
||||
};
|
||||
|
@ -1,14 +1,14 @@
|
||||
import React, { useContext, useState } from "react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { useLocation } from "wouter";
|
||||
|
||||
import plusIcon from "~/assets/plus.svg";
|
||||
import styles from "./UploadForm.module.css";
|
||||
import { submitFile, validateResponse, validState } from "~/utils/api";
|
||||
import { BookListContext } from "~/context";
|
||||
import { IPageProps } from "~/types/page";
|
||||
|
||||
export const UploadForm = () => {
|
||||
export const UploadForm = ({ setLoading }: IPageProps) => {
|
||||
const [error, setError] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [_, setLocation] = useLocation();
|
||||
|
||||
const [__, saveBook] = useContext(BookListContext);
|
||||
|
6
src/types/page.ts
Normal file
6
src/types/page.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
export interface IPageProps {
|
||||
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
loading: boolean;
|
||||
}
|
13
src/utils/Dots.tsx
Normal file
13
src/utils/Dots.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
export const Dots = () => {
|
||||
const [n, setN] = useState(3);
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => setN((n + 1) % 4), 500);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [n]);
|
||||
|
||||
return <span>{".".repeat(n)}</span>;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user