development; fixes; added basic loading screen

This commit is contained in:
Myzel394 2022-10-14 16:41:14 +02:00
parent 69dadf1122
commit f35a07efc1
14 changed files with 134 additions and 39 deletions

View File

@ -17,6 +17,7 @@
"axios": "^1.1.2", "axios": "^1.1.2",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"formik": "^2.2.9", "formik": "^2.2.9",
"in-seconds": "^1.2.0",
"openpgp": "^5.5.0", "openpgp": "^5.5.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

View File

@ -2,8 +2,11 @@ import {RouterProvider, createBrowserRouter} from "react-router-dom"
import React, {ReactElement} from "react" import React, {ReactElement} from "react"
import {QueryClientProvider} from "@tanstack/react-query" import {QueryClientProvider} from "@tanstack/react-query"
import {CssBaseline, ThemeProvider} from "@mui/material"
import {queryClient} from "~/constants/react-query" import {queryClient} from "~/constants/react-query"
import {lightTheme} from "~/constants/themes"
import LoadCriticalContent from "~/LoadCriticalContent"
import RootRoute from "~/routes/Root" import RootRoute from "~/routes/Root"
const router = createBrowserRouter([ const router = createBrowserRouter([
@ -17,7 +20,12 @@ export default function App(): ReactElement {
return ( return (
<React.StrictMode> <React.StrictMode>
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<ThemeProvider theme={lightTheme}>
<CssBaseline />
<LoadCriticalContent>
<RouterProvider router={router} /> <RouterProvider router={router} />
</LoadCriticalContent>
</ThemeProvider>
</QueryClientProvider> </QueryClientProvider>
</React.StrictMode> </React.StrictMode>
) )

View File

@ -0,0 +1,44 @@
import * as inSeconds from "in-seconds"
import {AxiosError} from "axios"
import React, {ReactElement} from "react"
import {useQuery} from "@tanstack/react-query"
import {getServerSettings} from "~/apis"
import LoadingScreen from "~/LoadingScreen"
import ServerSettingsContext, {
ServerSettingsContextType,
} from "./ServerSettingsContext"
export interface LoadCriticalContentProps {
children: ReactElement
}
export default function LoadCriticalContent({
children,
}: LoadCriticalContentProps): ReactElement {
const {data} = useQuery<ServerSettingsContextType, AxiosError>(
["server-settings"],
async () => {
const settings = await getServerSettings()
return {
serverSettings: settings,
}
},
{
staleTime: inSeconds.days(20),
},
)
if (!data) {
return <LoadingScreen />
}
return (
<ServerSettingsContext.Provider value={data}>
{children}
</ServerSettingsContext.Provider>
)
}

View File

@ -0,0 +1,12 @@
import {createContext} from "react"
import {ServerSettings} from "~/types"
export interface ServerSettingsContextType {
serverSettings: ServerSettings
}
// @ts-ignore
const ServerSettingsContext = createContext<ServerSettingsContextType>()
export default ServerSettingsContext

View File

@ -3,7 +3,6 @@ import axios from "axios"
import {ServerSettings} from "~/types" import {ServerSettings} from "~/types"
export default async function getServerSettings(): Promise<ServerSettings> { export default async function getServerSettings(): Promise<ServerSettings> {
return ( return (await axios.get(`${import.meta.env.VITE_SERVER_BASE_URL}/settings`))
await axios.get(`${process.env.NEXT_PUBLIC_SERVER_BASE_URL}/settings`) .data
).data
} }

View File

@ -6,7 +6,7 @@ export interface SignupResult {
export default async function signup(email: string): Promise<SignupResult> { export default async function signup(email: string): Promise<SignupResult> {
const {data} = await axios.post( const {data} = await axios.post(
`${process.env.NEXT_PUBLIC_SERVER_BASE_URL}/auth/signup`, `${import.meta.env.VITE_SERVER_BASE_URL}/auth/signup`,
{ {
email, email,
}, },

View File

@ -12,7 +12,7 @@ export default async function validateToken({
token, token,
}: ValidateTokenData): Promise<AuthenticationDetails> { }: ValidateTokenData): Promise<AuthenticationDetails> {
const {data} = await axios.post( const {data} = await axios.post(
`${process.env.NEXT_PUBLIC_SERVER_BASE_URL}/auth/verify-email`, `${import.meta.env.VITE_SERVER_BASE_URL}/auth/verify-email`,
{ {
email: email, email: email,
token: token, token: token,

View File

@ -1,27 +1,23 @@
import React, { import React, {ReactElement, useEffect, useRef, useState} from "react"
ReactElement,
forwardRef,
useEffect,
useRef,
useState,
} from "react"
import {Paper} from "@mui/material" import {Paper} from "@mui/material"
import {whenElementHasBounds} from "~/utils" import {whenElementHasBounds} from "~/utils"
export interface MultiStepFormProps { export interface MultiStepFormProps {
steps: (() => ReactElement)[] steps: ReactElement[]
index: number index: number
duration?: number duration?: number
easing?: string easing?: string
} }
function MultiStepForm( function MultiStepForm({
{steps, index, duration = 900, easing = "ease-in-out"}: MultiStepFormProps, steps,
ref: any, index,
): ReactElement { duration = 900,
easing = "ease-in-out",
}: MultiStepFormProps): ReactElement {
const $currentElement = useRef<HTMLDivElement>(null) const $currentElement = useRef<HTMLDivElement>(null)
const $timeout = useRef<any>() const $timeout = useRef<any>()
@ -44,6 +40,11 @@ function MultiStepForm(
return $timeout.current?.cancel! return $timeout.current?.cancel!
}, [index, currentIndex]) }, [index, currentIndex])
const hasSize = Boolean(
(currentSize?.width || nextSize?.width) &&
(currentSize?.height || nextSize?.height),
)
return ( return (
<div <div
style={{ style={{
@ -52,9 +53,13 @@ function MultiStepForm(
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
width: Math.max(currentSize?.width, nextSize?.width), width:
height: Math.max(currentSize?.height, nextSize?.height), Math.max(currentSize?.width || 0, nextSize?.width || 0) ||
overflow: "hidden", "100%",
height:
Math.max(currentSize?.height || 0, nextSize?.height || 0) ||
"100%",
overflow: hasSize ? "hidden" : "visible",
}} }}
> >
<Paper <Paper
@ -122,7 +127,7 @@ function MultiStepForm(
transform: isTransitioning ? "translateX(-100%)" : "", transform: isTransitioning ? "translateX(-100%)" : "",
}} }}
> >
{steps[currentIndex]()} {steps[currentIndex]}
</div> </div>
<div <div
// @ts-ignore // @ts-ignore
@ -151,10 +156,10 @@ function MultiStepForm(
> >
{currentIndex === steps.length - 1 {currentIndex === steps.length - 1
? null ? null
: steps[currentIndex + 1]()} : steps[currentIndex + 1]}
</div> </div>
</div> </div>
) )
} }
export default forwardRef(MultiStepForm) export default MultiStepForm

11
src/constants/themes.ts Normal file
View File

@ -0,0 +1,11 @@
import {createTheme} from "@mui/material"
export const lightTheme = createTheme({
palette: {
mode: "dark",
secondary: {
main: "#ACF",
contrastText: "#FFF",
},
},
})

2
src/hooks/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from "./use-server-settings"
export {default as useServerSettings} from "./use-server-settings"

View File

@ -0,0 +1,10 @@
import {useContext} from "react"
import {ServerSettings} from "~/types"
import ServerSettingsContext from "~/ServerSettingsContext"
export default function useServerSettings(): ServerSettings {
const {serverSettings} = useContext(ServerSettingsContext)
return serverSettings
}

View File

@ -5,22 +5,19 @@ import React, {ReactElement} from "react"
import {InputAdornment, TextField} from "@mui/material" import {InputAdornment, TextField} from "@mui/material"
import {MultiStepFormElement, SimpleForm} from "~/components" import {MultiStepFormElement, SimpleForm} from "~/components"
import {ServerSettings} from "~/types"
import {signup} from "~/apis" import {signup} from "~/apis"
import {handleErrors} from "~/utils" import {handleErrors} from "~/utils"
import {useServerSettings} from "~/hooks"
import DetectEmailAutofillService from "./DetectEmailAutofillService" import DetectEmailAutofillService from "./DetectEmailAutofillService"
import useSchema, {Form} from "./use-schema" import useSchema, {Form} from "./use-schema"
interface EmailFormProps { interface EmailFormProps {
serverSettings: ServerSettings
onSignUp: (email: string) => void onSignUp: (email: string) => void
} }
export default function EmailForm({ export default function EmailForm({onSignUp}: EmailFormProps): ReactElement {
serverSettings, const serverSettings = useServerSettings()
onSignUp,
}: EmailFormProps): ReactElement {
const schema = useSchema(serverSettings) const schema = useSchema(serverSettings)
const formik = useFormik<Form>({ const formik = useFormik<Form>({
validationSchema: schema, validationSchema: schema,

View File

@ -1 +1 @@
export {default as EmailForm} from "./EmailForm" export {default} from "./EmailForm"

View File

@ -1,23 +1,29 @@
import {useLocalStorage} from "react-use"
import React, {ReactElement} from "react" import React, {ReactElement} from "react"
import {MultiStepForm, SingleElementWrapper} from "~/components" import {MultiStepForm, SingleElementWrapper} from "~/components"
import EmailForm from "~/route-widgets/root/EmailForm/EmailForm" import EmailForm from "~/route-widgets/root/EmailForm"
import LoadingScreen from "~/LoadingScreen"
import YouGotMail from "~/route-widgets/root/YouGotMail" import YouGotMail from "~/route-widgets/root/YouGotMail"
export default function RootRoute(): ReactElement { export default function RootRoute(): ReactElement {
return <LoadingScreen /> const [email, setEmail] = useLocalStorage<string>(
"signup-form-state-email",
"",
)
const index = email ? 1 : 0
return ( return (
<SingleElementWrapper> <SingleElementWrapper>
<MultiStepForm <MultiStepForm
steps={[ steps={[
() => ( <EmailForm onSignUp={setEmail} key="email" />,
<EmailForm serverSettings={{}} onSignUp={() => null} /> <YouGotMail
), domain={(email || "").split("@")[1]}
() => <YouGotMail domain={""} />, key="you_got_mail"
/>,
]} ]}
index={0} index={index}
/> />
</SingleElementWrapper> </SingleElementWrapper>
) )