mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-19 07:55:25 +02:00
improved extension integration
This commit is contained in:
parent
cf21360509
commit
35e4630b51
@ -4,6 +4,7 @@ import {useEvent} from "react-use"
|
|||||||
|
|
||||||
import {ExtensionKleckEvent} from "~/extension-types"
|
import {ExtensionKleckEvent} from "~/extension-types"
|
||||||
import {User} from "~/server-types"
|
import {User} from "~/server-types"
|
||||||
|
import {AUTHENTICATION_PATHS} from "~/constants/values"
|
||||||
|
|
||||||
export interface UseExtensionHandlerResult {
|
export interface UseExtensionHandlerResult {
|
||||||
sharePassword: () => void
|
sharePassword: () => void
|
||||||
@ -69,7 +70,10 @@ export default function useExtensionHandler(
|
|||||||
)
|
)
|
||||||
break
|
break
|
||||||
case "enter-password":
|
case "enter-password":
|
||||||
if ($enterPasswordAmount.current < 1) {
|
if (
|
||||||
|
$enterPasswordAmount.current < 1 &&
|
||||||
|
!AUTHENTICATION_PATHS.includes(location.pathname)
|
||||||
|
) {
|
||||||
$enterPasswordAmount.current += 1
|
$enterPasswordAmount.current += 1
|
||||||
|
|
||||||
navigate("/enter-password")
|
navigate("/enter-password")
|
||||||
|
@ -15,3 +15,4 @@ export const DEFAULT_ALIAS_NOTE: AliasNote = {
|
|||||||
}
|
}
|
||||||
export const ERROR_SNACKBAR_SHOW_DURATION = 5000
|
export const ERROR_SNACKBAR_SHOW_DURATION = 5000
|
||||||
export const SUCCESS_SNACKBAR_SHOW_DURATION = 2000
|
export const SUCCESS_SNACKBAR_SHOW_DURATION = 2000
|
||||||
|
export const AUTHENTICATION_PATHS = ["/auth/login", "/auth/signup", "/auth/complete-account"]
|
||||||
|
@ -14,3 +14,5 @@ export * from "./use-error-success-snacks"
|
|||||||
export {default as useErrorSuccessSnacks} from "./use-error-success-snacks"
|
export {default as useErrorSuccessSnacks} from "./use-error-success-snacks"
|
||||||
export * from "./use-is-any-input-focused"
|
export * from "./use-is-any-input-focused"
|
||||||
export {default as useIsAnyInputFocused} from "./use-is-any-input-focused"
|
export {default as useIsAnyInputFocused} from "./use-is-any-input-focused"
|
||||||
|
export * from "./use-extension-handler"
|
||||||
|
export {default as useExtensionHandler} from "./use-extension-handler"
|
||||||
|
23
src/hooks/use-extension-handler.ts
Normal file
23
src/hooks/use-extension-handler.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import {useCallback} from "react"
|
||||||
|
import {useEvent} from "react-use"
|
||||||
|
|
||||||
|
import {ExtensionKleckEvent} from "~/extension-types"
|
||||||
|
|
||||||
|
export interface UseExtensionHandlerData {
|
||||||
|
onEnterPassword?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useExtensionHandler({onEnterPassword}: UseExtensionHandlerData): void {
|
||||||
|
const handleExtensionEvent = useCallback(
|
||||||
|
(event: ExtensionKleckEvent) => {
|
||||||
|
switch (event.detail.type) {
|
||||||
|
case "enter-password":
|
||||||
|
onEnterPassword?.()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onEnterPassword],
|
||||||
|
)
|
||||||
|
|
||||||
|
useEvent("kleckrelay-kleck", handleExtensionEvent)
|
||||||
|
}
|
@ -2,14 +2,9 @@ import {useLocation, useNavigate} from "react-router-dom"
|
|||||||
import {useContext, useLayoutEffect} from "react"
|
import {useContext, useLayoutEffect} from "react"
|
||||||
|
|
||||||
import {ServerUser, User} from "~/server-types"
|
import {ServerUser, User} from "~/server-types"
|
||||||
|
import {AUTHENTICATION_PATHS} from "~/constants/values"
|
||||||
import AuthContext from "~/AuthContext/AuthContext"
|
import AuthContext from "~/AuthContext/AuthContext"
|
||||||
|
|
||||||
const AUTHENTICATION_PATHS = [
|
|
||||||
"/auth/login",
|
|
||||||
"/auth/signup",
|
|
||||||
"/auth/complete-account",
|
|
||||||
]
|
|
||||||
|
|
||||||
/// Returns the currently authenticated user.
|
/// Returns the currently authenticated user.
|
||||||
// If the user is not authenticated, it will automatically redirect to the login page.
|
// If the user is not authenticated, it will automatically redirect to the login page.
|
||||||
export default function useUser(): ServerUser | User {
|
export default function useUser(): ServerUser | User {
|
||||||
@ -18,10 +13,7 @@ export default function useUser(): ServerUser | User {
|
|||||||
const {user, isAuthenticated} = useContext(AuthContext)
|
const {user, isAuthenticated} = useContext(AuthContext)
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (
|
if (!isAuthenticated && !AUTHENTICATION_PATHS.includes(location.pathname)) {
|
||||||
!isAuthenticated &&
|
|
||||||
!AUTHENTICATION_PATHS.includes(location.pathname)
|
|
||||||
) {
|
|
||||||
navigate("/auth/login")
|
navigate("/auth/login")
|
||||||
}
|
}
|
||||||
}, [isAuthenticated, navigate])
|
}, [isAuthenticated, navigate])
|
||||||
|
@ -6,13 +6,13 @@ import {AxiosError} from "axios"
|
|||||||
import {useTranslation} from "react-i18next"
|
import {useTranslation} from "react-i18next"
|
||||||
import {Box, InputAdornment} from "@mui/material"
|
import {Box, InputAdornment} from "@mui/material"
|
||||||
import {useMutation} from "@tanstack/react-query"
|
import {useMutation} from "@tanstack/react-query"
|
||||||
import React, {ReactElement, useContext, useMemo} from "react"
|
import React, {ReactElement, useCallback, useContext, useMemo, useRef} from "react"
|
||||||
import passwordGenerator from "secure-random-password"
|
import passwordGenerator from "secure-random-password"
|
||||||
|
|
||||||
import {PasswordField, SimpleForm} 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 {useExtensionHandler, useSystemPreferredTheme, useUser} from "~/hooks"
|
||||||
import {MASTER_PASSWORD_LENGTH} from "~/constants/values"
|
import {MASTER_PASSWORD_LENGTH} from "~/constants/values"
|
||||||
import {AuthenticationDetails, UserNote} from "~/server-types"
|
import {AuthenticationDetails, UserNote} from "~/server-types"
|
||||||
import {UpdateAccountData, updateAccount} from "~/apis"
|
import {UpdateAccountData, updateAccount} from "~/apis"
|
||||||
@ -34,6 +34,8 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
|
|||||||
const user = useUser()
|
const user = useUser()
|
||||||
const theme = useSystemPreferredTheme()
|
const theme = useSystemPreferredTheme()
|
||||||
|
|
||||||
|
const $password = useRef<HTMLInputElement | null>(null)
|
||||||
|
const $passwordConfirmation = useRef<HTMLInputElement | null>(null)
|
||||||
const schema = yup.object().shape({
|
const schema = yup.object().shape({
|
||||||
password: yup.string().required(),
|
password: yup.string().required(),
|
||||||
passwordConfirmation: yup
|
passwordConfirmation: yup
|
||||||
@ -113,6 +115,17 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
const focusPassword = useCallback(() => {
|
||||||
|
if ($password.current?.value !== "") {
|
||||||
|
$passwordConfirmation.current?.focus()
|
||||||
|
} else {
|
||||||
|
$password.current?.focus()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useExtensionHandler({
|
||||||
|
onEnterPassword: focusPassword,
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box maxWidth="80vw">
|
<Box maxWidth="80vw">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as yup from "yup"
|
import * as yup from "yup"
|
||||||
import {ReactElement} from "react"
|
import {ReactElement, useCallback, useRef} from "react"
|
||||||
import {AxiosError} from "axios"
|
import {AxiosError} from "axios"
|
||||||
import {useFormik} from "formik"
|
import {useFormik} from "formik"
|
||||||
import {MdEmail} from "react-icons/md"
|
import {MdEmail} from "react-icons/md"
|
||||||
@ -11,6 +11,7 @@ import {InputAdornment, TextField} from "@mui/material"
|
|||||||
import {LoginWithEmailResult, loginWithEmail} from "~/apis"
|
import {LoginWithEmailResult, loginWithEmail} from "~/apis"
|
||||||
import {parseFastAPIError} from "~/utils"
|
import {parseFastAPIError} from "~/utils"
|
||||||
import {MultiStepFormElement, SimpleForm} from "~/components"
|
import {MultiStepFormElement, SimpleForm} from "~/components"
|
||||||
|
import {useExtensionHandler} from "~/hooks"
|
||||||
|
|
||||||
export interface EmailFormProps {
|
export interface EmailFormProps {
|
||||||
onLogin: (email: string, sameRequestToken: string) => void
|
onLogin: (email: string, sameRequestToken: string) => void
|
||||||
@ -24,6 +25,7 @@ interface Form {
|
|||||||
export default function EmailForm({onLogin}: EmailFormProps): ReactElement {
|
export default function EmailForm({onLogin}: EmailFormProps): ReactElement {
|
||||||
const {t} = useTranslation()
|
const {t} = useTranslation()
|
||||||
|
|
||||||
|
const $password = useRef<HTMLInputElement | null>(null)
|
||||||
const schema = yup.object().shape({
|
const schema = yup.object().shape({
|
||||||
email: yup
|
email: yup
|
||||||
.string()
|
.string()
|
||||||
@ -50,6 +52,12 @@ export default function EmailForm({onLogin}: EmailFormProps): ReactElement {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const focusPassword = useCallback(() => $password.current?.focus(), [])
|
||||||
|
|
||||||
|
useExtensionHandler({
|
||||||
|
onEnterPassword: focusPassword,
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MultiStepFormElement>
|
<MultiStepFormElement>
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
@ -68,6 +76,7 @@ export default function EmailForm({onLogin}: EmailFormProps): ReactElement {
|
|||||||
id="email"
|
id="email"
|
||||||
label="Email"
|
label="Email"
|
||||||
placeholder={t("routes.LoginRoute.forms.email.form.email.placeholder")}
|
placeholder={t("routes.LoginRoute.forms.email.form.email.placeholder")}
|
||||||
|
inputRef={$password}
|
||||||
inputMode="email"
|
inputMode="email"
|
||||||
value={formik.values.email}
|
value={formik.values.email}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
|
@ -3,7 +3,7 @@ import {useFormik} from "formik"
|
|||||||
import {MdEmail} from "react-icons/md"
|
import {MdEmail} from "react-icons/md"
|
||||||
import {AxiosError} from "axios"
|
import {AxiosError} from "axios"
|
||||||
import {useTranslation} from "react-i18next"
|
import {useTranslation} from "react-i18next"
|
||||||
import React, {ReactElement} from "react"
|
import React, {ReactElement, useCallback, useRef} from "react"
|
||||||
|
|
||||||
import {InputAdornment, TextField} from "@mui/material"
|
import {InputAdornment, TextField} from "@mui/material"
|
||||||
import {useMutation} from "@tanstack/react-query"
|
import {useMutation} from "@tanstack/react-query"
|
||||||
@ -13,6 +13,7 @@ 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 {useExtensionHandler} from "~/hooks"
|
||||||
import DetectEmailAutofillService from "./DetectEmailAutofillService"
|
import DetectEmailAutofillService from "./DetectEmailAutofillService"
|
||||||
|
|
||||||
export interface EmailFormProps {
|
export interface EmailFormProps {
|
||||||
@ -28,6 +29,7 @@ interface Form {
|
|||||||
export default function EmailForm({onSignUp, serverSettings}: EmailFormProps): ReactElement {
|
export default function EmailForm({onSignUp, serverSettings}: EmailFormProps): ReactElement {
|
||||||
const {t} = useTranslation()
|
const {t} = useTranslation()
|
||||||
|
|
||||||
|
const $password = useRef<HTMLInputElement | null>(null)
|
||||||
const schema = yup.object().shape({
|
const schema = yup.object().shape({
|
||||||
email: yup
|
email: yup
|
||||||
.string()
|
.string()
|
||||||
@ -69,6 +71,11 @@ export default function EmailForm({onSignUp, serverSettings}: EmailFormProps): R
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
const focusPassword = useCallback(() => $password.current?.focus(), [])
|
||||||
|
|
||||||
|
useExtensionHandler({
|
||||||
|
onEnterPassword: focusPassword,
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -92,6 +99,7 @@ export default function EmailForm({onSignUp, serverSettings}: EmailFormProps): R
|
|||||||
"routes.SignupRoute.forms.email.form.email.placeholder",
|
"routes.SignupRoute.forms.email.form.email.placeholder",
|
||||||
)}
|
)}
|
||||||
inputMode="email"
|
inputMode="email"
|
||||||
|
inputRef={$password}
|
||||||
value={formik.values.email}
|
value={formik.values.email}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
disabled={formik.isSubmitting}
|
disabled={formik.isSubmitting}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user