@@ -102,11 +102,11 @@ const ResendVerificationForm = () => {
102102
103103
104104const ActionHandlerContent : React . FC = ( ) => {
105+ // All hooks at the very top
105106 const router = useRouter ( ) ;
106107 const searchParams = useSearchParams ( ) ;
107108 const { toast } = useToast ( ) ;
108109 const { auth } = useFirebaseAuth ( ) ;
109-
110110 const [ mode , setMode ] = React . useState < Action > ( null ) ;
111111 const [ actionCode , setActionCode ] = React . useState < string | null > ( null ) ;
112112 const [ loading , setLoading ] = React . useState ( true ) ;
@@ -115,24 +115,29 @@ const ActionHandlerContent: React.FC = () => {
115115 const [ info , setInfo ] = React . useState < { email : string ; from : 'verifyEmail' | 'resetPassword' } | null > ( null ) ;
116116 const [ success , setSuccess ] = React . useState ( false ) ;
117117 const [ redirecting , setRedirecting ] = React . useState ( false ) ;
118- // Moved up: fallback email verification check states
119- const [ showCheckVerification , setShowCheckVerification ] = React . useState ( false ) ;
118+ const [ showVerifiedSuccess , setShowVerifiedSuccess ] = React . useState ( false ) ;
120119 const [ checkEmail , setCheckEmail ] = React . useState ( "" ) ;
121120 const [ checking , setChecking ] = React . useState ( false ) ;
122121 const [ checkResult , setCheckResult ] = React . useState < null | "verified" | "not_verified" | "error" > ( null ) ;
123122 const [ checkError , setCheckError ] = React . useState ( "" ) ;
124-
125123 const form = useForm < PasswordResetValues > ( {
126124 resolver : zodResolver ( passwordResetSchema ) ,
127125 } ) ;
128-
126+ // Move handlePasswordResetSubmit to the top level
127+ const handlePasswordResetSubmit = async ( data : PasswordResetValues ) => {
128+ if ( ! auth || ! actionCode ) return ;
129+ try {
130+ await confirmPasswordReset ( auth , actionCode , data . password ) ;
131+ setSuccess ( true ) ;
132+ } catch ( err ) {
133+ setError ( "Failed to reset password. The link may have expired. Please try again." ) ;
134+ }
135+ } ;
129136 React . useEffect ( ( ) => {
130137 const modeParam = searchParams . getAll ( 'mode' ) [ 0 ] as Action ; // Always get the first mode param
131138 const codeParam = searchParams . get ( 'oobCode' ) ;
132-
133139 setMode ( modeParam ) ;
134140 setActionCode ( codeParam ) ;
135-
136141 if ( ! auth || ! modeParam || ! codeParam ) {
137142 if ( ! auth ) setLoading ( false ) ;
138143 if ( ! modeParam || ! codeParam ) {
@@ -141,17 +146,14 @@ const ActionHandlerContent: React.FC = () => {
141146 }
142147 return ;
143148 }
144-
145149 const handleAction = async ( ) => {
146150 try {
147151 const actionInfo = await checkActionCode ( auth , codeParam ) ;
148152 const { operation } = actionInfo ;
149153 const { email } = actionInfo . data ;
150-
151154 if ( ! email ) {
152155 throw new Error ( "Invalid action code: email is missing." ) ;
153156 }
154-
155157 if ( operation === 'VERIFY_EMAIL' ) {
156158 await applyActionCode ( auth , codeParam ) ;
157159 setSuccess ( true ) ;
@@ -172,54 +174,8 @@ const ActionHandlerContent: React.FC = () => {
172174 setLoading ( false ) ;
173175 }
174176 } ;
175-
176177 handleAction ( ) ;
177-
178178 } , [ searchParams , auth ] ) ;
179-
180- const handlePasswordResetSubmit = async ( data : PasswordResetValues ) => {
181- if ( ! auth || ! actionCode ) return ;
182-
183- try {
184- await confirmPasswordReset ( auth , actionCode , data . password ) ;
185- setSuccess ( true ) ;
186- } catch ( err ) {
187- setError ( "Failed to reset password. The link may have expired. Please try again." ) ;
188- }
189- }
190-
191- if ( loading ) {
192- return (
193- < div className = "text-center" >
194- < Loader2 className = "h-12 w-12 animate-spin text-primary mx-auto mb-4" />
195- < p className = "text-muted-foreground" > Verifying link...</ p >
196- </ div >
197- ) ;
198- }
199-
200- if ( showResendForm ) {
201- return < ResendVerificationForm /> ;
202- }
203-
204- if ( error ) {
205- return (
206- < div className = "text-center" >
207- < XCircle className = "h-12 w-12 text-destructive mx-auto mb-4" />
208- < h2 className = "text-2xl font-bold mb-2" > Action Failed</ h2 >
209- < p className = "text-muted-foreground mb-6" > { error } </ p >
210- < div className = "flex flex-col gap-2 items-center" >
211- < Button onClick = { ( ) => router . push ( '/' ) } variant = "secondary" > Go to Homepage</ Button >
212- < Button onClick = { ( ) => router . push ( '/?action=login' ) } variant = "default" > Go to Login</ Button >
213- < Button onClick = { ( ) => setShowResendForm ( true ) } variant = "outline" > Resend Verification Email</ Button >
214- </ div >
215- </ div >
216- ) ;
217- }
218-
219- // Add a new state to track if we should show the verified success UI
220- const [ showVerifiedSuccess , setShowVerifiedSuccess ] = React . useState ( false ) ;
221-
222- // Move the async redirect logic into useEffect
223179 React . useEffect ( ( ) => {
224180 if ( success && info ?. from === 'verifyEmail' ) {
225181 const token = typeof window !== 'undefined' ? localStorage . getItem ( 'token' ) : null ;
@@ -262,13 +218,36 @@ const ActionHandlerContent: React.FC = () => {
262218 }
263219 } ) ( ) ;
264220 } else {
265- // No token, just show the verified success UI
266221 setShowVerifiedSuccess ( true ) ;
267222 }
268223 }
269224 } , [ success , info , router , toast ] ) ;
270-
271- // In render, show the verified success UI if set
225+ // All hooks above, now logic and returns below
226+ if ( loading ) {
227+ return (
228+ < div className = "text-center" >
229+ < Loader2 className = "h-12 w-12 animate-spin text-primary mx-auto mb-4" />
230+ < p className = "text-muted-foreground" > Verifying link...</ p >
231+ </ div >
232+ ) ;
233+ }
234+ if ( showResendForm ) {
235+ return < ResendVerificationForm /> ;
236+ }
237+ if ( error ) {
238+ return (
239+ < div className = "text-center" >
240+ < XCircle className = "h-12 w-12 text-destructive mx-auto mb-4" />
241+ < h2 className = "text-2xl font-bold mb-2" > Action Failed</ h2 >
242+ < p className = "text-muted-foreground mb-6" > { error } </ p >
243+ < div className = "flex flex-col gap-2 items-center" >
244+ < Button onClick = { ( ) => router . push ( '/' ) } variant = "secondary" > Go to Homepage</ Button >
245+ < Button onClick = { ( ) => router . push ( '/?action=login' ) } variant = "default" > Go to Login</ Button >
246+ < Button onClick = { ( ) => setShowResendForm ( true ) } variant = "outline" > Resend Verification Email</ Button >
247+ </ div >
248+ </ div >
249+ ) ;
250+ }
272251 if ( showVerifiedSuccess ) {
273252 return (
274253 < div className = "text-center" >
@@ -279,7 +258,6 @@ const ActionHandlerContent: React.FC = () => {
279258 </ div >
280259 ) ;
281260 }
282-
283261 if ( mode === 'resetPassword' && info ) {
284262 return (
285263 < div >
@@ -322,7 +300,6 @@ const ActionHandlerContent: React.FC = () => {
322300 </ div >
323301 ) ;
324302 }
325-
326303 // Fallback for any other state
327304 return (
328305 < div className = "text-center" >
0 commit comments