16
16
17
17
"use client" ;
18
18
19
- import {
20
- FirebaseUIError ,
21
- completeEmailLinkSignIn ,
22
- createEmailLinkAuthFormSchema ,
23
- getTranslation ,
24
- sendSignInLinkToEmail ,
25
- } from "@firebase-ui/core" ;
26
- import { useForm } from "@tanstack/react-form" ;
27
- import { useEffect , useMemo , useState } from "react" ;
28
- import { useUI } from "~/hooks" ;
29
- import { Button } from "../../components/button" ;
30
- import { FieldInfo } from "../../components/field-info" ;
31
- import { Policies } from "../../components/policies" ;
19
+ import { FirebaseUIError , completeEmailLinkSignIn , getTranslation , sendSignInLinkToEmail } from "@firebase-ui/core" ;
20
+ import type { UserCredential } from "firebase/auth" ;
21
+ import { useEmailLinkAuthFormSchema , useUI } from "~/hooks" ;
22
+ import { form } from "~/components/form" ;
23
+ import { Policies } from "~/components/policies" ;
24
+ import { useCallback , useEffect , useState } from "react" ;
32
25
33
26
export type EmailLinkAuthFormProps = {
34
27
onEmailSent ?: ( ) => void ;
28
+ onSignIn ?: ( credential : UserCredential ) => void ;
35
29
} ;
36
30
37
- export function EmailLinkAuthForm ( { onEmailSent } : EmailLinkAuthFormProps ) {
31
+ export function useEmailLinkAuthFormAction ( ) {
38
32
const ui = useUI ( ) ;
39
33
40
- const [ formError , setFormError ] = useState < string | null > ( null ) ;
41
- const [ emailSent , setEmailSent ] = useState ( false ) ;
42
- const [ firstValidationOccured , setFirstValidationOccured ] = useState ( false ) ;
43
-
44
- const emailLinkFormSchema = useMemo ( ( ) => createEmailLinkAuthFormSchema ( ui ) , [ ui ] ) ;
45
-
46
- const form = useForm ( {
47
- defaultValues : {
48
- email : "" ,
49
- } ,
50
- validators : {
51
- onBlur : emailLinkFormSchema ,
52
- onSubmit : emailLinkFormSchema ,
53
- } ,
54
- onSubmit : async ( { value } ) => {
55
- setFormError ( null ) ;
34
+ return useCallback (
35
+ async ( { email } : { email : string } ) => {
56
36
try {
57
- await sendSignInLinkToEmail ( ui , value . email ) ;
58
- setEmailSent ( true ) ;
59
- onEmailSent ?.( ) ;
37
+ return await sendSignInLinkToEmail ( ui , email ) ;
60
38
} catch ( error ) {
61
39
if ( error instanceof FirebaseUIError ) {
62
- setFormError ( error . message ) ;
63
- return ;
40
+ throw new Error ( error . message ) ;
64
41
}
65
42
66
43
console . error ( error ) ;
67
- setFormError ( getTranslation ( ui , "errors" , "unknownError" ) ) ;
44
+ throw new Error ( getTranslation ( ui , "errors" , "unknownError" ) ) ;
68
45
}
69
46
} ,
47
+ [ ui ]
48
+ ) ;
49
+ }
50
+
51
+ export function useEmailLinkAuthForm ( onSuccess ?: EmailLinkAuthFormProps [ "onEmailSent" ] ) {
52
+ const schema = useEmailLinkAuthFormSchema ( ) ;
53
+ const action = useEmailLinkAuthFormAction ( ) ;
54
+
55
+ return form . useAppForm ( {
56
+ defaultValues : {
57
+ email : "" ,
58
+ } ,
59
+ validators : {
60
+ onBlur : schema ,
61
+ onSubmit : schema ,
62
+ onSubmitAsync : async ( { value } ) => {
63
+ try {
64
+ await action ( value ) ;
65
+ onSuccess ?.( ) ;
66
+ } catch ( error ) {
67
+ return error instanceof Error ? error . message : String ( error ) ;
68
+ }
69
+ } ,
70
+ } ,
70
71
} ) ;
72
+ }
73
+
74
+ export function useEmailLinkAuthFormCompleteSignIn ( onSignIn ?: EmailLinkAuthFormProps [ "onSignIn" ] ) {
75
+ const ui = useUI ( ) ;
71
76
72
- // Handle email link sign-in if URL contains the link
73
77
useEffect ( ( ) => {
74
78
const completeSignIn = async ( ) => {
75
- try {
76
- await completeEmailLinkSignIn ( ui , window . location . href ) ;
77
- } catch ( error ) {
78
- if ( error instanceof FirebaseUIError ) {
79
- setFormError ( error . message ) ;
80
- }
79
+ const credential = await completeEmailLinkSignIn ( ui , window . location . href ) ;
80
+
81
+ if ( credential ) {
82
+ onSignIn ?.( credential ) ;
81
83
}
82
84
} ;
83
85
84
86
void completeSignIn ( ) ;
85
87
} , [ ui ] ) ;
88
+ }
89
+
90
+ export function EmailLinkAuthForm ( { onEmailSent, onSignIn } : EmailLinkAuthFormProps ) {
91
+ const ui = useUI ( ) ;
92
+ const [ emailSent , setEmailSent ] = useState ( false ) ;
93
+
94
+ const form = useEmailLinkAuthForm ( ( ) => {
95
+ setEmailSent ( true ) ;
96
+ onEmailSent ?.( ) ;
97
+ } ) ;
98
+
99
+ useEmailLinkAuthFormCompleteSignIn ( onSignIn ) ;
86
100
87
101
if ( emailSent ) {
88
102
return < div className = "fui-success" > { getTranslation ( ui , "messages" , "signInLinkSent" ) } </ div > ;
@@ -97,47 +111,16 @@ export function EmailLinkAuthForm({ onEmailSent }: EmailLinkAuthFormProps) {
97
111
await form . handleSubmit ( ) ;
98
112
} }
99
113
>
100
- < fieldset >
101
- < form . Field
102
- name = "email"
103
- // eslint-disable-next-line react/no-children-prop
104
- children = { ( field ) => (
105
- < >
106
- < label htmlFor = { field . name } >
107
- < span > { getTranslation ( ui , "labels" , "emailAddress" ) } </ span >
108
- < input
109
- aria-invalid = { field . state . meta . isTouched && field . state . meta . errors . length > 0 }
110
- id = { field . name }
111
- name = { field . name }
112
- type = "email"
113
- value = { field . state . value }
114
- onBlur = { ( ) => {
115
- setFirstValidationOccured ( true ) ;
116
- field . handleBlur ( ) ;
117
- } }
118
- onInput = { ( e ) => {
119
- field . handleChange ( ( e . target as HTMLInputElement ) . value ) ;
120
- if ( firstValidationOccured ) {
121
- field . handleBlur ( ) ;
122
- form . update ( ) ;
123
- }
124
- } }
125
- />
126
- < FieldInfo field = { field } />
127
- </ label >
128
- </ >
129
- ) }
130
- />
131
- </ fieldset >
132
-
133
- < Policies />
134
-
135
- < fieldset >
136
- < Button type = "submit" disabled = { ui . state !== "idle" } >
137
- { getTranslation ( ui , "labels" , "sendSignInLink" ) }
138
- </ Button >
139
- { formError && < div className = "fui-form__error" > { formError } </ div > }
140
- </ fieldset >
114
+ < form . AppForm >
115
+ < fieldset >
116
+ < form . AppField name = "email" children = { ( field ) => < field . Input label = "Email" type = "email" /> } />
117
+ </ fieldset >
118
+ < Policies />
119
+ < fieldset >
120
+ < form . SubmitButton > { getTranslation ( ui , "labels" , "sendSignInLink" ) } </ form . SubmitButton >
121
+ < form . ErrorMessage />
122
+ </ fieldset >
123
+ </ form . AppForm >
141
124
</ form >
142
125
) ;
143
126
}
0 commit comments