Added global loading indicator

This commit is contained in:
Dmitriy Shishkov 2021-07-16 15:33:30 +05:00
parent 3677abb2a7
commit f47c150146
No known key found for this signature in database
GPG Key ID: 14358F96FCDD8060
11 changed files with 109 additions and 60 deletions

View File

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

View File

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

View File

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

View File

@ -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) => {

View File

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

View File

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

View File

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

View File

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

View File

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