Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ form textarea {
word-break: break-word;
}

.appcss-pulse {
animation: loading 1.4s infinite;
}

@keyframes loading {
50% {
opacity: 0.5;
}

100% {
opacity: 1;
}
}

form > div {
width: 90%;
margin-left: auto;
Expand Down
11 changes: 5 additions & 6 deletions src/components/BooksBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { ReactElement } from 'react'
import { AuthContainer } from '../hooks/useAuth';

const BooksBody = ({ hook }: { hook: Record<string, any> }): ReactElement | null => {
const auth = AuthContainer.useContainer();
if (hook.booksLoading) return null;
const { isLoading, getRequest } = AuthContainer.useContainer();
if (isLoading && getRequest) return null;
return (
<div className={hook.books.length ? 'grid lg:grid-cols-3 md:grid-cols-2 gap-y-6 md:gap-6 my-6' : 'mt-4'}>
<div className="grid lg:grid-cols-3 md:grid-cols-2 gap-y-6 md:gap-6 my-6">
{
hook.books.length ? hook.books.map(((book: any) => {
hook.books.map(((book: any) => {
return (
<div key={book._id} className="grid grid-rows-1 shadow-sm">
<div className="bg-white rounded-tl rounded-tr p-4 border-x border-t border-gray-300">
Expand All @@ -33,8 +33,7 @@ const BooksBody = ({ hook }: { hook: Record<string, any> }): ReactElement | null
</div>
</div>
)
})) :
<div className={auth.isLoading ? 'hidden' : ''}>You have not added any book yet. Click on the add button to begin.</div>
}))
}
</div>
)
Expand Down
14 changes: 10 additions & 4 deletions src/components/Loader.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { ReactElement } from "react";
import { AuthContainer } from "../hooks/useAuth";
import { useLoader } from "../hooks/useLoader";
import SvgAnimatedLoader from "./ui/SvgAnimatedLoader";

const Loader = (): ReactElement | null => {
const auth = AuthContainer.useContainer();
if (!auth.isLoading) return null;
const Loader = ({ fixed }: { fixed: boolean }): ReactElement | null => {
const { isLoading } = AuthContainer.useContainer();
const { loader } = useLoader({ fixed });
if (!isLoading) return null;
return (
<div className="fixed bottom-6 right-6 text-pink-800 text-base py-2 px-3 md:text-lg md:px-4 rounded border border-pink-800 text-bold">Loading...</div>
<div className={`${loader.style} flex items-center w-fit`}>
<div className="text-base text-pink-800 w-fit">{loader.text}</div>
<SvgAnimatedLoader />
</div>
);
}

Expand Down
2 changes: 2 additions & 0 deletions src/components/forms/AuthForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Fragment, ReactElement, useEffect } from "react";
import { AuthContainer } from "../../hooks/useAuth";
import AppLink from "../AppLink";
import Error from "../Error";
import Loader from "../Loader";

const AuthForm = (): ReactElement => {
const auth = AuthContainer.useContainer();
Expand Down Expand Up @@ -64,6 +65,7 @@ const AuthForm = (): ReactElement => {
text={auth.authFormContent.LinkText}
/>
</div>
<Loader fixed={true} />
<Error />
</form>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/forms/BookForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const BookForm = ({ hook }: { hook: Record<string, any> }): ReactElement => {
</button>
</div>
)}
<Loader />
<Loader fixed={true} />
<Error />
</div>
</form>
Expand Down
36 changes: 36 additions & 0 deletions src/components/ui/Skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ReactElement } from "react";
import { AuthContainer } from "../../hooks/useAuth";

const Skeleton = (): ReactElement | null => {
const { isLoading, getRequest } = AuthContainer.useContainer();
if (isLoading && getRequest) {
return (
<div className="cursor-progress grid lg:grid-cols-3 md:grid-cols-2 gap-y-6 md:gap-6 my-6">
{
[...Array(6)].map(((book: any, index: number) => {
return (
<div key={index} className="grid grid-rows-1 shadow-sm">
<div className="bg-white rounded-tl rounded-tr p-4 border-x border-t border-gray-300">
<h3 className="appcss-pulse appcss-wordbreak font-medium leading-tight text-xl mt-0 mb-2 text-gray-300 bg-gray-300 rounded w-5/6 mb-2">Skeleton title</h3>
<p className="appcss-pulse appcss-wordbreak mb-1 text-sm text-gray-300 bg-gray-300 rounded w-3/4">Skeleton description text</p>
<p className="appcss-pulse underline decoration-gray-300 text-white bg-white rounded w-fit">Preview or download PDF</p>
</div>
<div className="flex">
<div className="flex justify-center flex-grow bg-white text-gray-300 p-2 border border-gray-300 active:shadow-lg mouse shadow transition ease-in duration-100">
<span className="appcss-pulse block text-sm text-gray-300 bg-gray-300 min-w-fit rounded">Skeleton</span>
</div>
<div className="flex justify-center flex-grow bg-gray-300 text-white p-2 border border-transparent active:shadow-lg mouse shadow transition ease-in duration-100">
<span className="appcss-pulse block text-sm text-white bg-white min-w-fit rounded">Skeleton</span>
</div>
</div>
</div>
)
}))
}
</div>
)
}
return null;
}

export default Skeleton;
62 changes: 62 additions & 0 deletions src/components/ui/SvgAnimatedLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { ReactElement } from "react";

const SvgAnimatedLoader = (): ReactElement => {
return (
<div className="w-8 h-8 w-fit ml-2">
<svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" style={{ margin: 'auto', background: 'none', display: 'block', shapeRendering: 'auto' }} width="100%" height="100%" viewBox="18 18 65 65" preserveAspectRatio="xMidYMid">
<g transform="rotate(0 50 50)">
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#9d174d">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.9166666666666666s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(30 50 50)">
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#9d174d">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.8333333333333334s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(60 50 50)">
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#9d174d">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.75s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(90 50 50)">
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#9d174d">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.6666666666666666s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(120 50 50)">
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#9d174d">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5833333333333334s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(150 50 50)">
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#9d174d">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(180 50 50)">
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#9d174d">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.4166666666666667s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(210 50 50)">
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#9d174d">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.3333333333333333s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(240 50 50)">
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#9d174d">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.25s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(270 50 50)">
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#9d174d">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.16666666666666666s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(300 50 50)">
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#9d174d">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.08333333333333333s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(330 50 50)">
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#9d174d">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animate>
</rect>
</g>
{/* <!-- [ldio] generated by https://loading.io/ --> */}
</svg>
</div>
);
}

export default SvgAnimatedLoader;
30 changes: 22 additions & 8 deletions src/hooks/useAuth.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChangeEvent, useState } from "react";
import { ChangeEvent, useEffect, useState } from "react";
import { Location, NavigateFunction, useLocation, useNavigate } from "react-router-dom";
import { createContainer } from "unstated-next";
import { AuthForm, AuthFormContent, User } from "../interfaces/auth";
Expand All @@ -10,6 +10,8 @@ export const useAuth = () => {
const pageRoute: string = location.pathname;
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isFetching, setIsFetching] = useState<boolean>(false);
const [getRequest, setGetRequest] = useState<boolean>(false);
const [error, setError] = useState<boolean>(false);
const [errorMessage, setErrorMessage] = useState<string>('');
const [unAuthorizedError, setUnAuthorizedError] = useState<boolean>(false);
Expand All @@ -36,8 +38,16 @@ export const useAuth = () => {
const [user, setUser] = useState<User>(initialUser);
const [form, setForm] = useState<AuthForm>(initialForm);

useEffect(() => {
const token = localStorage.getItem('buuks_accessToken');
token ? handleLogIn() : handleLogout();
isFetching ? setIsLoading(true) : setIsLoading(false);
if (getRequest) setIsLoading(true);
}, [isFetching, getRequest]);

const authenticateUser = async (e: any, apiEndpoint: string, destinationPage: string) => {
e.preventDefault();
setIsFetching(true);
const response = await fetch(`${process.env.REACT_APP_BASE_URL}/${apiEndpoint}`, {
method: 'POST',
headers: {
Expand All @@ -48,10 +58,9 @@ export const useAuth = () => {
const data = await response.json();
if (data.user) {
if (pageRoute === '/login') {
localStorage.setItem('accessToken', data.accessToken);
localStorage.setItem('_user', JSON.stringify(data.user));
setUser(JSON.parse(localStorage.getItem('_user') as string));
setIsAuthenticated(true);
localStorage.setItem('buuks_accessToken', data.accessToken);
localStorage.setItem('buuks_user', JSON.stringify(data.user));
handleLogIn();
}
navigate(destinationPage);
resetForm(initialForm);
Expand All @@ -66,6 +75,7 @@ export const useAuth = () => {
if (response.status === 401) {
handleError(true, data.error.message);
}
setIsFetching(false);
// console.log(data)
}

Expand All @@ -76,16 +86,17 @@ export const useAuth = () => {
}

const handleLogIn = () => {
const userLocal = localStorage.getItem('buuks_user');
setUser(JSON.parse(userLocal as string));
setIsAuthenticated(true);
localStorage.getItem('accessToken');
}

const handleLogout = () => {
setIsAuthenticated(false);
setUnAuthorizedError(false);
navigate('/login');
localStorage.removeItem('accessToken');
localStorage.removeItem('_user');
localStorage.removeItem('buuks_accessToken');
localStorage.removeItem('buuks_user');
setUser(initialUser);
}

Expand Down Expand Up @@ -143,9 +154,12 @@ export const useAuth = () => {
user,
setUser,
isLoading,
getRequest,
error,
errorMessage,
setIsLoading,
setIsFetching,
setGetRequest,
handleError,
unAuthorizedError,
setUnAuthorizedError,
Expand Down
Loading