mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-24 01:50: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",
|
||||
"crypto-js": "^4.1.1",
|
||||
"formik": "^2.2.9",
|
||||
"in-seconds": "^1.2.0",
|
||||
"openpgp": "^5.5.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
10
src/App.tsx
10
src/App.tsx
@ -2,8 +2,11 @@ import {RouterProvider, createBrowserRouter} from "react-router-dom"
|
||||
import React, {ReactElement} from "react"
|
||||
|
||||
import {QueryClientProvider} from "@tanstack/react-query"
|
||||
import {CssBaseline, ThemeProvider} from "@mui/material"
|
||||
|
||||
import {queryClient} from "~/constants/react-query"
|
||||
import {lightTheme} from "~/constants/themes"
|
||||
import LoadCriticalContent from "~/LoadCriticalContent"
|
||||
import RootRoute from "~/routes/Root"
|
||||
|
||||
const router = createBrowserRouter([
|
||||
@ -17,7 +20,12 @@ export default function App(): ReactElement {
|
||||
return (
|
||||
<React.StrictMode>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RouterProvider router={router} />
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<CssBaseline />
|
||||
<LoadCriticalContent>
|
||||
<RouterProvider router={router} />
|
||||
</LoadCriticalContent>
|
||||
</ThemeProvider>
|
||||
</QueryClientProvider>
|
||||
</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"
|
||||
|
||||
export default async function getServerSettings(): Promise<ServerSettings> {
|
||||
return (
|
||||
await axios.get(`${process.env.NEXT_PUBLIC_SERVER_BASE_URL}/settings`)
|
||||
).data
|
||||
return (await axios.get(`${import.meta.env.VITE_SERVER_BASE_URL}/settings`))
|
||||
.data
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ export interface SignupResult {
|
||||
|
||||
export default async function signup(email: string): Promise<SignupResult> {
|
||||
const {data} = await axios.post(
|
||||
`${process.env.NEXT_PUBLIC_SERVER_BASE_URL}/auth/signup`,
|
||||
`${import.meta.env.VITE_SERVER_BASE_URL}/auth/signup`,
|
||||
{
|
||||
email,
|
||||
},
|
||||
|
@ -12,7 +12,7 @@ export default async function validateToken({
|
||||
token,
|
||||
}: ValidateTokenData): Promise<AuthenticationDetails> {
|
||||
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,
|
||||
token: token,
|
||||
|
@ -1,27 +1,23 @@
|
||||
import React, {
|
||||
ReactElement,
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react"
|
||||
import React, {ReactElement, useEffect, useRef, useState} from "react"
|
||||
|
||||
import {Paper} from "@mui/material"
|
||||
|
||||
import {whenElementHasBounds} from "~/utils"
|
||||
|
||||
export interface MultiStepFormProps {
|
||||
steps: (() => ReactElement)[]
|
||||
steps: ReactElement[]
|
||||
index: number
|
||||
|
||||
duration?: number
|
||||
easing?: string
|
||||
}
|
||||
|
||||
function MultiStepForm(
|
||||
{steps, index, duration = 900, easing = "ease-in-out"}: MultiStepFormProps,
|
||||
ref: any,
|
||||
): ReactElement {
|
||||
function MultiStepForm({
|
||||
steps,
|
||||
index,
|
||||
duration = 900,
|
||||
easing = "ease-in-out",
|
||||
}: MultiStepFormProps): ReactElement {
|
||||
const $currentElement = useRef<HTMLDivElement>(null)
|
||||
const $timeout = useRef<any>()
|
||||
|
||||
@ -44,6 +40,11 @@ function MultiStepForm(
|
||||
return $timeout.current?.cancel!
|
||||
}, [index, currentIndex])
|
||||
|
||||
const hasSize = Boolean(
|
||||
(currentSize?.width || nextSize?.width) &&
|
||||
(currentSize?.height || nextSize?.height),
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@ -52,9 +53,13 @@ function MultiStepForm(
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: Math.max(currentSize?.width, nextSize?.width),
|
||||
height: Math.max(currentSize?.height, nextSize?.height),
|
||||
overflow: "hidden",
|
||||
width:
|
||||
Math.max(currentSize?.width || 0, nextSize?.width || 0) ||
|
||||
"100%",
|
||||
height:
|
||||
Math.max(currentSize?.height || 0, nextSize?.height || 0) ||
|
||||
"100%",
|
||||
overflow: hasSize ? "hidden" : "visible",
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
@ -122,7 +127,7 @@ function MultiStepForm(
|
||||
transform: isTransitioning ? "translateX(-100%)" : "",
|
||||
}}
|
||||
>
|
||||
{steps[currentIndex]()}
|
||||
{steps[currentIndex]}
|
||||
</div>
|
||||
<div
|
||||
// @ts-ignore
|
||||
@ -151,10 +156,10 @@ function MultiStepForm(
|
||||
>
|
||||
{currentIndex === steps.length - 1
|
||||
? null
|
||||
: steps[currentIndex + 1]()}
|
||||
: steps[currentIndex + 1]}
|
||||
</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 {MultiStepFormElement, SimpleForm} from "~/components"
|
||||
import {ServerSettings} from "~/types"
|
||||
import {signup} from "~/apis"
|
||||
import {handleErrors} from "~/utils"
|
||||
import {useServerSettings} from "~/hooks"
|
||||
|
||||
import DetectEmailAutofillService from "./DetectEmailAutofillService"
|
||||
import useSchema, {Form} from "./use-schema"
|
||||
|
||||
interface EmailFormProps {
|
||||
serverSettings: ServerSettings
|
||||
onSignUp: (email: string) => void
|
||||
}
|
||||
|
||||
export default function EmailForm({
|
||||
serverSettings,
|
||||
onSignUp,
|
||||
}: EmailFormProps): ReactElement {
|
||||
export default function EmailForm({onSignUp}: EmailFormProps): ReactElement {
|
||||
const serverSettings = useServerSettings()
|
||||
const schema = useSchema(serverSettings)
|
||||
const formik = useFormik<Form>({
|
||||
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 {MultiStepForm, SingleElementWrapper} from "~/components"
|
||||
import EmailForm from "~/route-widgets/root/EmailForm/EmailForm"
|
||||
import LoadingScreen from "~/LoadingScreen"
|
||||
import EmailForm from "~/route-widgets/root/EmailForm"
|
||||
import YouGotMail from "~/route-widgets/root/YouGotMail"
|
||||
|
||||
export default function RootRoute(): ReactElement {
|
||||
return <LoadingScreen />
|
||||
const [email, setEmail] = useLocalStorage<string>(
|
||||
"signup-form-state-email",
|
||||
"",
|
||||
)
|
||||
|
||||
const index = email ? 1 : 0
|
||||
|
||||
return (
|
||||
<SingleElementWrapper>
|
||||
<MultiStepForm
|
||||
steps={[
|
||||
() => (
|
||||
<EmailForm serverSettings={{}} onSignUp={() => null} />
|
||||
),
|
||||
() => <YouGotMail domain={""} />,
|
||||
<EmailForm onSignUp={setEmail} key="email" />,
|
||||
<YouGotMail
|
||||
domain={(email || "").split("@")[1]}
|
||||
key="you_got_mail"
|
||||
/>,
|
||||
]}
|
||||
index={0}
|
||||
index={index}
|
||||
/>
|
||||
</SingleElementWrapper>
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user