@@ -19,32 +19,65 @@ import { Input } from "@follow/components/ui/input/index.js"
19
19
import { env } from "@follow/shared/env"
20
20
import { zodResolver } from "@hookform/resolvers/zod"
21
21
import { useMutation } from "@tanstack/react-query"
22
- import { useRef } from "react"
22
+ import { useEffect , useRef } from "react"
23
23
import ReCAPTCHA from "react-google-recaptcha"
24
24
import { useForm } from "react-hook-form"
25
25
import { useTranslation } from "react-i18next"
26
26
import { useNavigate } from "react-router"
27
27
import { toast } from "sonner"
28
28
import { z } from "zod"
29
29
30
- const forgetPasswordFormSchema = z . object ( {
31
- email : z . string ( ) . email ( ) ,
32
- } )
30
+ function closeRecaptcha (
31
+ recaptchaRef : React . RefObject < ReCAPTCHA > ,
32
+ mutation ?: { reset : ( ) => void } ,
33
+ ) {
34
+ const handleClick = ( e : MouseEvent ) => {
35
+ const recaptchaIframeSelector =
36
+ 'iframe[src*="recaptcha/api2"], iframe[src*="www.recaptcha.net"], iframe[src*="google.com/recaptcha"]'
37
+ const recaptchaChallengeIframe = document . querySelector ( recaptchaIframeSelector )
38
+
39
+ if (
40
+ e . target instanceof Element &&
41
+ ( ! recaptchaChallengeIframe || ! recaptchaChallengeIframe . contains ( e . target ) ) &&
42
+ ! e . target . closest ( ".g-recaptcha" ) &&
43
+ recaptchaChallengeIframe
44
+ ) {
45
+ recaptchaRef . current ?. reset ( )
46
+ mutation ?. reset ( )
47
+ }
48
+ }
49
+
50
+ document . addEventListener ( "click" , handleClick )
51
+ return ( ) => document . removeEventListener ( "click" , handleClick )
52
+ }
53
+
54
+ const createEmailSchema = ( t : any ) =>
55
+ z . object ( {
56
+ email : z
57
+ . string ( )
58
+ . min ( 1 , t ( "login.forget_password.email_required" ) )
59
+ . email ( t ( "login.forget_password.email_invalid" ) ) ,
60
+ } )
33
61
34
62
export function Component ( ) {
35
63
const { t } = useTranslation ( )
36
- const form = useForm < z . infer < typeof forgetPasswordFormSchema > > ( {
37
- resolver : zodResolver ( forgetPasswordFormSchema ) ,
64
+ const navigate = useNavigate ( )
65
+ const recaptchaRef = useRef < ReCAPTCHA > ( null )
66
+
67
+ const EmailSchema = createEmailSchema ( t )
68
+
69
+ const form = useForm < z . infer < typeof EmailSchema > > ( {
70
+ resolver : zodResolver ( EmailSchema ) ,
38
71
defaultValues : {
39
72
email : "" ,
40
73
} ,
74
+ mode : "onChange" ,
75
+ delayError : 500 ,
41
76
} )
42
77
43
- const recaptchaRef = useRef < ReCAPTCHA > ( null )
44
-
45
78
const { isValid } = form . formState
46
79
const updateMutation = useMutation ( {
47
- mutationFn : async ( values : z . infer < typeof forgetPasswordFormSchema > ) => {
80
+ mutationFn : async ( values : z . infer < typeof EmailSchema > ) => {
48
81
const token = await recaptchaRef . current ?. executeAsync ( )
49
82
const res = await forgetPassword (
50
83
{
@@ -62,19 +95,25 @@ export function Component() {
62
95
}
63
96
} ,
64
97
onError : ( error ) => {
98
+ recaptchaRef . current ?. reset ( )
65
99
toast . error ( error . message )
66
100
} ,
67
101
onSuccess : ( ) => {
68
102
toast . success ( t ( "login.forget_password.success" ) )
69
103
} ,
104
+ onSettled : ( ) => {
105
+ recaptchaRef . current ?. reset ( )
106
+ } ,
70
107
} )
71
108
72
- function onSubmit ( values : z . infer < typeof forgetPasswordFormSchema > ) {
109
+ useEffect ( ( ) => {
110
+ return closeRecaptcha ( recaptchaRef , updateMutation )
111
+ } , [ updateMutation ] )
112
+
113
+ function onSubmit ( values : z . infer < typeof EmailSchema > ) {
73
114
updateMutation . mutate ( values )
74
115
}
75
116
76
- const navigate = useNavigate ( )
77
-
78
117
return (
79
118
< div className = "flex h-full items-center justify-center" >
80
119
< Card className = "w-[350px] max-w-full" >
@@ -116,7 +155,11 @@ export function Component() {
116
155
size = "invisible"
117
156
/>
118
157
< div className = "text-right" >
119
- < Button disabled = { ! isValid } type = "submit" isLoading = { updateMutation . isPending } >
158
+ < Button
159
+ disabled = { ! isValid || updateMutation . isPending }
160
+ type = "submit"
161
+ isLoading = { updateMutation . isPending }
162
+ >
120
163
{ t ( "login.submit" ) }
121
164
</ Button >
122
165
</ div >
0 commit comments