diff --git a/next.config.js b/next.config.js index 3741a82..3b3f69c 100644 --- a/next.config.js +++ b/next.config.js @@ -1,7 +1,7 @@ /** -* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful -* for Docker builds. -*/ + * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful + * for Docker builds. + */ import './src/env.js'; import { withSentryConfig } from '@sentry/nextjs'; diff --git a/src/app/auth/confirm/route.ts b/src/app/auth/confirm/route.ts index f1a1864..6d8c73d 100644 --- a/src/app/auth/confirm/route.ts +++ b/src/app/auth/confirm/route.ts @@ -1,35 +1,35 @@ -import { type EmailOtpType } from '@supabase/supabase-js' -import { type NextRequest, NextResponse } from 'next/server' +import { type EmailOtpType } from '@supabase/supabase-js'; +import { type NextRequest, NextResponse } from 'next/server'; -import { createClient } from '@/utils/supabase/server' +import { createClient } from '@/utils/supabase/server'; // Creating a handler to a GET request to route /auth/confirm export const GET = async (request: NextRequest) => { - const { searchParams } = new URL(request.url) - const token_hash = searchParams.get('token_hash') - const type = searchParams.get('type') as EmailOtpType | null - const next = '/account' + const { searchParams } = new URL(request.url); + const token_hash = searchParams.get('token_hash'); + const type = searchParams.get('type') as EmailOtpType | null; + const next = '/account'; // Create redirect link without the secret token - const redirectTo = request.nextUrl.clone() - redirectTo.pathname = next - redirectTo.searchParams.delete('token_hash') - redirectTo.searchParams.delete('type') + const redirectTo = request.nextUrl.clone(); + redirectTo.pathname = next; + redirectTo.searchParams.delete('token_hash'); + redirectTo.searchParams.delete('type'); if (token_hash && type) { - const supabase = await createClient() + const supabase = await createClient(); const { error } = await supabase.auth.verifyOtp({ type, token_hash, - }) + }); if (!error) { - redirectTo.searchParams.delete('next') - return NextResponse.redirect(redirectTo) + redirectTo.searchParams.delete('next'); + return NextResponse.redirect(redirectTo); } } // return the user to an error page with some instructions - redirectTo.pathname = '/error' - return NextResponse.redirect(redirectTo) -} + redirectTo.pathname = '/error'; + return NextResponse.redirect(redirectTo); +}; diff --git a/src/app/error/page.tsx b/src/app/error/page.tsx index 7e8af33..1c5cf6f 100644 --- a/src/app/error/page.tsx +++ b/src/app/error/page.tsx @@ -1,4 +1,4 @@ const ErrorPage = () => { - return

Sorry, something went wrong

-} + return

Sorry, something went wrong

; +}; export default ErrorPage; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f57a61a..e9d027d 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -3,6 +3,9 @@ import { GeistSans } from 'geist/font/sans'; import { cn } from '@/lib/utils'; import { ThemeProvider } from '@/components/context/Theme'; import { TVModeProvider } from '@/components/context/TVMode'; +import { createClient } from '@/utils/supabase/server'; +import LoginForm from '@/components/auth/LoginForm'; +import Header from '@/components/defaults/Header'; import { type Metadata } from 'next'; export const metadata: Metadata = { @@ -28,20 +31,14 @@ export const metadata: Metadata = { ], }; -const RootLayout = ({ - children, -}: Readonly<{ children: React.ReactNode }>) => { +const RootLayout = async ({ children }: Readonly<{ children: React.ReactNode }>) => { return ( - + - {children} +
+
+ {children} +
diff --git a/src/app/page.tsx b/src/app/page.tsx index 781bb9f..deef445 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,5 @@ -'use server' +'use server'; import LoginForm from '@/components/auth/LoginForm'; -import Header from '@/components/defaults/Header'; import { createClient } from '@/utils/supabase/server'; const HomePage = async () => { @@ -8,21 +7,15 @@ const HomePage = async () => { const { data: { session } } = await supabase.auth.getSession(); if (!session) { return ( -
-
-
- -
-
+
+ +
); } return ( -
-
-
-

Hello, {session.user.email}

-
-
+
+

Hello, {session.user.email}

+
); }; export default HomePage; diff --git a/src/components/auth/AvatarDropdown.tsx b/src/components/auth/AvatarDropdown.tsx index 20296b8..b9320ed 100644 --- a/src/components/auth/AvatarDropdown.tsx +++ b/src/components/auth/AvatarDropdown.tsx @@ -1,4 +1,4 @@ -'use client' +'use client'; import { useState, useEffect } from 'react'; import Image from 'next/image'; @@ -13,6 +13,7 @@ import { import { createClient } from '@/utils/supabase/client'; import type { Session } from '@supabase/supabase-js'; import { Button } from '@/components/ui/button'; +import { redirect } from 'next/navigation'; const AvatarDropdown = () => { const supabase = createClient(); @@ -23,7 +24,9 @@ const AvatarDropdown = () => { // Function to fetch the session async function fetchSession() { try { - const { data: { session } } = await supabase.auth.getSession(); + const { + data: { session }, + } = await supabase.auth.getSession(); setSession(session); } catch (error) { console.error('Error fetching session:', error); @@ -33,17 +36,16 @@ const AvatarDropdown = () => { } // Call the function - fetchSession() - .catch((error) => { - console.error('Error fetching session:', error); - }); + fetchSession().catch((error) => { + console.error('Error fetching session:', error); + }); // Set up auth state change listener - const { data: { subscription } } = supabase.auth.onAuthStateChange( - (_event, session) => { - setSession(session); - } - ); + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((_event, session) => { + setSession(session); + }); // Clean up the subscription when component unmounts return () => { @@ -54,11 +56,12 @@ const AvatarDropdown = () => { // Handle sign out const handleSignOut = async () => { await supabase.auth.signOut(); + redirect('/'); }; // Show nothing while loading if (loading) { - return
; + return
; } // If no session, return empty div @@ -67,14 +70,16 @@ const AvatarDropdown = () => { } // Get user details - const pfp = session.user?.user_metadata?.avatar_url as string ?? - session.user?.user_metadata?.picture as string ?? - '/images/default_user_pfp.png'; - - const name: string = session.user?.user_metadata?.full_name as string ?? - session.user?.user_metadata?.name as string ?? - session.user?.email as string ?? - 'Profile' as string; + const pfp = + (session.user?.user_metadata?.avatar_url as string) ?? + (session.user?.user_metadata?.picture as string) ?? + '/images/default_user_pfp.png'; + + const name: string = + (session.user?.user_metadata?.full_name as string) ?? + (session.user?.user_metadata?.name as string) ?? + (session.user?.email as string) ?? + ('Profile' as string); // If no session, return empty div if (!session) return
; @@ -84,7 +89,7 @@ const AvatarDropdown = () => { User profile { const formRef = useRef(null); - + const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { @@ -68,21 +68,17 @@ const LoginForm = () => { > Sign In - - Log in or create an account. - + Log in or create an account.
- + ( - - Full Name - + Full Name @@ -95,12 +91,10 @@ const LoginForm = () => { /> ( - - Email - + Email @@ -110,12 +104,10 @@ const LoginForm = () => { /> ( - - Password - + Password @@ -123,12 +115,12 @@ const LoginForm = () => { )} /> -
+
); }; diff --git a/src/components/auth/microsoft/SignIn.tsx b/src/components/auth/microsoft/SignIn.tsx index 00a3a9d..868b5c6 100644 --- a/src/components/auth/microsoft/SignIn.tsx +++ b/src/components/auth/microsoft/SignIn.tsx @@ -11,7 +11,8 @@ const MicrosoftSignIn = () => { Microsoft Logo

Sign in with Microsoft diff --git a/src/components/context/TVMode.tsx b/src/components/context/TVMode.tsx index c6b2e2b..70c327b 100644 --- a/src/components/context/TVMode.tsx +++ b/src/components/context/TVMode.tsx @@ -1,8 +1,9 @@ 'use client'; -import React, { createContext, useContext, useState } from 'react'; +import React, { createContext, useContext, useState, useEffect } from 'react'; import Image from 'next/image'; import type { ReactNode } from 'react'; -//import { useSession } from 'next-auth/react'; +import type { Session } from '@supabase/supabase-js'; +import { createClient } from '@/utils/supabase/client'; interface TVModeContextProps { tvMode: boolean; @@ -35,8 +36,45 @@ export const useTVMode = () => { export const TVToggle = () => { const { tvMode, toggleTVMode } = useTVMode(); - //const { data: session } = useSession(); - //if (!session) return

; + const supabase = createClient(); + const [session, setSession] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + // Function to fetch the session + async function fetchSession() { + try { + const { + data: { session }, + } = await supabase.auth.getSession(); + setSession(session); + } catch (error) { + console.error('Error fetching session:', error); + } finally { + setLoading(false); + } + } + + // Call the function + fetchSession().catch((error) => { + console.error('Error fetching session:', error); + }); + + // Set up auth state change listener + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((_event, session) => { + setSession(session); + }); + + // Clean up the subscription when component unmounts + return () => { + subscription.unsubscribe(); + }; + }, [supabase]); + + if (loading || !session) return
; + return (