mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-20 00:05:26 +02:00
adding password form
This commit is contained in:
parent
2b20d129ae
commit
455ecf2a37
@ -11,6 +11,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.4",
|
"@emotion/react": "^11.10.4",
|
||||||
"@emotion/styled": "^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/lab": "^5.0.0-alpha.103",
|
||||||
"@mui/material": "^5.10.9",
|
"@mui/material": "^5.10.9",
|
||||||
"@tanstack/react-query": "^4.12.0",
|
"@tanstack/react-query": "^4.12.0",
|
||||||
|
@ -11,6 +11,7 @@ import AliasesRoute from "~/routes/AliasesRoute"
|
|||||||
import AuthContextProvider from "~/AuthContext/AuthContextProvider"
|
import AuthContextProvider from "~/AuthContext/AuthContextProvider"
|
||||||
import AuthenticateRoute from "~/routes/AuthenticateRoute"
|
import AuthenticateRoute from "~/routes/AuthenticateRoute"
|
||||||
import AuthenticatedRoute from "~/routes/AuthenticatedRoute"
|
import AuthenticatedRoute from "~/routes/AuthenticatedRoute"
|
||||||
|
import CompleteAccountRoute from "~/routes/CompleteAccountRoute"
|
||||||
import RootRoute from "~/routes/Root"
|
import RootRoute from "~/routes/Root"
|
||||||
import SignupRoute from "~/routes/SignupRoute"
|
import SignupRoute from "~/routes/SignupRoute"
|
||||||
import VerifyEmailRoute from "~/routes/VerifyEmailRoute"
|
import VerifyEmailRoute from "~/routes/VerifyEmailRoute"
|
||||||
@ -35,6 +36,10 @@ const router = createBrowserRouter([
|
|||||||
path: "/auth/signup",
|
path: "/auth/signup",
|
||||||
element: <SignupRoute />,
|
element: <SignupRoute />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/auth/complete-account",
|
||||||
|
element: <CompleteAccountRoute />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,7 @@ import {User} from "~/server-types"
|
|||||||
interface AuthContextTypeBase {
|
interface AuthContextTypeBase {
|
||||||
user: User | null
|
user: User | null
|
||||||
isAuthenticated: boolean
|
isAuthenticated: boolean
|
||||||
login: (user: User) => Promise<void>
|
login: (user: User, callback: () => void) => Promise<void>
|
||||||
logout: () => void
|
logout: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,8 +34,10 @@ export default function AuthContextProvider({
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const login = useCallback(async (user: User) => {
|
const login = useCallback(async (user: User, callback?: () => void) => {
|
||||||
setUser(user)
|
setUser(user)
|
||||||
|
|
||||||
|
callback?.()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const {mutateAsync: refresh} = useMutation<
|
const {mutateAsync: refresh} = useMutation<
|
||||||
|
@ -24,12 +24,12 @@ interface CreateAliasDataBase extends CreateAliasDataOther {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface CreateAliasDataRandomType extends CreateAliasDataBase {
|
interface CreateAliasDataRandomType extends CreateAliasDataBase {
|
||||||
type: AliasType.Random
|
type: AliasType.RANDOM
|
||||||
local?: undefined
|
local?: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateAliasDataCustomType extends CreateAliasDataBase {
|
interface CreateAliasDataCustomType extends CreateAliasDataBase {
|
||||||
type: AliasType.Custom
|
type: AliasType.CUSTOM
|
||||||
local: string
|
local: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ export default async function createAlias(
|
|||||||
): Promise<Alias> {
|
): Promise<Alias> {
|
||||||
const {data} = await axios.post(
|
const {data} = await axios.post(
|
||||||
`${import.meta.env.VITE_SERVER_BASE_URL}/alias`,
|
`${import.meta.env.VITE_SERVER_BASE_URL}/alias`,
|
||||||
aliasData,
|
{},
|
||||||
)
|
)
|
||||||
|
|
||||||
return parseAlias(data)
|
return parseAlias(data)
|
||||||
|
4
src/apis/update-account.ts
Normal file
4
src/apis/update-account.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface UpdateAccountData {
|
||||||
|
password: string
|
||||||
|
publicKey: string
|
||||||
|
}
|
@ -10,7 +10,7 @@ export default function MultiStepFormElement({
|
|||||||
children,
|
children,
|
||||||
}: MultiStepFormElementProps): ReactElement {
|
}: MultiStepFormElementProps): ReactElement {
|
||||||
return (
|
return (
|
||||||
<Box width="90vw" justifyContent="center" alignItems="center">
|
<Box maxWidth="90vw" justifyContent="center" alignItems="center">
|
||||||
<Container maxWidth="xs">{children}</Container>
|
<Container maxWidth="xs">{children}</Container>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
@ -5,7 +5,7 @@ import {AxiosError} from "axios"
|
|||||||
import {Button} from "@mui/material"
|
import {Button} from "@mui/material"
|
||||||
import {useMutation} from "@tanstack/react-query"
|
import {useMutation} from "@tanstack/react-query"
|
||||||
|
|
||||||
import {createAlias, CreateAliasData} from "~/apis"
|
import {CreateAliasData, createAlias} from "~/apis"
|
||||||
import {Alias, AliasType} from "~/server-types"
|
import {Alias, AliasType} from "~/server-types"
|
||||||
|
|
||||||
export interface CreateRandomAliasButtonProps {
|
export interface CreateRandomAliasButtonProps {
|
||||||
@ -27,7 +27,7 @@ export default function CreateRandomAliasButton({
|
|||||||
startIcon={<BsArrowClockwise />}
|
startIcon={<BsArrowClockwise />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
mutate({
|
mutate({
|
||||||
type: AliasType.Random,
|
type: AliasType.RANDOM,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import {ReactElement} from "react"
|
import {ReactElement} from "react"
|
||||||
import {BiStats} from "react-icons/bi"
|
import {BiStats} from "react-icons/bi"
|
||||||
import {MdMail, MdSettings} from "react-icons/md"
|
import {MdMail, MdSettings} from "react-icons/md"
|
||||||
import {IoMdDocument} from "react-icons/io"
|
|
||||||
import {Link as RouterLink, useLocation} from "react-router-dom"
|
import {Link as RouterLink, useLocation} from "react-router-dom"
|
||||||
|
|
||||||
import {Button} from "@mui/material"
|
import {Button} from "@mui/material"
|
||||||
|
import {mdiTextBoxMultiple} from "@mdi/js/commonjs/mdi"
|
||||||
|
import Icon from "@mdi/react"
|
||||||
|
|
||||||
export enum NavigationSection {
|
export enum NavigationSection {
|
||||||
Overview,
|
Overview,
|
||||||
@ -20,7 +21,7 @@ export interface NavigationButtonProps {
|
|||||||
const SECTION_ICON_MAP: Record<NavigationSection, ReactElement> = {
|
const SECTION_ICON_MAP: Record<NavigationSection, ReactElement> = {
|
||||||
[NavigationSection.Overview]: <BiStats />,
|
[NavigationSection.Overview]: <BiStats />,
|
||||||
[NavigationSection.Aliases]: <MdMail />,
|
[NavigationSection.Aliases]: <MdMail />,
|
||||||
[NavigationSection.Reports]: <IoMdDocument />,
|
[NavigationSection.Reports]: <Icon path={mdiTextBoxMultiple} size={0.8} />,
|
||||||
[NavigationSection.Settings]: <MdSettings />,
|
[NavigationSection.Settings]: <MdSettings />,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,9 @@ import {TiCancel} from "react-icons/ti"
|
|||||||
import React, {ReactElement} from "react"
|
import React, {ReactElement} from "react"
|
||||||
|
|
||||||
import {Box, Button, Grid, Typography} from "@mui/material"
|
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 {
|
export interface GenerateEmailReportsFormProps {
|
||||||
onYes: () => void
|
onYes: () => void
|
||||||
@ -14,7 +17,7 @@ export default function GenerateEmailReportsForm({
|
|||||||
onYes,
|
onYes,
|
||||||
}: GenerateEmailReportsFormProps): ReactElement {
|
}: GenerateEmailReportsFormProps): ReactElement {
|
||||||
return (
|
return (
|
||||||
<Box width="80vw">
|
<MultiStepFormElement>
|
||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
direction="column"
|
direction="column"
|
||||||
@ -33,7 +36,7 @@ export default function GenerateEmailReportsForm({
|
|||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Grid container spacing={2} direction="column">
|
<Grid container spacing={4} direction="column">
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Typography
|
<Typography
|
||||||
variant="h6"
|
variant="h6"
|
||||||
@ -43,6 +46,14 @@ export default function GenerateEmailReportsForm({
|
|||||||
Generate Email Reports?
|
Generate Email Reports?
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Box display="flex" justifyContent="center">
|
||||||
|
<Icon
|
||||||
|
path={mdiTextBoxMultiple}
|
||||||
|
size={2}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Typography
|
<Typography
|
||||||
variant="subtitle1"
|
variant="subtitle1"
|
||||||
@ -81,6 +92,6 @@ export default function GenerateEmailReportsForm({
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</MultiStepFormElement>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -10,10 +10,8 @@ import {Box, Grid, InputAdornment, Typography} from "@mui/material"
|
|||||||
import {PasswordField} from "~/components"
|
import {PasswordField} from "~/components"
|
||||||
import {encryptString} from "~/utils"
|
import {encryptString} from "~/utils"
|
||||||
import {isDev} from "~/constants/development"
|
import {isDev} from "~/constants/development"
|
||||||
|
import {useUser} from "~/hooks"
|
||||||
export interface PasswordFormProps {
|
import {useMutation} from "@tanstack/react-query"
|
||||||
email: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Form {
|
interface Form {
|
||||||
password: string
|
password: string
|
||||||
@ -29,7 +27,9 @@ const schema = yup.object().shape({
|
|||||||
.oneOf([yup.ref("password"), null], "Passwords must match"),
|
.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(
|
const awaitGenerateKey = useMemo(
|
||||||
() =>
|
() =>
|
||||||
generateKey({
|
generateKey({
|
||||||
@ -54,7 +54,7 @@ export default function PasswordForm({email}: PasswordFormProps): ReactElement {
|
|||||||
|
|
||||||
const encryptedPrivateKey = encryptString(
|
const encryptedPrivateKey = encryptString(
|
||||||
keyPair.privateKey,
|
keyPair.privateKey,
|
||||||
`${values.password}-${email}`,
|
`${values.password}-${user.email.address}`,
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log(encryptedPrivateKey)
|
console.log(encryptedPrivateKey)
|
@ -6,13 +6,14 @@ import React, {ReactElement} from "react"
|
|||||||
|
|
||||||
import {InputAdornment, TextField} from "@mui/material"
|
import {InputAdornment, TextField} from "@mui/material"
|
||||||
|
|
||||||
|
import {useMutation} from "@tanstack/react-query"
|
||||||
|
import DetectEmailAutofillService from "./DetectEmailAutofillService"
|
||||||
|
|
||||||
import {MultiStepFormElement, SimpleForm} from "~/components"
|
import {MultiStepFormElement, SimpleForm} from "~/components"
|
||||||
import {checkIsDomainDisposable, signup} from "~/apis"
|
import {SignupResult, checkIsDomainDisposable, signup} from "~/apis"
|
||||||
import {parseFastapiError} from "~/utils"
|
import {parseFastapiError} from "~/utils"
|
||||||
import {ServerSettings} from "~/server-types"
|
import {ServerSettings} from "~/server-types"
|
||||||
|
|
||||||
import DetectEmailAutofillService from "./DetectEmailAutofillService"
|
|
||||||
|
|
||||||
export interface EmailFormProps {
|
export interface EmailFormProps {
|
||||||
serverSettings: ServerSettings
|
serverSettings: ServerSettings
|
||||||
onSignUp: (email: string) => void
|
onSignUp: (email: string) => void
|
||||||
@ -31,6 +32,12 @@ export default function EmailForm({
|
|||||||
onSignUp,
|
onSignUp,
|
||||||
serverSettings,
|
serverSettings,
|
||||||
}: EmailFormProps): ReactElement {
|
}: EmailFormProps): ReactElement {
|
||||||
|
const {mutateAsync} = useMutation<SignupResult, AxiosError, string>(
|
||||||
|
signup,
|
||||||
|
{
|
||||||
|
onSuccess: ({normalized_email}) => onSignUp(normalized_email),
|
||||||
|
},
|
||||||
|
)
|
||||||
const formik = useFormik<Form>({
|
const formik = useFormik<Form>({
|
||||||
validationSchema: SCHEMA,
|
validationSchema: SCHEMA,
|
||||||
initialValues: {
|
initialValues: {
|
||||||
@ -57,8 +64,7 @@ export default function EmailForm({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await signup(values.email)
|
await mutateAsync(values.email)
|
||||||
onSignUp(values.email)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setErrors(parseFastapiError(error as AxiosError))
|
setErrors(parseFastapiError(error as AxiosError))
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
} from "@mui/material"
|
} from "@mui/material"
|
||||||
|
|
||||||
import {MultiStepFormElement, OpenMailButton} from "~/components"
|
import {MultiStepFormElement, OpenMailButton} from "~/components"
|
||||||
import ResendMailButton from "~/route-widgets/root/YouGotMail/ResendMailButton"
|
import ResendMailButton from "~/route-widgets/SignupRoute/YouGotMail/ResendMailButton"
|
||||||
|
|
||||||
export interface YouGotMailProps {
|
export interface YouGotMailProps {
|
||||||
email: string
|
email: string
|
||||||
|
@ -24,7 +24,7 @@ export default function AuthenticateRoute(): ReactElement {
|
|||||||
<Grid item>
|
<Grid item>
|
||||||
<Button
|
<Button
|
||||||
component={RouterLink}
|
component={RouterLink}
|
||||||
to="/signup"
|
to="/auth/signup"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
size="small"
|
size="small"
|
||||||
startIcon={<MdAdd />}
|
startIcon={<MdAdd />}
|
||||||
@ -35,7 +35,7 @@ export default function AuthenticateRoute(): ReactElement {
|
|||||||
<Grid item>
|
<Grid item>
|
||||||
<Button
|
<Button
|
||||||
component={RouterLink}
|
component={RouterLink}
|
||||||
to="/login"
|
to="/auth/login"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
size="small"
|
size="small"
|
||||||
startIcon={<MdLogin />}
|
startIcon={<MdLogin />}
|
||||||
|
@ -25,7 +25,7 @@ export default function AuthenticatedRoute(): ReactElement {
|
|||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
height="100vh"
|
height="100vh"
|
||||||
>
|
>
|
||||||
<Box width="90vw" justifyContent="center" alignItems="center">
|
<Box maxWidth="90vw" justifyContent="center" alignItems="center">
|
||||||
<Container
|
<Container
|
||||||
maxWidth="md"
|
maxWidth="md"
|
||||||
style={{
|
style={{
|
||||||
|
27
src/routes/CompleteAccountRoute.tsx
Normal file
27
src/routes/CompleteAccountRoute.tsx
Normal 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}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
@ -46,8 +46,7 @@ export default function VerifyEmailRoute(): ReactElement {
|
|||||||
>(validateEmail, {
|
>(validateEmail, {
|
||||||
onSuccess: async ({user}) => {
|
onSuccess: async ({user}) => {
|
||||||
setEmail("")
|
setEmail("")
|
||||||
await login(user)
|
await login(user, () => navigate("/auth/complete-account"))
|
||||||
navigate("/")
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const {loading} = useAsync(async () => {
|
const {loading} = useAsync(async () => {
|
||||||
|
@ -14,8 +14,8 @@ export enum ProxyUserAgentType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum AliasType {
|
export enum AliasType {
|
||||||
Random = "random",
|
RANDOM = "random",
|
||||||
Custom = "custom",
|
CUSTOM = "custom",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
Loading…
x
Reference in New Issue
Block a user