mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-19 07:55:25 +02:00
improvements
This commit is contained in:
parent
64a8ae3096
commit
c6634dc740
@ -23,6 +23,8 @@
|
||||
"date-fns": "^2.29.3",
|
||||
"formik": "^2.2.9",
|
||||
"group-array": "^1.0.0",
|
||||
"immutability-helper": "^3.1.1",
|
||||
"in-milliseconds": "^1.2.0",
|
||||
"in-seconds": "^1.2.0",
|
||||
"openpgp": "^5.5.0",
|
||||
"react": "^18.2.0",
|
||||
|
@ -2,11 +2,18 @@ import {createContext} from "react"
|
||||
|
||||
import {ServerUser, User} from "~/server-types"
|
||||
|
||||
export enum EncryptionStatus {
|
||||
Unavailable = "Unavailable",
|
||||
PasswordRequired = "PasswordRequired",
|
||||
Available = "Available",
|
||||
}
|
||||
|
||||
interface AuthContextTypeBase {
|
||||
user: ServerUser | User | null
|
||||
isAuthenticated: boolean
|
||||
login: (user: ServerUser | User) => void
|
||||
logout: () => void
|
||||
encryptionStatus: EncryptionStatus
|
||||
_decryptUsingMasterPassword: (content: string) => string
|
||||
_encryptUsingMasterPassword: (content: string) => string
|
||||
_decryptUsingPrivateKey: (message: string) => Promise<string>
|
||||
@ -31,6 +38,7 @@ export type AuthContextType =
|
||||
const AuthContext = createContext<AuthContextType>({
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
encryptionStatus: EncryptionStatus.Unavailable,
|
||||
login: () => {
|
||||
throw new Error("login() not implemented")
|
||||
},
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
import {client} from "~/constants/axios-client"
|
||||
import {decryptString, encryptString} from "~/utils"
|
||||
|
||||
import AuthContext, {AuthContextType} from "./AuthContext"
|
||||
import AuthContext, {AuthContextType, EncryptionStatus} from "./AuthContext"
|
||||
|
||||
export interface AuthContextProviderProps {
|
||||
children: ReactNode
|
||||
@ -134,6 +134,21 @@ export default function AuthContextProvider({
|
||||
() => ({
|
||||
user: user ?? null,
|
||||
login: setUser,
|
||||
encryptionStatus: (() => {
|
||||
if (!user) {
|
||||
return EncryptionStatus.Unavailable
|
||||
}
|
||||
|
||||
if (!user.encryptedPassword) {
|
||||
return EncryptionStatus.Unavailable
|
||||
}
|
||||
|
||||
if (user.isDecrypted) {
|
||||
return EncryptionStatus.Available
|
||||
}
|
||||
|
||||
return EncryptionStatus.PasswordRequired
|
||||
})(),
|
||||
logout,
|
||||
isAuthenticated: user !== null,
|
||||
_encryptUsingMasterPassword: encryptUsingMasterPassword,
|
||||
|
@ -1,31 +1,85 @@
|
||||
import {ReactElement, useContext} from "react"
|
||||
import {useContext} from "react"
|
||||
import {MdLock} from "react-icons/md"
|
||||
import {Link as RouterLink} from "react-router-dom"
|
||||
|
||||
import {Alert, Button, Grid} from "@mui/material"
|
||||
import {Button, Grid, Typography} from "@mui/material"
|
||||
|
||||
import AuthContext, {EncryptionStatus} from "~/AuthContext/AuthContext"
|
||||
import LockNavigationContext from "~/LockNavigationContext/LockNavigationContext"
|
||||
|
||||
export default function DecryptionPasswordMissingAlert(): ReactElement {
|
||||
const {handleAnchorClick} = useContext(LockNavigationContext)
|
||||
|
||||
return (
|
||||
<Grid container spacing={2} direction="column" alignItems="center">
|
||||
<Grid item>
|
||||
<Alert severity="warning">
|
||||
Your decryption password is required to view this section.
|
||||
</Alert>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
startIcon={<MdLock />}
|
||||
component={RouterLink}
|
||||
to="/enter-password"
|
||||
onClick={handleAnchorClick}
|
||||
>
|
||||
Enter password
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
export interface WithEncryptionRequiredProps {
|
||||
children?: JSX.Element
|
||||
}
|
||||
|
||||
export default function DecryptionPasswordMissingAlert({
|
||||
children = <></>,
|
||||
}: WithEncryptionRequiredProps): JSX.Element {
|
||||
const {handleAnchorClick} = useContext(LockNavigationContext)
|
||||
const {encryptionStatus} = useContext(AuthContext)
|
||||
|
||||
switch (encryptionStatus) {
|
||||
case EncryptionStatus.Unavailable: {
|
||||
return (
|
||||
<Grid container spacing={4}>
|
||||
<Grid item>
|
||||
<Typography variant="h6" component="h2">
|
||||
Encryption required
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography>
|
||||
You need to set up encryption to use this feature.
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
variant="contained"
|
||||
component={RouterLink}
|
||||
to="/complete-account?setup=true"
|
||||
startIcon={<MdLock />}
|
||||
onClick={handleAnchorClick}
|
||||
>
|
||||
Setup encryption
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
case EncryptionStatus.PasswordRequired: {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
spacing={4}
|
||||
direction="column"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid item>
|
||||
<Typography variant="h6" component="h2">
|
||||
Password required
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography>
|
||||
Your decryption password is required to view this
|
||||
section.
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to={`/enter-password?next=${window.location.pathname}`}
|
||||
startIcon={<MdLock />}
|
||||
onClick={handleAnchorClick}
|
||||
>
|
||||
Enter Password
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
default:
|
||||
return children
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,15 @@
|
||||
import * as inMilliseconds from "in-milliseconds"
|
||||
|
||||
import {QueryClient} from "@tanstack/react-query"
|
||||
|
||||
export const queryClient = new QueryClient()
|
||||
export const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: true,
|
||||
refetchOnReconnect: true,
|
||||
refetchInterval: inMilliseconds.minutes(10),
|
||||
refetchOnMount: "always",
|
||||
staleTime: inMilliseconds.minutes(10),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -1,76 +1,15 @@
|
||||
import {ReactElement} from "react"
|
||||
import {Link as RouterLink} from "react-router-dom"
|
||||
import {MdLock} from "react-icons/md"
|
||||
|
||||
import {Button, Grid, Typography} from "@mui/material"
|
||||
|
||||
import {useUser} from "~/hooks"
|
||||
import {DecryptionPasswordMissingAlert} from "~/components"
|
||||
|
||||
export default function WithEncryptionRequired(
|
||||
Component: any,
|
||||
): (props: any) => ReactElement {
|
||||
return (props: any): ReactElement => {
|
||||
const user = useUser()
|
||||
|
||||
if (!user.encryptedPassword) {
|
||||
return (
|
||||
<Grid container spacing={4}>
|
||||
<Grid item>
|
||||
<Typography variant="h6" component="h2">
|
||||
Encryption required
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography>
|
||||
To continue, you need to enable encryption.
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to="/complete-account?setup=true"
|
||||
startIcon={<MdLock />}
|
||||
>
|
||||
Setup encryption
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
if (!user.isDecrypted) {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
spacing={4}
|
||||
direction="column"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid item>
|
||||
<Typography variant="h6" component="h2">
|
||||
Encryption required
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography>
|
||||
To continue, please enter your password to decrypt
|
||||
your data.
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
variant="contained"
|
||||
component={RouterLink}
|
||||
to="/enter-password"
|
||||
startIcon={<MdLock />}
|
||||
>
|
||||
Enter Password
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
return <Component {...props} />
|
||||
return (
|
||||
<DecryptionPasswordMissingAlert>
|
||||
<Component {...props} />
|
||||
</DecryptionPasswordMissingAlert>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -6,3 +6,5 @@ export * from "./use-user"
|
||||
export {default as useUser} from "./use-user"
|
||||
export * from "./use-system-preferred-theme"
|
||||
export {default as useSystemPreferredTheme} from "./use-system-preferred-theme"
|
||||
export * from "./use-ui-state"
|
||||
export {default as useUIState} from "./use-ui-state"
|
||||
|
14
src/hooks/use-ui-state.ts
Normal file
14
src/hooks/use-ui-state.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import {Dispatch, SetStateAction, useState} from "react"
|
||||
import {useUpdateEffect} from "react-use"
|
||||
|
||||
export default function useUIState<T>(
|
||||
outerValue: T,
|
||||
): [T, Dispatch<SetStateAction<T>>] {
|
||||
const [value, setValue] = useState<T>(outerValue)
|
||||
|
||||
useUpdateEffect(() => {
|
||||
setValue(outerValue)
|
||||
}, [outerValue])
|
||||
|
||||
return [value, setValue]
|
||||
}
|
97
src/route-widgets/AliasDetailRoute/AliasDetails.tsx
Normal file
97
src/route-widgets/AliasDetailRoute/AliasDetails.tsx
Normal file
@ -0,0 +1,97 @@
|
||||
import {useParams} from "react-router"
|
||||
import {ReactElement, useContext} from "react"
|
||||
|
||||
import {Grid, Typography} from "@mui/material"
|
||||
|
||||
import {AliasTypeIndicator, DecryptionPasswordMissingAlert} from "~/components"
|
||||
import {Alias, DecryptedAlias} from "~/server-types"
|
||||
import {useUIState} from "~/hooks"
|
||||
import AliasNotesForm from "~/route-widgets/AliasDetailRoute/AliasNotesForm"
|
||||
import AliasPreferencesForm from "~/route-widgets/AliasDetailRoute/AliasPreferencesForm"
|
||||
import AuthContext, {EncryptionStatus} from "~/AuthContext/AuthContext"
|
||||
import ChangeAliasActivationStatusSwitch from "~/route-widgets/AliasDetailRoute/ChangeAliasActivationStatusSwitch"
|
||||
|
||||
export interface AliasDetailsProps {
|
||||
alias: Alias | DecryptedAlias
|
||||
}
|
||||
|
||||
export default function AliasDetails({
|
||||
alias: aliasValue,
|
||||
}: AliasDetailsProps): ReactElement {
|
||||
const params = useParams()
|
||||
const {encryptionStatus} = useContext(AuthContext)
|
||||
const address = atob(params.addressInBase64 as string)
|
||||
|
||||
const [aliasUIState, setAliasUIState] = useUIState<Alias | DecryptedAlias>(
|
||||
aliasValue,
|
||||
)
|
||||
|
||||
return (
|
||||
<Grid container spacing={4}>
|
||||
<Grid item>
|
||||
<Grid container spacing={1} direction="row" alignItems="center">
|
||||
<Grid item>
|
||||
<AliasTypeIndicator type={aliasUIState.type} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="subtitle1">{address}</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<ChangeAliasActivationStatusSwitch
|
||||
id={aliasUIState.id}
|
||||
isActive={aliasUIState.isActive}
|
||||
onChanged={setAliasUIState}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item width="100%">
|
||||
<Grid container direction="column" spacing={4}>
|
||||
<Grid item>
|
||||
<Typography variant="h6" component="h3">
|
||||
Notes
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
{encryptionStatus === EncryptionStatus.Available ? (
|
||||
<AliasNotesForm
|
||||
id={aliasUIState.id}
|
||||
notes={(aliasUIState as DecryptedAlias).notes}
|
||||
onChanged={setAliasUIState}
|
||||
/>
|
||||
) : (
|
||||
<DecryptionPasswordMissingAlert />
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Grid container spacing={4}>
|
||||
<Grid item>
|
||||
<Typography variant="h6" component="h3">
|
||||
Settings
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="body1">
|
||||
These settings apply to this alias only. You can
|
||||
either set a value manually or refer to your
|
||||
defaults settings. Note that this does change in
|
||||
behavior. When you set a value to refer to your
|
||||
default setting, the alias will always use the
|
||||
latest value. So when you change your default
|
||||
setting, the alias will automatically use the new
|
||||
value.
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<AliasPreferencesForm
|
||||
alias={aliasUIState}
|
||||
onChanged={setAliasUIState}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
@ -4,6 +4,7 @@ import {AxiosError} from "axios"
|
||||
import {ReactElement, useContext} from "react"
|
||||
import {RiLinkM, RiStickyNoteFill} from "react-icons/ri"
|
||||
import {FieldArray, FormikProvider, useFormik} from "formik"
|
||||
import update from "immutability-helper"
|
||||
|
||||
import {useMutation} from "@tanstack/react-query"
|
||||
import {
|
||||
@ -22,15 +23,17 @@ import {
|
||||
} from "@mui/material"
|
||||
|
||||
import {URL_REGEX} from "~/constants/values"
|
||||
import {parseFastAPIError, whenEnterPressed} from "~/utils"
|
||||
import {decryptAliasNotes, parseFastAPIError, whenEnterPressed} from "~/utils"
|
||||
import {BackupImage, ErrorSnack, SuccessSnack} from "~/components"
|
||||
import {Alias, AliasNote} from "~/server-types"
|
||||
import {Alias, AliasNote, DecryptedAlias} from "~/server-types"
|
||||
import {UpdateAliasData, updateAlias} from "~/apis"
|
||||
import AuthContext from "~/AuthContext/AuthContext"
|
||||
|
||||
export interface AliasNotesFormProps {
|
||||
id: string
|
||||
notes: AliasNote
|
||||
|
||||
onChanged: (alias: DecryptedAlias) => void
|
||||
}
|
||||
|
||||
interface Form {
|
||||
@ -65,13 +68,19 @@ const getDomain = (url: string): string => {
|
||||
export default function AliasNotesForm({
|
||||
id,
|
||||
notes,
|
||||
onChanged,
|
||||
}: AliasNotesFormProps): ReactElement {
|
||||
const {_encryptUsingMasterPassword} = useContext(AuthContext)
|
||||
const {_encryptUsingMasterPassword, _decryptUsingMasterPassword} =
|
||||
useContext(AuthContext)
|
||||
const {mutateAsync, isSuccess} = useMutation<
|
||||
Alias,
|
||||
AxiosError,
|
||||
UpdateAliasData
|
||||
>(values => updateAlias(id, values))
|
||||
>(values => updateAlias(id, values), {
|
||||
onSuccess: newAlias => {
|
||||
onChanged(decryptAliasNotes(newAlias, _decryptUsingMasterPassword))
|
||||
},
|
||||
})
|
||||
const formik = useFormik<Form>({
|
||||
validationSchema: SCHEMA,
|
||||
initialValues: {
|
||||
@ -80,14 +89,17 @@ export default function AliasNotesForm({
|
||||
},
|
||||
onSubmit: async (values, {setErrors}) => {
|
||||
try {
|
||||
const newNotes = {
|
||||
...notes,
|
||||
const newNotes = update(notes, {
|
||||
data: {
|
||||
...notes.data,
|
||||
personalNotes: values.personalNotes,
|
||||
websites: values.websites,
|
||||
personalNotes: {
|
||||
$set: values.personalNotes,
|
||||
},
|
||||
websites: {
|
||||
$set: values.websites,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const data = _encryptUsingMasterPassword(
|
||||
JSON.stringify(newNotes),
|
||||
)
|
||||
|
@ -30,6 +30,8 @@ import SelectField from "~/route-widgets/SettingsRoute/SelectField"
|
||||
|
||||
export interface AliasPreferencesFormProps {
|
||||
alias: Alias | DecryptedAlias
|
||||
|
||||
onChanged: (newAlias: Alias | DecryptedAlias) => void
|
||||
}
|
||||
|
||||
interface Form {
|
||||
@ -56,12 +58,15 @@ const SCHEMA = yup.object().shape({
|
||||
|
||||
export default function AliasPreferencesForm({
|
||||
alias,
|
||||
onChanged,
|
||||
}: AliasPreferencesFormProps): ReactElement {
|
||||
const {mutateAsync, isSuccess} = useMutation<
|
||||
Alias,
|
||||
AxiosError,
|
||||
UpdateAliasData
|
||||
>(data => updateAlias(alias.id, data))
|
||||
>(data => updateAlias(alias.id, data), {
|
||||
onSuccess: onChanged,
|
||||
})
|
||||
const formik = useFormik<Form>({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
|
@ -1,19 +1,20 @@
|
||||
import {ReactElement, useEffect, useState} from "react"
|
||||
import {ReactElement, useContext, useEffect, useState} from "react"
|
||||
import {AxiosError} from "axios"
|
||||
|
||||
import {Switch} from "@mui/material"
|
||||
import {useMutation} from "@tanstack/react-query"
|
||||
|
||||
import {Alias} from "~/server-types"
|
||||
import {Alias, DecryptedAlias} from "~/server-types"
|
||||
import {UpdateAliasData, updateAlias} from "~/apis"
|
||||
import {parseFastAPIError} from "~/utils"
|
||||
import {decryptAliasNotes, parseFastAPIError} from "~/utils"
|
||||
import {ErrorSnack, SuccessSnack} from "~/components"
|
||||
import AuthContext, {EncryptionStatus} from "~/AuthContext/AuthContext"
|
||||
|
||||
export interface ChangeAliasActivationStatusSwitchProps {
|
||||
id: string
|
||||
isActive: boolean
|
||||
|
||||
onChanged: () => void
|
||||
onChanged: (alias: Alias | DecryptedAlias) => void
|
||||
}
|
||||
|
||||
export default function ChangeAliasActivationStatusSwitch({
|
||||
@ -21,6 +22,9 @@ export default function ChangeAliasActivationStatusSwitch({
|
||||
isActive,
|
||||
onChanged,
|
||||
}: ChangeAliasActivationStatusSwitchProps): ReactElement {
|
||||
const {_decryptUsingMasterPassword, encryptionStatus} =
|
||||
useContext(AuthContext)
|
||||
|
||||
const [isActiveUIState, setIsActiveUIState] = useState<boolean>(true)
|
||||
|
||||
const [successMessage, setSuccessMessage] = useState<string>("")
|
||||
@ -31,7 +35,15 @@ export default function ChangeAliasActivationStatusSwitch({
|
||||
AxiosError,
|
||||
UpdateAliasData
|
||||
>(values => updateAlias(id, values), {
|
||||
onSuccess: onChanged,
|
||||
onSuccess: newAlias => {
|
||||
if (encryptionStatus === EncryptionStatus.Available) {
|
||||
onChanged(
|
||||
decryptAliasNotes(newAlias, _decryptUsingMasterPassword),
|
||||
)
|
||||
} else {
|
||||
onChanged(newAlias)
|
||||
}
|
||||
},
|
||||
onError: error =>
|
||||
setErrorMessage(parseFastAPIError(error).detail as string),
|
||||
})
|
||||
|
@ -3,27 +3,24 @@ import {useParams} from "react-router-dom"
|
||||
import {AxiosError} from "axios"
|
||||
|
||||
import {useQuery} from "@tanstack/react-query"
|
||||
import {Grid, Typography} from "@mui/material"
|
||||
|
||||
import {getAlias} from "~/apis"
|
||||
import {Alias, DecryptedAlias} from "~/server-types"
|
||||
import {AliasTypeIndicator, QueryResult, SimplePage} from "~/components"
|
||||
import {QueryResult, SimplePage} from "~/components"
|
||||
import {decryptAliasNotes} from "~/utils"
|
||||
import AliasNotesForm from "~/route-widgets/AliasDetailRoute/AliasNotesForm"
|
||||
import AliasPreferencesForm from "~/route-widgets/AliasDetailRoute/AliasPreferencesForm"
|
||||
import AuthContext from "~/AuthContext/AuthContext"
|
||||
import ChangeAliasActivationStatusSwitch from "~/route-widgets/AliasDetailRoute/ChangeAliasActivationStatusSwitch"
|
||||
import DecryptionPasswordMissingAlert from "~/components/DecryptionPasswordMissingAlert"
|
||||
import AliasDetails from "~/route-widgets/AliasDetailRoute/AliasDetails"
|
||||
import AuthContext, {EncryptionStatus} from "~/AuthContext/AuthContext"
|
||||
|
||||
export default function AliasDetailRoute(): ReactElement {
|
||||
const params = useParams()
|
||||
const {user, _decryptUsingMasterPassword} = useContext(AuthContext)
|
||||
const address = atob(params.addressInBase64 as string)
|
||||
const {user, _decryptUsingMasterPassword, encryptionStatus} =
|
||||
useContext(AuthContext)
|
||||
|
||||
const query = useQuery<Alias | DecryptedAlias, AxiosError>(
|
||||
["get_alias", params.addressInBase64],
|
||||
async () => {
|
||||
if (user?.encryptedPassword) {
|
||||
if (encryptionStatus === EncryptionStatus.Available) {
|
||||
return decryptAliasNotes(
|
||||
await getAlias(address),
|
||||
_decryptUsingMasterPassword,
|
||||
@ -37,81 +34,7 @@ export default function AliasDetailRoute(): ReactElement {
|
||||
return (
|
||||
<SimplePage title="Alias Details">
|
||||
<QueryResult<Alias | DecryptedAlias> query={query}>
|
||||
{alias => (
|
||||
<Grid container spacing={4}>
|
||||
<Grid item>
|
||||
<Grid
|
||||
container
|
||||
spacing={1}
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid item>
|
||||
<AliasTypeIndicator type={alias.type} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="subtitle1">
|
||||
{address}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<ChangeAliasActivationStatusSwitch
|
||||
id={alias.id}
|
||||
isActive={alias.isActive}
|
||||
onChanged={query.refetch}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item width="100%">
|
||||
<Grid container direction="column" spacing={4}>
|
||||
<Grid item>
|
||||
<Typography variant="h6" component="h3">
|
||||
Notes
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
{user?.encryptedPassword &&
|
||||
(alias as DecryptedAlias).notes ? (
|
||||
<AliasNotesForm
|
||||
id={alias.id}
|
||||
notes={
|
||||
(alias as DecryptedAlias).notes
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<DecryptionPasswordMissingAlert />
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Grid container spacing={4}>
|
||||
<Grid item>
|
||||
<Typography variant="h6" component="h3">
|
||||
Settings
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="body1">
|
||||
These settings apply to this alias only.
|
||||
You can either set a value manually or
|
||||
refer to your defaults settings. Note
|
||||
that this does change in behavior. When
|
||||
you set a value to refer to your default
|
||||
setting, the alias will always use the
|
||||
latest value. So when you change your
|
||||
default setting, the alias will
|
||||
automatically use the new value.
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<AliasPreferencesForm alias={alias} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
{alias => <AliasDetails alias={alias} />}
|
||||
</QueryResult>
|
||||
</SimplePage>
|
||||
)
|
||||
|
@ -1,13 +1,14 @@
|
||||
import * as yup from "yup"
|
||||
import {ReactElement, useContext} from "react"
|
||||
import {useNavigate} from "react-router-dom"
|
||||
import {useLocation, useNavigate} from "react-router-dom"
|
||||
import {useFormik} from "formik"
|
||||
import {MdLock} from "react-icons/md"
|
||||
|
||||
import {InputAdornment} from "@mui/material"
|
||||
|
||||
import {buildEncryptionPassword} from "~/utils"
|
||||
import {useUser} from "~/hooks"
|
||||
import {PasswordField, SimpleForm} from "~/components"
|
||||
import {InputAdornment} from "@mui/material"
|
||||
import {MdLock} from "react-icons/md"
|
||||
import AuthContext from "~/AuthContext/AuthContext"
|
||||
|
||||
interface Form {
|
||||
@ -20,6 +21,7 @@ const schema = yup.object().shape({
|
||||
|
||||
export default function EnterDecryptionPassword(): ReactElement {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const user = useUser()
|
||||
const {_setDecryptionPassword} = useContext(AuthContext)
|
||||
|
||||
@ -37,7 +39,9 @@ export default function EnterDecryptionPassword(): ReactElement {
|
||||
if (!_setDecryptionPassword(decryptionPassword)) {
|
||||
setErrors({password: "Password is invalid."})
|
||||
} else {
|
||||
navigate("/")
|
||||
const nextUrl =
|
||||
new URLSearchParams(location.search).get("next") || "/"
|
||||
navigate(nextUrl)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user