mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-23 01:20:30 +02:00
bugfixes
This commit is contained in:
parent
2840645b94
commit
a7eece7938
@ -226,6 +226,10 @@
|
|||||||
"saveAction": "Save preferences"
|
"saveAction": "Save preferences"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"LogoutRoute": {
|
||||||
|
"title": "Log out",
|
||||||
|
"description": "We are logging you out..."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -240,6 +244,9 @@
|
|||||||
"signup": "Sign up",
|
"signup": "Sign up",
|
||||||
"login": "Log in"
|
"login": "Log in"
|
||||||
},
|
},
|
||||||
|
"AuthenticatedRoute": {
|
||||||
|
"logout": "Logout"
|
||||||
|
},
|
||||||
"EnterDecryptionPassword": {
|
"EnterDecryptionPassword": {
|
||||||
"title": "Decrypt Reports",
|
"title": "Decrypt Reports",
|
||||||
"description": "Please enter your password so that your reports can de decrypted.",
|
"description": "Please enter your password so that your reports can de decrypted.",
|
||||||
|
@ -16,6 +16,7 @@ import AuthenticatedRoute from "~/routes/AuthenticatedRoute"
|
|||||||
import CompleteAccountRoute from "~/routes/CompleteAccountRoute"
|
import CompleteAccountRoute from "~/routes/CompleteAccountRoute"
|
||||||
import EnterDecryptionPassword from "~/routes/EnterDecryptionPassword"
|
import EnterDecryptionPassword from "~/routes/EnterDecryptionPassword"
|
||||||
import LoginRoute from "~/routes/LoginRoute"
|
import LoginRoute from "~/routes/LoginRoute"
|
||||||
|
import LogoutRoute from "~/routes/LogoutRoute"
|
||||||
import ReportDetailRoute from "~/routes/ReportDetailRoute"
|
import ReportDetailRoute from "~/routes/ReportDetailRoute"
|
||||||
import ReportsRoute from "~/routes/ReportsRoute"
|
import ReportsRoute from "~/routes/ReportsRoute"
|
||||||
import RootRoute from "~/routes/Root"
|
import RootRoute from "~/routes/Root"
|
||||||
@ -54,6 +55,10 @@ const router = createBrowserRouter([
|
|||||||
path: "/auth/complete-account",
|
path: "/auth/complete-account",
|
||||||
element: <CompleteAccountRoute />,
|
element: <CompleteAccountRoute />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/auth/logout",
|
||||||
|
element: <LogoutRoute />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2,16 +2,12 @@ import {ReactElement, ReactNode, useCallback, useEffect, useMemo} from "react"
|
|||||||
import {useLocalStorage} from "react-use"
|
import {useLocalStorage} from "react-use"
|
||||||
import {AxiosError} from "axios"
|
import {AxiosError} from "axios"
|
||||||
import {decrypt, readMessage, readPrivateKey} from "openpgp"
|
import {decrypt, readMessage, readPrivateKey} from "openpgp"
|
||||||
|
import {useNavigate} from "react-router-dom"
|
||||||
|
|
||||||
import {useMutation} from "@tanstack/react-query"
|
import {useMutation} from "@tanstack/react-query"
|
||||||
|
|
||||||
import {ServerUser, User} from "~/server-types"
|
import {ServerUser, User} from "~/server-types"
|
||||||
import {
|
import {REFRESH_TOKEN_URL, RefreshTokenResult, logout as logoutUser, refreshToken} from "~/apis"
|
||||||
REFRESH_TOKEN_URL,
|
|
||||||
RefreshTokenResult,
|
|
||||||
logout as logoutUser,
|
|
||||||
refreshToken,
|
|
||||||
} from "~/apis"
|
|
||||||
import {client} from "~/constants/axios-client"
|
import {client} from "~/constants/axios-client"
|
||||||
import {decryptString, encryptString} from "~/utils"
|
import {decryptString, encryptString} from "~/utils"
|
||||||
|
|
||||||
@ -21,20 +17,15 @@ export interface AuthContextProviderProps {
|
|||||||
children: ReactNode
|
children: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AuthContextProvider({
|
export default function AuthContextProvider({children}: AuthContextProviderProps): ReactElement {
|
||||||
children,
|
const {mutateAsync: refresh} = useMutation<RefreshTokenResult, AxiosError, void>(refreshToken, {
|
||||||
}: AuthContextProviderProps): ReactElement {
|
|
||||||
const {mutateAsync: refresh} = useMutation<
|
|
||||||
RefreshTokenResult,
|
|
||||||
AxiosError,
|
|
||||||
void
|
|
||||||
>(refreshToken, {
|
|
||||||
onError: () => logout(false),
|
onError: () => logout(false),
|
||||||
})
|
})
|
||||||
|
|
||||||
const [decryptionPassword, setDecryptionPassword] = useLocalStorage<
|
const [decryptionPassword, setDecryptionPassword] = useLocalStorage<string | null>(
|
||||||
string | null
|
"_global-context-auth-decryption-password",
|
||||||
>("_global-context-auth-decryption-password", null)
|
null,
|
||||||
|
)
|
||||||
const [user, setUser] = useLocalStorage<ServerUser | User | null>(
|
const [user, setUser] = useLocalStorage<ServerUser | User | null>(
|
||||||
"_global-context-auth-user",
|
"_global-context-auth-user",
|
||||||
null,
|
null,
|
||||||
@ -115,10 +106,7 @@ export default function AuthContextProvider({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if the password is correct
|
// Check if the password is correct
|
||||||
const masterPassword = decryptString(
|
const masterPassword = decryptString(user.encryptedPassword, password)
|
||||||
user.encryptedPassword,
|
|
||||||
password,
|
|
||||||
)
|
|
||||||
JSON.parse(decryptString(user.encryptedNotes, masterPassword))
|
JSON.parse(decryptString(user.encryptedNotes, masterPassword))
|
||||||
} catch {
|
} catch {
|
||||||
return false
|
return false
|
||||||
@ -162,15 +150,8 @@ export default function AuthContextProvider({
|
|||||||
|
|
||||||
// Decrypt user notes
|
// Decrypt user notes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (user && !user.isDecrypted && user.encryptedPassword && masterPassword) {
|
||||||
user &&
|
const note = JSON.parse(decryptUsingMasterPassword(user.encryptedNotes!))
|
||||||
!user.isDecrypted &&
|
|
||||||
user.encryptedPassword &&
|
|
||||||
masterPassword
|
|
||||||
) {
|
|
||||||
const note = JSON.parse(
|
|
||||||
decryptUsingMasterPassword(user.encryptedNotes!),
|
|
||||||
)
|
|
||||||
|
|
||||||
const newUser: User = {
|
const newUser: User = {
|
||||||
...user,
|
...user,
|
||||||
|
@ -2,6 +2,8 @@ import {ReactElement} from "react"
|
|||||||
import {useTranslation} from "react-i18next"
|
import {useTranslation} from "react-i18next"
|
||||||
import {useKeyPress} from "react-use"
|
import {useKeyPress} from "react-use"
|
||||||
import {MdContentCopy} from "react-icons/md"
|
import {MdContentCopy} from "react-icons/md"
|
||||||
|
import {useSnackbar} from "notistack"
|
||||||
|
import {Link as RouterLink} from "react-router-dom"
|
||||||
import copy from "copy-to-clipboard"
|
import copy from "copy-to-clipboard"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -16,7 +18,6 @@ import {
|
|||||||
import {AliasTypeIndicator} from "~/components"
|
import {AliasTypeIndicator} from "~/components"
|
||||||
import {AliasList} from "~/server-types"
|
import {AliasList} from "~/server-types"
|
||||||
import {useUIState} from "~/hooks"
|
import {useUIState} from "~/hooks"
|
||||||
import {useSnackbar} from "notistack"
|
|
||||||
import {SUCCESS_SNACKBAR_SHOW_DURATION} from "~/constants/values"
|
import {SUCCESS_SNACKBAR_SHOW_DURATION} from "~/constants/values"
|
||||||
import CreateAliasButton from "~/route-widgets/AliasesRoute/CreateAliasButton"
|
import CreateAliasButton from "~/route-widgets/AliasesRoute/CreateAliasButton"
|
||||||
import EmptyStateScreen from "~/route-widgets/AliasesRoute/EmptyStateScreen"
|
import EmptyStateScreen from "~/route-widgets/AliasesRoute/EmptyStateScreen"
|
||||||
@ -41,6 +42,7 @@ export default function AliasesDetails({aliases}: AliasesDetailsProps): ReactEle
|
|||||||
<List>
|
<List>
|
||||||
{aliasesUIState.map(alias => (
|
{aliasesUIState.map(alias => (
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
|
component={RouterLink}
|
||||||
key={alias.id}
|
key={alias.id}
|
||||||
onClick={event => {
|
onClick={event => {
|
||||||
if (isInCopyAddressMode) {
|
if (isInCopyAddressMode) {
|
||||||
@ -60,7 +62,7 @@ export default function AliasesDetails({aliases}: AliasesDetailsProps): ReactEle
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
href={`/aliases/${btoa(getAddress(alias))}`}
|
to={`/aliases/${btoa(getAddress(alias))}`}
|
||||||
>
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<AliasTypeIndicator type={alias.type} />
|
<AliasTypeIndicator type={alias.type} />
|
||||||
|
@ -11,7 +11,7 @@ import {LoadingButton} from "@mui/lab"
|
|||||||
import {Box, Grid, InputAdornment, Typography} from "@mui/material"
|
import {Box, Grid, InputAdornment, Typography} from "@mui/material"
|
||||||
import {useMutation} from "@tanstack/react-query"
|
import {useMutation} from "@tanstack/react-query"
|
||||||
|
|
||||||
import {PasswordField} from "~/components"
|
import {PasswordField, SimpleForm} from "~/components"
|
||||||
import {buildEncryptionPassword, encryptString} from "~/utils"
|
import {buildEncryptionPassword, encryptString} from "~/utils"
|
||||||
import {isDev} from "~/constants/development"
|
import {isDev} from "~/constants/development"
|
||||||
import {useSystemPreferredTheme, useUser} from "~/hooks"
|
import {useSystemPreferredTheme, useUser} from "~/hooks"
|
||||||
@ -68,7 +68,7 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
|
|||||||
{
|
{
|
||||||
onSuccess: ({user}) => {
|
onSuccess: ({user}) => {
|
||||||
login(user)
|
login(user)
|
||||||
onDone()
|
setTimeout(onDone, 0)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -119,32 +119,17 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
|
|||||||
return (
|
return (
|
||||||
<Box width="80vw">
|
<Box width="80vw">
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
<Grid
|
<SimpleForm
|
||||||
container
|
title={t("routes.CompleteAccountRoute.forms.password.title")}
|
||||||
spacing={4}
|
description={t("routes.CompleteAccountRoute.forms.password.description")}
|
||||||
paddingX={2}
|
continueActionLabel={t(
|
||||||
paddingY={4}
|
"routes.CompleteAccountRoute.forms.password.continueAction",
|
||||||
alignItems="center"
|
)}
|
||||||
justifyContent="center"
|
nonFieldError={formik.errors.detail}
|
||||||
>
|
>
|
||||||
<Grid item>
|
{[
|
||||||
<Grid container spacing={2} direction="column">
|
|
||||||
<Grid item>
|
|
||||||
<Typography variant="h6" component="h2" align="center">
|
|
||||||
{t("routes.CompleteAccountRoute.forms.password.title")}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
<Typography variant="subtitle1" component="p">
|
|
||||||
{t("routes.CompleteAccountRoute.forms.password.description")}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
<Grid container spacing={2} justifyContent="center">
|
|
||||||
<Grid item>
|
|
||||||
<PasswordField
|
<PasswordField
|
||||||
|
key="password"
|
||||||
fullWidth
|
fullWidth
|
||||||
id="password"
|
id="password"
|
||||||
name="password"
|
name="password"
|
||||||
@ -158,9 +143,7 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
|
|||||||
value={formik.values.password}
|
value={formik.values.password}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
disabled={formik.isSubmitting}
|
disabled={formik.isSubmitting}
|
||||||
error={
|
error={formik.touched.password && Boolean(formik.errors.password)}
|
||||||
formik.touched.password && Boolean(formik.errors.password)
|
|
||||||
}
|
|
||||||
helperText={formik.touched.password && formik.errors.password}
|
helperText={formik.touched.password && formik.errors.password}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
@ -169,10 +152,9 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
|
|||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>,
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
<PasswordField
|
<PasswordField
|
||||||
|
key="passwordConfirmation"
|
||||||
fullWidth
|
fullWidth
|
||||||
id="passwordConfirmation"
|
id="passwordConfirmation"
|
||||||
name="passwordConfirmation"
|
name="passwordConfirmation"
|
||||||
@ -200,21 +182,9 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
|
|||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>,
|
||||||
</Grid>
|
]}
|
||||||
</Grid>
|
</SimpleForm>
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
<LoadingButton
|
|
||||||
type="submit"
|
|
||||||
variant="contained"
|
|
||||||
loading={formik.isSubmitting}
|
|
||||||
startIcon={<MdChevronRight />}
|
|
||||||
>
|
|
||||||
{t("routes.CompleteAccountRoute.forms.password.continueAction")}
|
|
||||||
</LoadingButton>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</form>
|
</form>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
@ -54,7 +54,8 @@ export default function ConfirmCodeForm({
|
|||||||
|
|
||||||
return code.split("").every(char => chars.includes(char))
|
return code.split("").every(char => chars.includes(char))
|
||||||
},
|
},
|
||||||
),
|
)
|
||||||
|
.label(t("routes.LoginRoute.forms.confirmCode.form.code.label")),
|
||||||
})
|
})
|
||||||
const {mutateAsync} = useMutation<AuthenticationDetails, AxiosError, VerifyLoginWithEmailData>(
|
const {mutateAsync} = useMutation<AuthenticationDetails, AxiosError, VerifyLoginWithEmailData>(
|
||||||
verifyLoginWithEmail,
|
verifyLoginWithEmail,
|
||||||
|
@ -16,7 +16,7 @@ export default function EmptyStateScreen(): ReactElement {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Icon path={mdiTextBoxMultiple} size={2.5} />,
|
<Icon path={mdiTextBoxMultiple} size={2.5} />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import {ReactElement} from "react"
|
import {ReactElement} from "react"
|
||||||
import {Outlet} from "react-router-dom"
|
import {Outlet} from "react-router-dom"
|
||||||
|
import {Link as RouterLink} from "react-router-dom"
|
||||||
|
import {useTranslation} from "react-i18next"
|
||||||
|
import {MdLogout} from "react-icons/md"
|
||||||
|
|
||||||
import {Box, Grid, List, ListItem, Paper, useTheme} from "@mui/material"
|
import {Box, Button, Grid, List, ListItem, Paper, useTheme} from "@mui/material"
|
||||||
|
|
||||||
import {useUser} from "~/hooks"
|
import {useUser} from "~/hooks"
|
||||||
import LockNavigationContextProvider from "~/LockNavigationContext/LockNavigationContextProvider"
|
import LockNavigationContextProvider from "~/LockNavigationContext/LockNavigationContextProvider"
|
||||||
@ -9,11 +12,12 @@ import NavigationButton, {
|
|||||||
NavigationSection,
|
NavigationSection,
|
||||||
} from "~/route-widgets/AuthenticateRoute/NavigationButton"
|
} from "~/route-widgets/AuthenticateRoute/NavigationButton"
|
||||||
|
|
||||||
const sections = (
|
const sections = (Object.keys(NavigationSection) as Array<keyof typeof NavigationSection>).filter(
|
||||||
Object.keys(NavigationSection) as Array<keyof typeof NavigationSection>
|
value => isNaN(Number(value)),
|
||||||
).filter(value => isNaN(Number(value)))
|
)
|
||||||
|
|
||||||
export default function AuthenticatedRoute(): ReactElement {
|
export default function AuthenticatedRoute(): ReactElement {
|
||||||
|
const {t} = useTranslation()
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
|
|
||||||
useUser()
|
useUser()
|
||||||
@ -31,42 +35,65 @@ export default function AuthenticatedRoute(): ReactElement {
|
|||||||
display="flex"
|
display="flex"
|
||||||
maxWidth="90vw"
|
maxWidth="90vw"
|
||||||
width="100%"
|
width="100%"
|
||||||
|
height="100%"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Grid
|
<Grid
|
||||||
maxWidth="md"
|
maxWidth="md"
|
||||||
container
|
container
|
||||||
|
height="100%"
|
||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Grid item xs={12} sm={3} md={2}>
|
<Grid item xs={12} sm={3} md={2}>
|
||||||
<Box
|
<Box bgcolor={theme.palette.background.paper}>
|
||||||
bgcolor={theme.palette.background.paper}
|
<List component="nav">
|
||||||
component="nav"
|
|
||||||
>
|
|
||||||
<List>
|
|
||||||
{sections.map(key => (
|
{sections.map(key => (
|
||||||
<ListItem key={key}>
|
<ListItem key={key}>
|
||||||
<NavigationButton
|
<NavigationButton section={NavigationSection[key]} />
|
||||||
section={NavigationSection[key]}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={9} md={10}>
|
<Grid item xs={12} sm={9} md={10} height="100%">
|
||||||
<Paper>
|
<Grid
|
||||||
<Box
|
container
|
||||||
maxHeight="80vh"
|
direction="column"
|
||||||
sx={{overflowY: "auto"}}
|
height="100%"
|
||||||
padding={4}
|
justifyContent="space-between"
|
||||||
>
|
>
|
||||||
|
<Grid item></Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Paper>
|
||||||
|
<Box maxHeight="80vh" sx={{overflowY: "auto"}} padding={4}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={2}
|
||||||
|
justifyContent="center"
|
||||||
|
marginBottom={2}
|
||||||
|
>
|
||||||
|
<Grid item>
|
||||||
|
<Button
|
||||||
|
component={RouterLink}
|
||||||
|
color="inherit"
|
||||||
|
size="small"
|
||||||
|
to="/auth/logout"
|
||||||
|
startIcon={<MdLogout />}
|
||||||
|
>
|
||||||
|
{t("components.AuthenticatedRoute.logout")}
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -29,7 +29,10 @@ export default function CompleteAccountRoute(): ReactElement {
|
|||||||
onYes={() => setShowGenerationReportForm(true)}
|
onYes={() => setShowGenerationReportForm(true)}
|
||||||
onNo={navigateToNext}
|
onNo={navigateToNext}
|
||||||
/>,
|
/>,
|
||||||
<PasswordForm onDone={navigateToNext} key="password_form" />,
|
<PasswordForm
|
||||||
|
onDone={() => setTimeout(navigateToNext, 0)}
|
||||||
|
key="password_form"
|
||||||
|
/>,
|
||||||
]}
|
]}
|
||||||
index={showGenerationReportForm ? 1 : 0}
|
index={showGenerationReportForm ? 1 : 0}
|
||||||
/>
|
/>
|
||||||
|
@ -31,10 +31,7 @@ export default function EnterDecryptionPassword(): ReactElement {
|
|||||||
password: "",
|
password: "",
|
||||||
},
|
},
|
||||||
onSubmit: async ({password}, {setErrors}) => {
|
onSubmit: async ({password}, {setErrors}) => {
|
||||||
const decryptionPassword = buildEncryptionPassword(
|
const decryptionPassword = buildEncryptionPassword(password, user.email.address)
|
||||||
password,
|
|
||||||
user.email.address,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!_setDecryptionPassword(decryptionPassword)) {
|
if (!_setDecryptionPassword(decryptionPassword)) {
|
||||||
setErrors({
|
setErrors({
|
||||||
@ -43,7 +40,7 @@ export default function EnterDecryptionPassword(): ReactElement {
|
|||||||
),
|
),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
navigateToNext()
|
setTimeout(navigateToNext, 0)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -52,15 +49,9 @@ export default function EnterDecryptionPassword(): ReactElement {
|
|||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
<SimpleForm
|
<SimpleForm
|
||||||
title={t("components.EnterDecryptionPassword.title")}
|
title={t("components.EnterDecryptionPassword.title")}
|
||||||
description={t(
|
description={t("components.EnterDecryptionPassword.description")}
|
||||||
"components.EnterDecryptionPassword.description",
|
cancelActionLabel={t("components.EnterDecryptionPassword.cancelAction")}
|
||||||
)}
|
continueActionLabel={t("components.EnterDecryptionPassword.continueAction")}
|
||||||
cancelActionLabel={t(
|
|
||||||
"components.EnterDecryptionPassword.cancelAction",
|
|
||||||
)}
|
|
||||||
continueActionLabel={t(
|
|
||||||
"components.EnterDecryptionPassword.continueAction",
|
|
||||||
)}
|
|
||||||
isSubmitting={formik.isSubmitting}
|
isSubmitting={formik.isSubmitting}
|
||||||
>
|
>
|
||||||
{[
|
{[
|
||||||
@ -69,22 +60,15 @@ export default function EnterDecryptionPassword(): ReactElement {
|
|||||||
fullWidth
|
fullWidth
|
||||||
name="password"
|
name="password"
|
||||||
id="password"
|
id="password"
|
||||||
label={t(
|
label={t("components.EnterDecryptionPassword.form.password.label")}
|
||||||
"components.EnterDecryptionPassword.form.password.label",
|
|
||||||
)}
|
|
||||||
placeholder={t(
|
placeholder={t(
|
||||||
"components.EnterDecryptionPassword.form.password.placeholder",
|
"components.EnterDecryptionPassword.form.password.placeholder",
|
||||||
)}
|
)}
|
||||||
value={formik.values.password}
|
value={formik.values.password}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
disabled={formik.isSubmitting}
|
disabled={formik.isSubmitting}
|
||||||
error={
|
error={formik.touched.password && Boolean(formik.errors.password)}
|
||||||
formik.touched.password &&
|
helperText={formik.touched.password && formik.errors.password}
|
||||||
Boolean(formik.errors.password)
|
|
||||||
}
|
|
||||||
helperText={
|
|
||||||
formik.touched.password && formik.errors.password
|
|
||||||
}
|
|
||||||
InputProps={{
|
InputProps={{
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
<InputAdornment position="start">
|
<InputAdornment position="start">
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {ReactElement, useContext, useState} from "react"
|
import {ReactElement, useContext, useState} from "react"
|
||||||
import {useNavigate} from "react-router-dom"
|
import {useNavigate} from "react-router-dom"
|
||||||
|
import {useUpdateEffect} from "react-use"
|
||||||
|
|
||||||
import {MultiStepForm} from "~/components"
|
import {MultiStepForm} from "~/components"
|
||||||
import AuthContext from "~/AuthContext/AuthContext"
|
import AuthContext from "~/AuthContext/AuthContext"
|
||||||
@ -8,11 +9,23 @@ import EmailForm from "~/route-widgets/LoginRoute/EmailForm"
|
|||||||
|
|
||||||
export default function LoginRoute(): ReactElement {
|
export default function LoginRoute(): ReactElement {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const {login} = useContext(AuthContext)
|
const {login, user} = useContext(AuthContext)
|
||||||
|
|
||||||
const [email, setEmail] = useState<string>("")
|
const [email, setEmail] = useState<string>("")
|
||||||
const [sameRequestToken, setSameRequestToken] = useState<string>("")
|
const [sameRequestToken, setSameRequestToken] = useState<string>("")
|
||||||
|
|
||||||
|
useUpdateEffect(() => {
|
||||||
|
if (!user) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user?.encryptedPassword) {
|
||||||
|
navigate("/enter-password")
|
||||||
|
} else {
|
||||||
|
navigate("/")
|
||||||
|
}
|
||||||
|
}, [user?.encryptedPassword])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MultiStepForm
|
<MultiStepForm
|
||||||
steps={[
|
steps={[
|
||||||
@ -25,17 +38,7 @@ export default function LoginRoute(): ReactElement {
|
|||||||
/>,
|
/>,
|
||||||
<ConfirmCodeForm
|
<ConfirmCodeForm
|
||||||
key="confirm_code_form"
|
key="confirm_code_form"
|
||||||
onConfirm={user => {
|
onConfirm={login}
|
||||||
login(user)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
if (user.encryptedPassword) {
|
|
||||||
navigate("/enter-password")
|
|
||||||
} else {
|
|
||||||
navigate("/")
|
|
||||||
}
|
|
||||||
}, 0)
|
|
||||||
}}
|
|
||||||
email={email}
|
email={email}
|
||||||
sameRequestToken={sameRequestToken}
|
sameRequestToken={sameRequestToken}
|
||||||
/>,
|
/>,
|
||||||
|
41
src/routes/LogoutRoute.tsx
Normal file
41
src/routes/LogoutRoute.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import {ReactElement, useContext} from "react"
|
||||||
|
import {useTranslation} from "react-i18next"
|
||||||
|
import {useEffectOnce} from "react-use"
|
||||||
|
|
||||||
|
import {Box, CircularProgress, Grid, Paper, Typography} from "@mui/material"
|
||||||
|
|
||||||
|
import {useNavigateToNext} from "~/hooks"
|
||||||
|
import AuthContext from "~/AuthContext/AuthContext"
|
||||||
|
|
||||||
|
export default function LogoutRoute(): ReactElement {
|
||||||
|
const {t} = useTranslation()
|
||||||
|
const navigateToNext = useNavigateToNext("/auth/login")
|
||||||
|
const {logout} = useContext(AuthContext)
|
||||||
|
|
||||||
|
useEffectOnce(() => {
|
||||||
|
logout()
|
||||||
|
navigateToNext()
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Paper>
|
||||||
|
<Box padding={4}>
|
||||||
|
<Grid container spacing={4} direction="column" alignItems="center">
|
||||||
|
<Grid item>
|
||||||
|
<Typography variant="h6" component="h1">
|
||||||
|
{t("routes.LogoutRoute.title")}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<CircularProgress />
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Typography variant="body1">
|
||||||
|
{t("routes.LogoutRoute.description")}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user