From 5068d4952ee70fe0e58c1457465b67910fca0173 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Mon, 17 Jun 2024 21:30:58 +0300 Subject: [PATCH] Improve first loading behavior --- .../src/components/wrap/RequireAuth.tsx | 33 +-- rsconcept/frontend/src/pages/HomePage.tsx | 25 ++- rsconcept/frontend/src/pages/RegisterPage.tsx | 207 ------------------ .../src/pages/RegisterPage/FormSignup.tsx | 195 +++++++++++++++++ .../src/pages/RegisterPage/RegisterPage.tsx | 40 ++++ .../frontend/src/pages/RegisterPage/index.tsx | 1 + 6 files changed, 270 insertions(+), 231 deletions(-) delete mode 100644 rsconcept/frontend/src/pages/RegisterPage.tsx create mode 100644 rsconcept/frontend/src/pages/RegisterPage/FormSignup.tsx create mode 100644 rsconcept/frontend/src/pages/RegisterPage/RegisterPage.tsx create mode 100644 rsconcept/frontend/src/pages/RegisterPage/index.tsx diff --git a/rsconcept/frontend/src/components/wrap/RequireAuth.tsx b/rsconcept/frontend/src/components/wrap/RequireAuth.tsx index 7cf30688..bd5f1bcd 100644 --- a/rsconcept/frontend/src/components/wrap/RequireAuth.tsx +++ b/rsconcept/frontend/src/components/wrap/RequireAuth.tsx @@ -1,27 +1,34 @@ 'use client'; +import { AnimatePresence } from 'framer-motion'; + import { useAuth } from '@/context/AuthContext'; +import Loader from '../ui/Loader'; import TextURL from '../ui/TextURL'; +import AnimateFade from './AnimateFade'; interface RequireAuthProps { children: React.ReactNode; } function RequireAuth({ children }: RequireAuthProps) { - const { user } = useAuth(); - if (user) { - return children; - } else { - return ( -
-

Пожалуйста войдите в систему

- - - -
- ); - } + const { user, loading } = useAuth(); + + return ( + + {loading ? : null} + {!loading && user ? {children} : null} + {!loading && !user ? ( + +

Пожалуйста войдите в систему

+ + + +
+ ) : null} +
+ ); } export default RequireAuth; diff --git a/rsconcept/frontend/src/pages/HomePage.tsx b/rsconcept/frontend/src/pages/HomePage.tsx index a0272dbe..f02778e3 100644 --- a/rsconcept/frontend/src/pages/HomePage.tsx +++ b/rsconcept/frontend/src/pages/HomePage.tsx @@ -1,27 +1,30 @@ import { useLayoutEffect } from 'react'; import { urls } from '@/app/urls'; +import Loader from '@/components/ui/Loader'; import { useAuth } from '@/context/AuthContext'; import { useConceptNavigation } from '@/context/NavigationContext'; import { PARAMETER } from '@/utils/constants'; function HomePage() { const router = useConceptNavigation(); - const { user } = useAuth(); + const { user, loading } = useAuth(); useLayoutEffect(() => { - if (!user) { - setTimeout(() => { - router.push(urls.manuals); - }, PARAMETER.refreshTimeout); - } else { - setTimeout(() => { - router.push(urls.library); - }, PARAMETER.refreshTimeout); + if (!loading) { + if (!user) { + setTimeout(() => { + router.push(urls.manuals); + }, PARAMETER.refreshTimeout); + } else { + setTimeout(() => { + router.push(urls.library); + }, PARAMETER.refreshTimeout); + } } - }, [router, user]); + }, [router, user, loading]); - return
; + return ; } export default HomePage; diff --git a/rsconcept/frontend/src/pages/RegisterPage.tsx b/rsconcept/frontend/src/pages/RegisterPage.tsx deleted file mode 100644 index adb5bfa2..00000000 --- a/rsconcept/frontend/src/pages/RegisterPage.tsx +++ /dev/null @@ -1,207 +0,0 @@ -'use client'; - -import axios from 'axios'; -import clsx from 'clsx'; -import { useEffect, useMemo, useState } from 'react'; -import { toast } from 'react-toastify'; - -import { urls } from '@/app/urls'; -import { IconHelp } from '@/components/Icons'; -import InfoError, { ErrorData } from '@/components/info/InfoError'; -import Button from '@/components/ui/Button'; -import Checkbox from '@/components/ui/Checkbox'; -import FlexColumn from '@/components/ui/FlexColumn'; -import Overlay from '@/components/ui/Overlay'; -import PrettyJson from '@/components/ui/PrettyJSON'; -import SubmitButton from '@/components/ui/SubmitButton'; -import TextInput from '@/components/ui/TextInput'; -import TextURL from '@/components/ui/TextURL'; -import Tooltip from '@/components/ui/Tooltip'; -import AnimateFade from '@/components/wrap/AnimateFade'; -import ExpectedAnonymous from '@/components/wrap/ExpectedAnonymous'; -import { useAuth } from '@/context/AuthContext'; -import { useConceptNavigation } from '@/context/NavigationContext'; -import { HelpTopic } from '@/models/miscellaneous'; -import { IUserSignupData } from '@/models/user'; -import { globals, patterns } from '@/utils/constants'; - -function RegisterPage() { - const router = useConceptNavigation(); - const { user, signup, loading, error, setError } = useAuth(); - - const [username, setUsername] = useState(''); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [password2, setPassword2] = useState(''); - const [firstName, setFirstName] = useState(''); - const [lastName, setLastName] = useState(''); - - const [acceptPrivacy, setAcceptPrivacy] = useState(false); - const [acceptRules, setAcceptRules] = useState(false); - - const isValid = useMemo( - () => acceptPrivacy && acceptRules && !!email && !!username, - [acceptPrivacy, acceptRules, email, username] - ); - - useEffect(() => { - setError(undefined); - }, [username, email, password, password2, setError]); - - function handleCancel() { - if (router.canBack()) { - router.back(); - } else { - router.push(urls.library); - } - } - - function handleSubmit(event: React.FormEvent) { - event.preventDefault(); - if (!loading) { - const data: IUserSignupData = { - username, - email, - password, - password2, - first_name: firstName, - last_name: lastName - }; - signup(data, createdUser => { - router.push(urls.login_hint(createdUser.username)); - toast.success(`Пользователь успешно создан: ${createdUser.username}`); - }); - } - } - - if (user) { - return ( - - - - ); - } - return ( - -
-

- Новый пользователь - - - - - используйте уникальный пароль для каждого сайта - - - - - - электронная почта используется для восстановления пароля - -

-
- - setUsername(event.target.value)} - /> - setPassword(event.target.value)} - /> - setPassword2(event.target.value)} - /> - - - - setEmail(event.target.value)} - /> - setFirstName(event.target.value)} - /> - setLastName(event.target.value)} - /> - -
- -
- - -
-
- - -
- -
- -
- {error ? : null} - -
- ); -} - -export default RegisterPage; - -// ====== Internals ========= -function ProcessError({ error }: { error: ErrorData }): React.ReactElement { - if (axios.isAxiosError(error) && error.response && error.response.status === 400) { - if ('email' in error.response.data) { - return ( - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -
{error.response.data.email}.
- ); - } else if ('username' in error.response.data) { - return ( - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -
{error.response.data.username}.
- ); - } else { - return ( -
- -
- ); - } - } - return ; -} diff --git a/rsconcept/frontend/src/pages/RegisterPage/FormSignup.tsx b/rsconcept/frontend/src/pages/RegisterPage/FormSignup.tsx new file mode 100644 index 00000000..91afc60a --- /dev/null +++ b/rsconcept/frontend/src/pages/RegisterPage/FormSignup.tsx @@ -0,0 +1,195 @@ +'use client'; + +import axios from 'axios'; +import clsx from 'clsx'; +import { useEffect, useMemo, useState } from 'react'; +import { toast } from 'react-toastify'; + +import { urls } from '@/app/urls'; +import { IconHelp } from '@/components/Icons'; +import InfoError, { ErrorData } from '@/components/info/InfoError'; +import Button from '@/components/ui/Button'; +import Checkbox from '@/components/ui/Checkbox'; +import FlexColumn from '@/components/ui/FlexColumn'; +import Overlay from '@/components/ui/Overlay'; +import PrettyJson from '@/components/ui/PrettyJSON'; +import SubmitButton from '@/components/ui/SubmitButton'; +import TextInput from '@/components/ui/TextInput'; +import TextURL from '@/components/ui/TextURL'; +import Tooltip from '@/components/ui/Tooltip'; +import { useAuth } from '@/context/AuthContext'; +import { useConceptNavigation } from '@/context/NavigationContext'; +import { HelpTopic } from '@/models/miscellaneous'; +import { IUserSignupData } from '@/models/user'; +import { globals, patterns } from '@/utils/constants'; + +function FormSignup() { + const router = useConceptNavigation(); + const { signup, loading, error, setError } = useAuth(); + + const [username, setUsername] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [password2, setPassword2] = useState(''); + const [firstName, setFirstName] = useState(''); + const [lastName, setLastName] = useState(''); + + const [acceptPrivacy, setAcceptPrivacy] = useState(false); + const [acceptRules, setAcceptRules] = useState(false); + + const isValid = useMemo( + () => acceptPrivacy && acceptRules && !!email && !!username, + [acceptPrivacy, acceptRules, email, username] + ); + + useEffect(() => { + setError(undefined); + }, [username, email, password, password2, setError]); + + function handleCancel() { + if (router.canBack()) { + router.back(); + } else { + router.push(urls.library); + } + } + + function handleSubmit(event: React.FormEvent) { + event.preventDefault(); + if (!loading) { + const data: IUserSignupData = { + username, + email, + password, + password2, + first_name: firstName, + last_name: lastName + }; + signup(data, createdUser => { + router.push(urls.login_hint(createdUser.username)); + toast.success(`Пользователь успешно создан: ${createdUser.username}`); + }); + } + } + return ( +
+

+ Новый пользователь + + + + + используйте уникальный пароль для каждого сайта + + + + + + электронная почта используется для восстановления пароля + +

+
+ + setUsername(event.target.value)} + /> + setPassword(event.target.value)} + /> + setPassword2(event.target.value)} + /> + + + + setEmail(event.target.value)} + /> + setFirstName(event.target.value)} + /> + setLastName(event.target.value)} + /> + +
+ +
+ + +
+
+ + +
+ +
+ +
+ {error ? : null} + + ); +} + +export default FormSignup; + +// ====== Internals ========= +function ProcessError({ error }: { error: ErrorData }): React.ReactElement { + if (axios.isAxiosError(error) && error.response && error.response.status === 400) { + if ('email' in error.response.data) { + return ( + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access +
{error.response.data.email}.
+ ); + } else if ('username' in error.response.data) { + return ( + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access +
{error.response.data.username}.
+ ); + } else { + return ( +
+ +
+ ); + } + } + return ; +} diff --git a/rsconcept/frontend/src/pages/RegisterPage/RegisterPage.tsx b/rsconcept/frontend/src/pages/RegisterPage/RegisterPage.tsx new file mode 100644 index 00000000..f57ec233 --- /dev/null +++ b/rsconcept/frontend/src/pages/RegisterPage/RegisterPage.tsx @@ -0,0 +1,40 @@ +import { AnimatePresence } from 'framer-motion'; + +import Loader from '@/components/ui/Loader'; +import AnimateFade from '@/components/wrap/AnimateFade'; +import ExpectedAnonymous from '@/components/wrap/ExpectedAnonymous'; +import { useAuth } from '@/context/AuthContext'; + +import FormSignup from './FormSignup'; + +function RegisterPage() { + const { user, loading } = useAuth(); + + if (loading) { + return ; + } + if (user && !loading) { + return ( + + + + ); + } + return ( + + {loading ? : null} + {!loading && user ? ( + + + + ) : null} + {!loading && !user ? ( + + + + ) : null} + + ); +} + +export default RegisterPage; diff --git a/rsconcept/frontend/src/pages/RegisterPage/index.tsx b/rsconcept/frontend/src/pages/RegisterPage/index.tsx new file mode 100644 index 00000000..1b9629b7 --- /dev/null +++ b/rsconcept/frontend/src/pages/RegisterPage/index.tsx @@ -0,0 +1 @@ +export { default } from './RegisterPage';