adding password form

This commit is contained in:
Myzel394 2022-10-16 16:50:49 +02:00
parent 2b20d129ae
commit 455ecf2a37
18 changed files with 89 additions and 32 deletions

View File

@ -11,6 +11,8 @@
"dependencies": {
"@emotion/react": "^11.10.4",
"@emotion/styled": "^11.10.4",
"@mdi/js": "^7.0.96",
"@mdi/react": "^1.6.1",
"@mui/lab": "^5.0.0-alpha.103",
"@mui/material": "^5.10.9",
"@tanstack/react-query": "^4.12.0",

View File

@ -11,6 +11,7 @@ import AliasesRoute from "~/routes/AliasesRoute"
import AuthContextProvider from "~/AuthContext/AuthContextProvider"
import AuthenticateRoute from "~/routes/AuthenticateRoute"
import AuthenticatedRoute from "~/routes/AuthenticatedRoute"
import CompleteAccountRoute from "~/routes/CompleteAccountRoute"
import RootRoute from "~/routes/Root"
import SignupRoute from "~/routes/SignupRoute"
import VerifyEmailRoute from "~/routes/VerifyEmailRoute"
@ -35,6 +36,10 @@ const router = createBrowserRouter([
path: "/auth/signup",
element: <SignupRoute />,
},
{
path: "/auth/complete-account",
element: <CompleteAccountRoute />,
},
],
},
{

View File

@ -5,7 +5,7 @@ import {User} from "~/server-types"
interface AuthContextTypeBase {
user: User | null
isAuthenticated: boolean
login: (user: User) => Promise<void>
login: (user: User, callback: () => void) => Promise<void>
logout: () => void
}

View File

@ -34,8 +34,10 @@ export default function AuthContextProvider({
}
}, [])
const login = useCallback(async (user: User) => {
const login = useCallback(async (user: User, callback?: () => void) => {
setUser(user)
callback?.()
}, [])
const {mutateAsync: refresh} = useMutation<

View File

@ -24,12 +24,12 @@ interface CreateAliasDataBase extends CreateAliasDataOther {
}
interface CreateAliasDataRandomType extends CreateAliasDataBase {
type: AliasType.Random
type: AliasType.RANDOM
local?: undefined
}
interface CreateAliasDataCustomType extends CreateAliasDataBase {
type: AliasType.Custom
type: AliasType.CUSTOM
local: string
}
@ -42,7 +42,7 @@ export default async function createAlias(
): Promise<Alias> {
const {data} = await axios.post(
`${import.meta.env.VITE_SERVER_BASE_URL}/alias`,
aliasData,
{},
)
return parseAlias(data)

View File

@ -0,0 +1,4 @@
export interface UpdateAccountData {
password: string
publicKey: string
}

View File

@ -10,7 +10,7 @@ export default function MultiStepFormElement({
children,
}: MultiStepFormElementProps): ReactElement {
return (
<Box width="90vw" justifyContent="center" alignItems="center">
<Box maxWidth="90vw" justifyContent="center" alignItems="center">
<Container maxWidth="xs">{children}</Container>
</Box>
)

View File

@ -5,7 +5,7 @@ import {AxiosError} from "axios"
import {Button} from "@mui/material"
import {useMutation} from "@tanstack/react-query"
import {createAlias, CreateAliasData} from "~/apis"
import {CreateAliasData, createAlias} from "~/apis"
import {Alias, AliasType} from "~/server-types"
export interface CreateRandomAliasButtonProps {
@ -27,7 +27,7 @@ export default function CreateRandomAliasButton({
startIcon={<BsArrowClockwise />}
onClick={() =>
mutate({
type: AliasType.Random,
type: AliasType.RANDOM,
})
}
>

View File

@ -1,10 +1,11 @@
import {ReactElement} from "react"
import {BiStats} from "react-icons/bi"
import {MdMail, MdSettings} from "react-icons/md"
import {IoMdDocument} from "react-icons/io"
import {Link as RouterLink, useLocation} from "react-router-dom"
import {Button} from "@mui/material"
import {mdiTextBoxMultiple} from "@mdi/js/commonjs/mdi"
import Icon from "@mdi/react"
export enum NavigationSection {
Overview,
@ -20,7 +21,7 @@ export interface NavigationButtonProps {
const SECTION_ICON_MAP: Record<NavigationSection, ReactElement> = {
[NavigationSection.Overview]: <BiStats />,
[NavigationSection.Aliases]: <MdMail />,
[NavigationSection.Reports]: <IoMdDocument />,
[NavigationSection.Reports]: <Icon path={mdiTextBoxMultiple} size={0.8} />,
[NavigationSection.Settings]: <MdSettings />,
}

View File

@ -3,6 +3,9 @@ import {TiCancel} from "react-icons/ti"
import React, {ReactElement} from "react"
import {Box, Button, Grid, Typography} from "@mui/material"
import {MultiStepFormElement} from "~/components"
import {mdiTextBoxMultiple} from "@mdi/js/commonjs/mdi"
import Icon from "@mdi/react"
export interface GenerateEmailReportsFormProps {
onYes: () => void
@ -14,7 +17,7 @@ export default function GenerateEmailReportsForm({
onYes,
}: GenerateEmailReportsFormProps): ReactElement {
return (
<Box width="80vw">
<MultiStepFormElement>
<Grid
container
direction="column"
@ -33,7 +36,7 @@ export default function GenerateEmailReportsForm({
alignItems="center"
>
<Grid item>
<Grid container spacing={2} direction="column">
<Grid container spacing={4} direction="column">
<Grid item>
<Typography
variant="h6"
@ -43,6 +46,14 @@ export default function GenerateEmailReportsForm({
Generate Email Reports?
</Typography>
</Grid>
<Grid item>
<Box display="flex" justifyContent="center">
<Icon
path={mdiTextBoxMultiple}
size={2}
/>
</Box>
</Grid>
<Grid item>
<Typography
variant="subtitle1"
@ -81,6 +92,6 @@ export default function GenerateEmailReportsForm({
</Grid>
</Grid>
</Grid>
</Box>
</MultiStepFormElement>
)
}

View File

@ -10,10 +10,8 @@ import {Box, Grid, InputAdornment, Typography} from "@mui/material"
import {PasswordField} from "~/components"
import {encryptString} from "~/utils"
import {isDev} from "~/constants/development"
export interface PasswordFormProps {
email: string
}
import {useUser} from "~/hooks"
import {useMutation} from "@tanstack/react-query"
interface Form {
password: string
@ -29,7 +27,9 @@ const schema = yup.object().shape({
.oneOf([yup.ref("password"), null], "Passwords must match"),
})
export default function PasswordForm({email}: PasswordFormProps): ReactElement {
export default function PasswordForm(): ReactElement {
const user = useUser()
const {} = useMutation()
const awaitGenerateKey = useMemo(
() =>
generateKey({
@ -54,7 +54,7 @@ export default function PasswordForm({email}: PasswordFormProps): ReactElement {
const encryptedPrivateKey = encryptString(
keyPair.privateKey,
`${values.password}-${email}`,
`${values.password}-${user.email.address}`,
)
console.log(encryptedPrivateKey)

View File

@ -6,13 +6,14 @@ import React, {ReactElement} from "react"
import {InputAdornment, TextField} from "@mui/material"
import {useMutation} from "@tanstack/react-query"
import DetectEmailAutofillService from "./DetectEmailAutofillService"
import {MultiStepFormElement, SimpleForm} from "~/components"
import {checkIsDomainDisposable, signup} from "~/apis"
import {SignupResult, checkIsDomainDisposable, signup} from "~/apis"
import {parseFastapiError} from "~/utils"
import {ServerSettings} from "~/server-types"
import DetectEmailAutofillService from "./DetectEmailAutofillService"
export interface EmailFormProps {
serverSettings: ServerSettings
onSignUp: (email: string) => void
@ -31,6 +32,12 @@ export default function EmailForm({
onSignUp,
serverSettings,
}: EmailFormProps): ReactElement {
const {mutateAsync} = useMutation<SignupResult, AxiosError, string>(
signup,
{
onSuccess: ({normalized_email}) => onSignUp(normalized_email),
},
)
const formik = useFormik<Form>({
validationSchema: SCHEMA,
initialValues: {
@ -57,8 +64,7 @@ export default function EmailForm({
}
try {
await signup(values.email)
onSignUp(values.email)
await mutateAsync(values.email)
} catch (error) {
setErrors(parseFastapiError(error as AxiosError))
}

View File

@ -14,7 +14,7 @@ import {
} from "@mui/material"
import {MultiStepFormElement, OpenMailButton} from "~/components"
import ResendMailButton from "~/route-widgets/root/YouGotMail/ResendMailButton"
import ResendMailButton from "~/route-widgets/SignupRoute/YouGotMail/ResendMailButton"
export interface YouGotMailProps {
email: string

View File

@ -24,7 +24,7 @@ export default function AuthenticateRoute(): ReactElement {
<Grid item>
<Button
component={RouterLink}
to="/signup"
to="/auth/signup"
color="inherit"
size="small"
startIcon={<MdAdd />}
@ -35,7 +35,7 @@ export default function AuthenticateRoute(): ReactElement {
<Grid item>
<Button
component={RouterLink}
to="/login"
to="/auth/login"
color="inherit"
size="small"
startIcon={<MdLogin />}

View File

@ -25,7 +25,7 @@ export default function AuthenticatedRoute(): ReactElement {
justifyContent="center"
height="100vh"
>
<Box width="90vw" justifyContent="center" alignItems="center">
<Box maxWidth="90vw" justifyContent="center" alignItems="center">
<Container
maxWidth="md"
style={{

View File

@ -0,0 +1,27 @@
import {ReactElement, useState} from "react"
import {useNavigate} from "react-router-dom"
import {MultiStepForm} from "~/components"
import GenerateEmailReportsForm from "~/route-widgets/CompleteAccountRoute/GenerateEmailReportsForm"
import PasswordForm from "~/route-widgets/CompleteAccountRoute/PasswordForm"
export default function CompleteAccountRoute(): ReactElement {
const navigate = useNavigate()
const [showGenerationReportForm, setShowGenerationReportForm] =
useState(false)
return (
<MultiStepForm
steps={[
<GenerateEmailReportsForm
key="generate_email_reports"
onYes={() => setShowGenerationReportForm(true)}
onNo={() => navigate("/")}
/>,
<PasswordForm key="password_form" />,
]}
index={showGenerationReportForm ? 1 : 0}
/>
)
}

View File

@ -46,8 +46,7 @@ export default function VerifyEmailRoute(): ReactElement {
>(validateEmail, {
onSuccess: async ({user}) => {
setEmail("")
await login(user)
navigate("/")
await login(user, () => navigate("/auth/complete-account"))
},
})
const {loading} = useAsync(async () => {

View File

@ -14,8 +14,8 @@ export enum ProxyUserAgentType {
}
export enum AliasType {
Random = "random",
Custom = "custom",
RANDOM = "random",
CUSTOM = "custom",
}
export interface User {