mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-18 23:45:26 +02:00
added resent email functionality
This commit is contained in:
parent
c8c774de54
commit
457ef7bc8c
@ -16,6 +16,7 @@
|
||||
"@tanstack/react-query": "^4.12.0",
|
||||
"axios": "^1.1.2",
|
||||
"crypto-js": "^4.1.1",
|
||||
"date-fns": "^2.29.3",
|
||||
"formik": "^2.2.9",
|
||||
"in-seconds": "^1.2.0",
|
||||
"openpgp": "^5.5.0",
|
||||
@ -28,6 +29,7 @@
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/date-fns": "^2.6.0",
|
||||
"@types/openpgp": "^4.4.18",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
|
@ -6,3 +6,5 @@ export * from "./signup"
|
||||
export {default as signup} from "./signup"
|
||||
export * from "./validate-email"
|
||||
export {default as validateEmail} from "./validate-email"
|
||||
export * from "./resend-email-verification-code"
|
||||
export {default as resendEmailVerificationCode} from "./resend-email-verification-code"
|
||||
|
12
src/apis/resend-email-verification-code.ts
Normal file
12
src/apis/resend-email-verification-code.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import axios from "axios"
|
||||
|
||||
export default function resendEmailVerificationCode(
|
||||
email: string,
|
||||
): Promise<void> {
|
||||
return axios.post(
|
||||
`${import.meta.env.VITE_SERVER_BASE_URL}/auth/resend-email`,
|
||||
{
|
||||
email,
|
||||
},
|
||||
)
|
||||
}
|
78
src/components/MutationStatusSnackbar.tsx
Normal file
78
src/components/MutationStatusSnackbar.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
import {AxiosError} from "axios"
|
||||
import {ReactElement, useEffect, useState} from "react"
|
||||
|
||||
import {UseMutationResult} from "@tanstack/react-query"
|
||||
import {Alert, Snackbar} from "@mui/material"
|
||||
|
||||
import {FastAPIError} from "~/utils"
|
||||
import getErrorMessage from "~/utils/get-error-message"
|
||||
|
||||
export interface MutationStatusSnackbarProps<
|
||||
TData = unknown,
|
||||
TError = unknown,
|
||||
TVariables = unknown,
|
||||
TContext = unknown,
|
||||
> {
|
||||
mutation: UseMutationResult<TData, TError, TVariables, TContext>
|
||||
|
||||
successMessage?: string
|
||||
errorMessage?: string
|
||||
}
|
||||
|
||||
export default function MutationStatusSnackbar<
|
||||
TData extends {data: {detail?: string}} = {data: {detail?: string}},
|
||||
TError extends AxiosError = AxiosError<FastAPIError>,
|
||||
TVariables = unknown,
|
||||
TContext = unknown,
|
||||
>({
|
||||
mutation,
|
||||
successMessage,
|
||||
errorMessage,
|
||||
}: MutationStatusSnackbarProps<
|
||||
TData,
|
||||
TError,
|
||||
TVariables,
|
||||
TContext
|
||||
>): ReactElement {
|
||||
const [open, setOpen] = useState<boolean>(false)
|
||||
|
||||
const severity = (() => {
|
||||
if (mutation.isError) {
|
||||
return "error"
|
||||
}
|
||||
|
||||
if (mutation.isSuccess) {
|
||||
return "success"
|
||||
}
|
||||
|
||||
return "info"
|
||||
})()
|
||||
const message = (() => {
|
||||
if (mutation.isError) {
|
||||
// @ts-ignore
|
||||
return errorMessage ?? getErrorMessage(mutation.error)
|
||||
}
|
||||
|
||||
if (mutation.isSuccess) {
|
||||
return successMessage ?? mutation.data?.data?.detail ?? "Success!"
|
||||
}
|
||||
})()
|
||||
|
||||
useEffect(() => {
|
||||
if (mutation.isSuccess || mutation.isError) {
|
||||
setOpen(true)
|
||||
}
|
||||
}, [mutation.isSuccess, mutation.isError])
|
||||
|
||||
return (
|
||||
<Snackbar
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
autoHideDuration={5000}
|
||||
>
|
||||
<Alert severity={severity} variant="filled">
|
||||
{message}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
)
|
||||
}
|
@ -8,3 +8,5 @@ export * from "./PasswordField"
|
||||
export {default as PasswordField} from "./PasswordField"
|
||||
export * from "./SimpleForm"
|
||||
export {default as SimpleForm} from "./SimpleForm"
|
||||
export * from "./MutationStatusSnackbar"
|
||||
export {default as MutationStatusSnackbar} from "./MutationStatusSnackbar"
|
||||
|
2
src/hooks/index.ts
Normal file
2
src/hooks/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./use-interval-update"
|
||||
export {default as useIntervalUpdate} from "./use-interval-update"
|
18
src/hooks/use-interval-update.ts
Normal file
18
src/hooks/use-interval-update.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {useEffect, useState} from "react"
|
||||
|
||||
export default function useIntervalUpdate(
|
||||
updateIntervalInMilliSeconds = 1000,
|
||||
): [Date, () => void] {
|
||||
const [, setForceUpdateValue] = useState<number>(0)
|
||||
const [startDate, setStartDate] = useState(() => new Date())
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setForceUpdateValue(value => value + 1)
|
||||
}, updateIntervalInMilliSeconds)
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
return [startDate, () => setStartDate(new Date())]
|
||||
}
|
52
src/route-widgets/root/YouGotMail/ResendMailButton.tsx
Normal file
52
src/route-widgets/root/YouGotMail/ResendMailButton.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import {MdEmail} from "react-icons/md"
|
||||
import {AxiosError} from "axios"
|
||||
import React, {ReactElement} from "react"
|
||||
import differenceInSeconds from "date-fns/differenceInSeconds"
|
||||
|
||||
import {useMutation} from "@tanstack/react-query"
|
||||
import {LoadingButton} from "@mui/lab"
|
||||
|
||||
import {useIntervalUpdate} from "~/hooks"
|
||||
import {resendEmailVerificationCode} from "~/apis"
|
||||
import {isDev} from "~/constants/development"
|
||||
import {MutationStatusSnackbar} from "~/components"
|
||||
|
||||
export interface ResendMailButtonProps {
|
||||
email: string
|
||||
}
|
||||
|
||||
const RESEND_INTERVAL = isDev ? 3 : 60
|
||||
|
||||
export default function ResendMailButton({
|
||||
email,
|
||||
}: ResendMailButtonProps): ReactElement {
|
||||
const [startDate, resetInterval] = useIntervalUpdate(1000)
|
||||
const secondsPassed = differenceInSeconds(new Date(), startDate)
|
||||
const secondsLeft = RESEND_INTERVAL - secondsPassed
|
||||
|
||||
const mutation = useMutation<void, AxiosError, string>(
|
||||
resendEmailVerificationCode,
|
||||
{
|
||||
onSettled: resetInterval,
|
||||
},
|
||||
)
|
||||
const {mutate: resendEmail, isLoading} = mutation
|
||||
|
||||
return (
|
||||
<>
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
startIcon={<MdEmail />}
|
||||
onClick={() => resendEmail(email)}
|
||||
loading={isLoading}
|
||||
disabled={secondsLeft > 0 || isLoading}
|
||||
>
|
||||
<span>
|
||||
<span>Resend Email</span>
|
||||
{secondsLeft > 0 && <span> ({secondsLeft})</span>}
|
||||
</span>
|
||||
</LoadingButton>
|
||||
<MutationStatusSnackbar mutation={mutation} />
|
||||
</>
|
||||
)
|
||||
}
|
@ -3,12 +3,15 @@ import React, {ReactElement} from "react"
|
||||
import {Grid, Typography} from "@mui/material"
|
||||
|
||||
import {MultiStepFormElement, OpenMailButton} from "~/components"
|
||||
import ResendMailButton from "~/route-widgets/root/YouGotMail/ResendMailButton"
|
||||
|
||||
export interface YouGotMailProps {
|
||||
domain: string
|
||||
email: string
|
||||
}
|
||||
|
||||
export default function YouGotMail({domain}: YouGotMailProps): ReactElement {
|
||||
export default function YouGotMail({email}: YouGotMailProps): ReactElement {
|
||||
const domain = email.split("@")[1]
|
||||
|
||||
return (
|
||||
<MultiStepFormElement>
|
||||
<Grid
|
||||
@ -35,6 +38,9 @@ export default function YouGotMail({domain}: YouGotMailProps): ReactElement {
|
||||
<Grid item>
|
||||
<OpenMailButton domain={domain} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<ResendMailButton email={email} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</MultiStepFormElement>
|
||||
)
|
1
src/route-widgets/root/YouGotMail/index.ts
Normal file
1
src/route-widgets/root/YouGotMail/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export {default} from "./YouGotMail"
|
@ -24,10 +24,7 @@ export default function SignupRoute(): ReactElement {
|
||||
onSignUp={setEmail}
|
||||
key="email"
|
||||
/>,
|
||||
<YouGotMail
|
||||
domain={(email || "").split("@")[1]}
|
||||
key="you_got_mail"
|
||||
/>,
|
||||
<YouGotMail email={email || ""} key="you_got_mail" />,
|
||||
]}
|
||||
index={index}
|
||||
/>
|
||||
|
0
src/types.d.ts → src/server-types.d.ts
vendored
0
src/types.d.ts → src/server-types.d.ts
vendored
13
src/utils/get-error-message.ts
Normal file
13
src/utils/get-error-message.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import {AxiosError} from "axios"
|
||||
|
||||
import {FastAPIError} from "~/utils/parse-fastapi-error"
|
||||
|
||||
export default function getErrorMessage(
|
||||
error: AxiosError<FastAPIError>,
|
||||
): string {
|
||||
if (typeof error.response?.data?.detail === "string") {
|
||||
return error.response.data.detail
|
||||
}
|
||||
|
||||
return "There was an error."
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user