@@ -16,6 +16,7 @@ import {
1616 FormMessage ,
1717} from '~/components/ui/form' ;
1818import { Input } from '~/components/ui/input' ;
19+ import { LoadingSpinner } from '~/components/ui/spinner' ;
1920import { LanguageSelector } from '~/components/LanguageSelector' ;
2021import { env } from '~/env' ;
2122import { getServerAuthSession } from '~/server/auth' ;
@@ -53,10 +54,12 @@ const Home: NextPage<{
5354 feedbackEmail : string ;
5455 providers : ClientSafeProvider [ ] ;
5556 callbackUrl ?: string ;
56- } > = ( { error, providers, feedbackEmail, callbackUrl } ) => {
57+ } > = ( { error, providers : serverProviders , feedbackEmail, callbackUrl } ) => {
5758 const { t } = useTranslation ( 'signin' ) ;
5859 const [ emailStatus , setEmailStatus ] = useState < 'idle' | 'sending' | 'success' > ( 'idle' ) ;
5960 const [ showVerificationStep , setShowVerificationStep ] = useState ( false ) ;
61+ const [ providers , setProviders ] = useState < ClientSafeProvider [ ] > ( serverProviders ) ;
62+ const [ isLoadingProviders , setIsLoadingProviders ] = useState ( false ) ;
6063
6164 const emailForm = useForm < EmailFormValues > ( {
6265 resolver : zodResolver ( emailSchema ( t ) ) ,
@@ -65,6 +68,30 @@ const Home: NextPage<{
6568 } ,
6669 } ) ;
6770
71+ // Client-side fallback for getProviders when server-side call fails
72+ useEffect ( ( ) => {
73+ if ( serverProviders . length > 0 ) {
74+ return ;
75+ }
76+
77+ void ( async ( ) => {
78+ setIsLoadingProviders ( true ) ;
79+ try {
80+ const clientProviders = await getProviders ( ) ;
81+ if ( clientProviders && Object . keys ( clientProviders ) . length > 0 ) {
82+ setProviders ( Object . values ( clientProviders ) ) ;
83+ } else {
84+ throw new Error ( 'No providers returned from getProviders()' ) ;
85+ }
86+ } catch ( error ) {
87+ console . error ( 'Error fetching providers client-side:' , error ) ;
88+ toast . error ( t ( 'errors.no_providers' ) , { duration : 8000 } ) ;
89+ } finally {
90+ setIsLoadingProviders ( false ) ;
91+ }
92+ } ) ( ) ;
93+ } , [ serverProviders . length , t ] ) ;
94+
6895 useEffect ( ( ) => {
6996 if ( error ) {
7097 if ( 'SignupDisabled' === error ) {
@@ -145,51 +172,62 @@ const Home: NextPage<{
145172 < LanguageSelector />
146173 </ div >
147174
148- { providers
149- . filter ( ( provider ) => 'email' !== provider . id )
150- . map ( ( provider ) => (
151- < Button
152- className = "mx-auto flex w-[300px] items-center gap-3 bg-white hover:bg-gray-100 focus:bg-gray-100"
153- onClick = { handleProviderSignIn ( provider . id ) }
154- key = { provider . id }
155- >
156- { providerSvgs [ provider . id as keyof typeof providerSvgs ] }
157- { t ( 'auth.continue_with' , { provider : provider . name } ) }
158- </ Button >
159- ) ) }
160- { providers && 2 === providers . length && (
161- < div className = "mt-6 flex w-[300px] items-center justify-between gap-2" >
162- < p className = "bg-background z-10 ml-[150px] -translate-x-1/2 px-4 text-sm" >
163- { t ( 'ui.or' , { ns : 'common' } ) }
164- </ p >
165- < div className = "absolute h-px w-[300px] bg-linear-to-r from-zinc-800 via-zinc-300 to-zinc-800" />
175+ { isLoadingProviders ? (
176+ < div className = "flex h-[200px] w-[300px] items-center justify-center" >
177+ < LoadingSpinner className = "h-8 w-8" />
166178 </ div >
167- ) }
168- { providers . find ( ( provider ) => 'email' === provider . id ) ? (
179+ ) : (
169180 < >
170- < Form { ... emailForm } >
171- < form onSubmit = { emailForm . handleSubmit ( onEmailSubmit ) } className = "mt-6 space-y-8" >
172- < FormField control = { emailForm . control } name = "email" render = { field } />
181+ { providers
182+ . filter ( ( provider ) => 'email' !== provider . id )
183+ . map ( ( provider ) => (
173184 < Button
174- className = "mt-6 w-[300px] bg-white hover:bg-gray-100 focus:bg-gray-100"
175- type = "submit"
176- disabled = { 'sending' === emailStatus }
185+ className = "mx-auto flex w-[300px] items-center gap-3 bg-white hover:bg-gray-100 focus:bg-gray-100"
186+ onClick = { handleProviderSignIn ( provider . id ) }
187+ key = { provider . id }
177188 >
178- { 'sending' === emailStatus ? t ( 'auth.sending' ) : t ( 'auth.send_magic_link' ) }
189+ { providerSvgs [ provider . id as keyof typeof providerSvgs ] }
190+ { t ( 'auth.continue_with' , { provider : provider . name } ) }
179191 </ Button >
180- </ form >
181- </ Form >
192+ ) ) }
193+ { providers && 2 === providers . length && (
194+ < div className = "mt-6 flex w-[300px] items-center justify-between gap-2" >
195+ < p className = "bg-background z-10 ml-[150px] -translate-x-1/2 px-4 text-sm" >
196+ { t ( 'ui.or' , { ns : 'common' } ) }
197+ </ p >
198+ < div className = "absolute h-px w-[300px] bg-linear-to-r from-zinc-800 via-zinc-300 to-zinc-800" />
199+ </ div >
200+ ) }
201+ { providers . find ( ( provider ) => 'email' === provider . id ) ? (
202+ < >
203+ < Form { ...emailForm } >
204+ < form
205+ onSubmit = { emailForm . handleSubmit ( onEmailSubmit ) }
206+ className = "mt-6 space-y-8"
207+ >
208+ < FormField control = { emailForm . control } name = "email" render = { field } />
209+ < Button
210+ className = "mt-6 w-[300px] bg-white hover:bg-gray-100 focus:bg-gray-100"
211+ type = "submit"
212+ disabled = { 'sending' === emailStatus }
213+ >
214+ { 'sending' === emailStatus ? t ( 'auth.sending' ) : t ( 'auth.send_magic_link' ) }
215+ </ Button >
216+ </ form >
217+ </ Form >
218+ </ >
219+ ) : null }
220+ { feedbackEmail && (
221+ < p className = "text-muted-foreground mt-6 w-[300px] text-center text-sm" >
222+ { t ( 'auth.trouble_logging_in' ) }
223+ < br />
224+ { /* oxlint-disable-next-line next/no-html-link-for-pages */ }
225+ < a className = "underline" href = { feedbackEmailLink } >
226+ { feedbackEmail ?? '' }
227+ </ a >
228+ </ p >
229+ ) }
182230 </ >
183- ) : null }
184- { feedbackEmail && (
185- < p className = "text-muted-foreground mt-6 w-[300px] text-center text-sm" >
186- { t ( 'auth.trouble_logging_in' ) }
187- < br />
188- { /* oxlint-disable-next-line next/no-html-link-for-pages */ }
189- < a className = "underline" href = { feedbackEmailLink } >
190- { feedbackEmail ?? '' }
191- </ a >
192- </ p >
193231 ) }
194232 </ div >
195233 </ main >
@@ -201,7 +239,12 @@ export default Home;
201239
202240export const getServerSideProps : GetServerSideProps = async ( context ) => {
203241 const session = await getServerAuthSession ( context ) ;
204- const providers = await getProviders ( ) ;
242+ let providers : Record < string , ClientSafeProvider > | null = null ;
243+ try {
244+ providers = await getProviders ( ) ;
245+ } catch ( error ) {
246+ console . error ( error ) ;
247+ }
205248 const { callbackUrl, error } = context . query ;
206249
207250 if ( session ) {
0 commit comments