mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-24 10:00:30 +02:00
development; fixes; added basic loading screen
This commit is contained in:
parent
69dadf1122
commit
f35a07efc1
@ -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",
|
||||||
|
@ -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>
|
||||||
)
|
)
|
||||||
|
44
src/LoadCriticalContent.tsx
Normal file
44
src/LoadCriticalContent.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
12
src/ServerSettingsContext.ts
Normal file
12
src/ServerSettingsContext.ts
Normal 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
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
|
@ -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
11
src/constants/themes.ts
Normal 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
2
src/hooks/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./use-server-settings"
|
||||||
|
export {default as useServerSettings} from "./use-server-settings"
|
10
src/hooks/use-server-settings.ts
Normal file
10
src/hooks/use-server-settings.ts
Normal 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
|
||||||
|
}
|
@ -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,
|
||||||
|
@ -1 +1 @@
|
|||||||
export {default as EmailForm} from "./EmailForm"
|
export {default} from "./EmailForm"
|
||||||
|
@ -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>
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user