1616
1717"use client" ;
1818
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" ;
3225
3326export type EmailLinkAuthFormProps = {
3427 onEmailSent ?: ( ) => void ;
28+ onSignIn ?: ( credential : UserCredential ) => void ;
3529} ;
3630
37- export function EmailLinkAuthForm ( { onEmailSent } : EmailLinkAuthFormProps ) {
31+ export function useEmailLinkAuthFormAction ( ) {
3832 const ui = useUI ( ) ;
3933
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 } ) => {
5636 try {
57- await sendSignInLinkToEmail ( ui , value . email ) ;
58- setEmailSent ( true ) ;
59- onEmailSent ?.( ) ;
37+ return await sendSignInLinkToEmail ( ui , email ) ;
6038 } catch ( error ) {
6139 if ( error instanceof FirebaseUIError ) {
62- setFormError ( error . message ) ;
63- return ;
40+ throw new Error ( error . message ) ;
6441 }
6542
6643 console . error ( error ) ;
67- setFormError ( getTranslation ( ui , "errors" , "unknownError" ) ) ;
44+ throw new Error ( getTranslation ( ui , "errors" , "unknownError" ) ) ;
6845 }
6946 } ,
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+ } ,
7071 } ) ;
72+ }
73+
74+ export function useEmailLinkAuthFormCompleteSignIn ( onSignIn ?: EmailLinkAuthFormProps [ "onSignIn" ] ) {
75+ const ui = useUI ( ) ;
7176
72- // Handle email link sign-in if URL contains the link
7377 useEffect ( ( ) => {
7478 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 ) ;
8183 }
8284 } ;
8385
8486 void completeSignIn ( ) ;
8587 } , [ 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 ) ;
86100
87101 if ( emailSent ) {
88102 return < div className = "fui-success" > { getTranslation ( ui , "messages" , "signInLinkSent" ) } </ div > ;
@@ -97,47 +111,16 @@ export function EmailLinkAuthForm({ onEmailSent }: EmailLinkAuthFormProps) {
97111 await form . handleSubmit ( ) ;
98112 } }
99113 >
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 >
141124 </ form >
142125 ) ;
143126}
0 commit comments