mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-19 07:55:25 +02:00
improvements & bugfixes
This commit is contained in:
parent
457ef7bc8c
commit
30c52cdaeb
@ -26,15 +26,20 @@ function MultiStepForm({
|
||||
const [currentSize, setCurrentSize] = useState<any>(null)
|
||||
const [nextSize, setNextSize] = useState<any>(null)
|
||||
|
||||
const isTransitioning = currentIndex !== index
|
||||
const isTransitioning = currentIndex < index
|
||||
|
||||
useEffect(() => {
|
||||
if (index !== currentIndex) {
|
||||
if (index > currentIndex) {
|
||||
$timeout.current = setTimeout(() => {
|
||||
setCurrentSize(null)
|
||||
setNextSize(null)
|
||||
setCurrentIndex(index)
|
||||
}, duration)
|
||||
} else if (index < currentIndex) {
|
||||
// "Going-back" animation is not supported
|
||||
setCurrentIndex(index)
|
||||
setCurrentSize(null)
|
||||
setNextSize(null)
|
||||
}
|
||||
|
||||
return $timeout.current?.cancel!
|
||||
|
@ -19,7 +19,7 @@ export default function OpenMailButton({
|
||||
return (
|
||||
<Button
|
||||
startIcon={<IoMdMailOpen />}
|
||||
variant="contained"
|
||||
variant="text"
|
||||
href={APP_LINK_MAP[domain].android}
|
||||
>
|
||||
Open Mail
|
||||
|
@ -1,37 +1,66 @@
|
||||
import * as yup from "yup"
|
||||
import {useFormik} from "formik"
|
||||
import {MdEmail} from "react-icons/md"
|
||||
import {AxiosError} from "axios"
|
||||
import React, {ReactElement} from "react"
|
||||
|
||||
import {InputAdornment, TextField} from "@mui/material"
|
||||
|
||||
import {MultiStepFormElement, SimpleForm} from "~/components"
|
||||
import {signup} from "~/apis"
|
||||
import {handleErrors} from "~/utils"
|
||||
import {ServerSettings} from "~/types"
|
||||
import {checkIsDomainDisposable, signup} from "~/apis"
|
||||
import {parseFastapiError} from "~/utils"
|
||||
import {ServerSettings} from "~/server-types"
|
||||
|
||||
import DetectEmailAutofillService from "./DetectEmailAutofillService"
|
||||
import useSchema, {Form} from "./use-schema"
|
||||
|
||||
interface EmailFormProps {
|
||||
export interface EmailFormProps {
|
||||
serverSettings: ServerSettings
|
||||
onSignUp: (email: string) => void
|
||||
}
|
||||
|
||||
interface Form {
|
||||
email: string
|
||||
detail?: string
|
||||
}
|
||||
|
||||
const SCHEMA = yup.object().shape({
|
||||
email: yup.string().email().required(),
|
||||
})
|
||||
|
||||
export default function EmailForm({
|
||||
onSignUp,
|
||||
serverSettings,
|
||||
}: EmailFormProps): ReactElement {
|
||||
const schema = useSchema(serverSettings)
|
||||
const formik = useFormik<Form>({
|
||||
validationSchema: schema,
|
||||
validationSchema: SCHEMA,
|
||||
initialValues: {
|
||||
email: "",
|
||||
},
|
||||
onSubmit: (values, {setErrors}) =>
|
||||
handleErrors(
|
||||
values.email,
|
||||
setErrors,
|
||||
)(signup).then(({normalized_email}) => onSignUp(normalized_email)),
|
||||
onSubmit: async (values, {setErrors}) => {
|
||||
// Check is email disposable
|
||||
try {
|
||||
const isDisposable = await checkIsDomainDisposable(values.email)
|
||||
|
||||
if (isDisposable) {
|
||||
setErrors({
|
||||
email: "Disposable email addresses are not allowed",
|
||||
})
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
setErrors({
|
||||
detail: "An error occurred",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await signup(values.email)
|
||||
onSignUp(values.email)
|
||||
} catch (error) {
|
||||
setErrors(parseFastapiError(error as AxiosError))
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
|
@ -1,44 +0,0 @@
|
||||
import * as yup from "yup"
|
||||
|
||||
import {checkIsDomainDisposable} from "~/apis"
|
||||
import {ServerSettings} from "~/types"
|
||||
|
||||
export interface Form {
|
||||
email: string
|
||||
detail?: string
|
||||
}
|
||||
|
||||
export default function useSchema(
|
||||
serverSettings: ServerSettings,
|
||||
): yup.BaseSchema {
|
||||
return yup.object().shape({
|
||||
email: yup
|
||||
.string()
|
||||
.email()
|
||||
.required()
|
||||
.test(
|
||||
"notDisposable",
|
||||
"Disposable email addresses are not allowed",
|
||||
async (value, context) => {
|
||||
if (serverSettings.disposable_emails_enabled) {
|
||||
return true
|
||||
}
|
||||
|
||||
try {
|
||||
await yup.string().email().validate(value, {
|
||||
strict: true,
|
||||
})
|
||||
const isDisposable = await checkIsDomainDisposable(
|
||||
value!.split("@")[1],
|
||||
)
|
||||
|
||||
return !isDisposable
|
||||
} catch ({message}) {
|
||||
// @ts-ignore
|
||||
context.createError({message})
|
||||
return false
|
||||
}
|
||||
},
|
||||
),
|
||||
})
|
||||
}
|
@ -8,7 +8,7 @@ import {LoadingButton} from "@mui/lab"
|
||||
import {Box, Grid, InputAdornment, Typography} from "@mui/material"
|
||||
|
||||
import {PasswordField} from "~/components"
|
||||
import {encryptString, handleErrors} from "~/utils"
|
||||
import {encryptString} from "~/utils"
|
||||
import {isDev} from "~/constants/development"
|
||||
|
||||
export interface PasswordFormProps {
|
||||
@ -18,6 +18,7 @@ export interface PasswordFormProps {
|
||||
interface Form {
|
||||
password: string
|
||||
passwordConfirmation: string
|
||||
detail?: string
|
||||
}
|
||||
|
||||
const schema = yup.object().shape({
|
||||
@ -47,11 +48,8 @@ export default function PasswordForm({email}: PasswordFormProps): ReactElement {
|
||||
password: "",
|
||||
passwordConfirmation: "",
|
||||
},
|
||||
onSubmit: (values, {setErrors}) =>
|
||||
handleErrors(
|
||||
values,
|
||||
setErrors,
|
||||
)(async () => {
|
||||
onSubmit: async (values, {setErrors}) => {
|
||||
try {
|
||||
const keyPair = await awaitGenerateKey
|
||||
|
||||
const encryptedPrivateKey = encryptString(
|
||||
@ -60,7 +58,10 @@ export default function PasswordForm({email}: PasswordFormProps): ReactElement {
|
||||
)
|
||||
|
||||
console.log(encryptedPrivateKey)
|
||||
}),
|
||||
} catch (error) {
|
||||
setErrors({detail: "An error occurred"})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
|
@ -1,18 +1,36 @@
|
||||
import React, {ReactElement} from "react"
|
||||
import {MdCancel, MdEdit} from "react-icons/md"
|
||||
import React, {ReactElement, useState} from "react"
|
||||
|
||||
import {Grid, Typography} from "@mui/material"
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
Grid,
|
||||
IconButton,
|
||||
Typography,
|
||||
} from "@mui/material"
|
||||
|
||||
import {MultiStepFormElement, OpenMailButton} from "~/components"
|
||||
import ResendMailButton from "~/route-widgets/root/YouGotMail/ResendMailButton"
|
||||
|
||||
export interface YouGotMailProps {
|
||||
email: string
|
||||
onGoBack: () => void
|
||||
}
|
||||
|
||||
export default function YouGotMail({email}: YouGotMailProps): ReactElement {
|
||||
export default function YouGotMail({
|
||||
email,
|
||||
onGoBack,
|
||||
}: YouGotMailProps): ReactElement {
|
||||
const [askToEditEmail, setAskToEditEmail] = useState<boolean>(false)
|
||||
|
||||
const domain = email.split("@")[1]
|
||||
|
||||
return (
|
||||
<>
|
||||
<MultiStepFormElement>
|
||||
<Grid
|
||||
container
|
||||
@ -30,11 +48,30 @@ export default function YouGotMail({email}: YouGotMailProps): ReactElement {
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="subtitle1" component="p">
|
||||
We sent you an email with a link to confirm your email
|
||||
address. Please check your inbox and click on the link
|
||||
to continue.
|
||||
We sent you an email with a link to confirm your
|
||||
email address. Please check your inbox and click on
|
||||
the link to continue.
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Grid
|
||||
container
|
||||
alignItems="center"
|
||||
direction="row"
|
||||
spacing={2}
|
||||
>
|
||||
<Grid item>
|
||||
<code>{email}</code>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<IconButton
|
||||
onClick={() => setAskToEditEmail(true)}
|
||||
>
|
||||
<MdEdit />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<OpenMailButton domain={domain} />
|
||||
</Grid>
|
||||
@ -43,5 +80,24 @@ export default function YouGotMail({email}: YouGotMailProps): ReactElement {
|
||||
</Grid>
|
||||
</Grid>
|
||||
</MultiStepFormElement>
|
||||
<Dialog open={askToEditEmail}>
|
||||
<DialogTitle>Edit email address?</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Would you like to return to the previous step and edit
|
||||
your email address?
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
startIcon={<MdCancel />}
|
||||
onClick={() => setAskToEditEmail(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={onGoBack}>Yes, edit email</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import {useLocalStorage} from "react-use"
|
||||
import {useLoaderData} from "react-router-dom"
|
||||
|
||||
import {MultiStepForm} from "~/components"
|
||||
import {ServerSettings} from "~/types"
|
||||
import {ServerSettings} from "~/server-types"
|
||||
import EmailForm from "~/route-widgets/root/EmailForm"
|
||||
import YouGotMail from "~/route-widgets/root/YouGotMail"
|
||||
|
||||
@ -24,7 +24,11 @@ export default function SignupRoute(): ReactElement {
|
||||
onSignUp={setEmail}
|
||||
key="email"
|
||||
/>,
|
||||
<YouGotMail email={email || ""} key="you_got_mail" />,
|
||||
<YouGotMail
|
||||
onGoBack={() => setEmail("")}
|
||||
email={email || ""}
|
||||
key="you_got_mail"
|
||||
/>,
|
||||
]}
|
||||
index={index}
|
||||
/>
|
||||
|
@ -6,7 +6,7 @@ import React, {ReactElement} from "react"
|
||||
|
||||
import {Grid, Paper, Typography, useTheme} from "@mui/material"
|
||||
|
||||
import {ServerSettings} from "~/types"
|
||||
import {ServerSettings} from "~/server-types"
|
||||
import {validateEmail} from "~/apis"
|
||||
|
||||
const emailSchema = yup.string().email()
|
||||
|
@ -1,17 +0,0 @@
|
||||
import {FormikHelpers} from "formik"
|
||||
import parseFastAPIError from "./parse-fastapi-error"
|
||||
import {AxiosError} from "axios"
|
||||
|
||||
export default function handleErrors<T = any, Result = any, Values = any>(
|
||||
values: T,
|
||||
setErrors: FormikHelpers<Values>["setErrors"],
|
||||
) {
|
||||
return async (callback: (values: T) => Promise<Result>) => {
|
||||
try {
|
||||
const result = await callback(values)
|
||||
return result
|
||||
} catch (error) {
|
||||
setErrors(parseFastAPIError(error as AxiosError))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
export * from "./app-url-links"
|
||||
export {default as APP_LINK_MAP} from "./app-url-links"
|
||||
export * from "./encrypt-string"
|
||||
export { default as encryptString } from "./encrypt-string"
|
||||
export * from "./handle-errors"
|
||||
export {default as handleErrors} from "./handle-errors"
|
||||
export {default as encryptString} from "./encrypt-string"
|
||||
export * from "./parse-fastapi-error"
|
||||
export {default as parseFastapiError} from "./parse-fastapi-error"
|
||||
export * from "./when-element-has-bounds"
|
||||
|
Loading…
x
Reference in New Issue
Block a user