poop
This commit is contained in:
parent
d1e9c7e6bb
commit
a346612d48
@ -8,6 +8,18 @@ import { withSentryConfig } from '@sentry/nextjs';
|
|||||||
/** @type {import("next").NextConfig} */
|
/** @type {import("next").NextConfig} */
|
||||||
const config = {
|
const config = {
|
||||||
// You can put your base config options here
|
// You can put your base config options here
|
||||||
|
images: {
|
||||||
|
remotePatterns: [
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: '*.gibbyb.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: '*.gbrown.org',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sentry configuration
|
// Sentry configuration
|
||||||
|
34
package.json
34
package.json
@ -25,15 +25,15 @@
|
|||||||
"@radix-ui/react-scroll-area": "^1.2.3",
|
"@radix-ui/react-scroll-area": "^1.2.3",
|
||||||
"@radix-ui/react-separator": "^1.1.2",
|
"@radix-ui/react-separator": "^1.1.2",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
"@sentry/nextjs": "^9",
|
"@sentry/nextjs": "^9.6.1",
|
||||||
"@supabase/ssr": "^0.6.1",
|
"@supabase/ssr": "^0.6.1",
|
||||||
"@supabase/supabase-js": "^2.49.1",
|
"@supabase/supabase-js": "^2.49.1",
|
||||||
"@t3-oss/env-nextjs": "^0.10.1",
|
"@t3-oss/env-nextjs": "^0.10.1",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"geist": "^1.3.0",
|
"geist": "^1.3.1",
|
||||||
"lucide-react": "^0.483.0",
|
"lucide-react": "^0.483.0",
|
||||||
"next": "^15.0.1",
|
"next": "^15.2.3",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@ -45,22 +45,22 @@
|
|||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/eslint": "^8.56.10",
|
"@types/eslint": "^8.56.12",
|
||||||
"@types/node": "^20.14.10",
|
"@types/node": "^20.17.24",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.19",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.5",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
"@typescript-eslint/eslint-plugin": "^8.27.0",
|
||||||
"@typescript-eslint/parser": "^8.1.0",
|
"@typescript-eslint/parser": "^8.27.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.1",
|
||||||
"eslint-config-next": "^15.0.1",
|
"eslint-config-next": "^15.2.3",
|
||||||
"postcss": "^8.4.39",
|
"postcss": "^8.5.3",
|
||||||
"prettier": "^3.3.2",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.5",
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.17",
|
||||||
"typescript": "^5.5.3"
|
"typescript": "^5.8.2"
|
||||||
},
|
},
|
||||||
"ct3aMetadata": {
|
"ct3aMetadata": {
|
||||||
"initVersion": "7.38.1"
|
"initVersion": "7.38.1"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.5.2"
|
"packageManager": "pnpm@10.6.5+sha512.cdf928fca20832cd59ec53826492b7dc25dc524d4370b6b4adbf65803d32efaa6c1c88147c0ae4e8d579a6c9eec715757b50d4fa35eea179d868eada4ed043af"
|
||||||
}
|
}
|
||||||
|
952
pnpm-lock.yaml
generated
952
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,9 @@ const HomePage = async () => {
|
|||||||
const { data: { session } } = await supabase.auth.getSession();
|
const { data: { session } } = await supabase.auth.getSession();
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col items-center justify-center md:min-h-[70vh]'>
|
<div className='flex flex-col items-center
|
||||||
|
justify-center md:min-h-[70vh]'
|
||||||
|
>
|
||||||
<LoginForm />
|
<LoginForm />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -13,12 +13,14 @@ import {
|
|||||||
import { createClient } from '@/utils/supabase/client';
|
import { createClient } from '@/utils/supabase/client';
|
||||||
import type { Session } from '@supabase/supabase-js';
|
import type { Session } from '@supabase/supabase-js';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { redirect } from 'next/navigation';
|
import type { User } from '@/lib/types';
|
||||||
|
|
||||||
const AvatarDropdown = () => {
|
const AvatarDropdown = () => {
|
||||||
const supabase = createClient();
|
const supabase = createClient();
|
||||||
const [session, setSession] = useState<Session | null>(null);
|
const [session, setSession] = useState<Session | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [user, setUser] = useState<User | null>(null);
|
||||||
|
const [pfp, setPfp] = useState<string>('/images/default_user_pfp.png');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Function to fetch the session
|
// Function to fetch the session
|
||||||
@ -28,6 +30,24 @@ const AvatarDropdown = () => {
|
|||||||
data: { session },
|
data: { session },
|
||||||
} = await supabase.auth.getSession();
|
} = await supabase.auth.getSession();
|
||||||
setSession(session);
|
setSession(session);
|
||||||
|
|
||||||
|
if (session?.user?.id) {
|
||||||
|
const { data: userData, error } = await supabase
|
||||||
|
.from('profiles')
|
||||||
|
.select('*')
|
||||||
|
.eq('id', session?.user.id)
|
||||||
|
.single();
|
||||||
|
if (error) {
|
||||||
|
console.error('Error fetching user data:', error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (userData) {
|
||||||
|
const user = userData as User;
|
||||||
|
console.log(user);
|
||||||
|
setUser(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching session:', error);
|
console.error('Error fetching session:', error);
|
||||||
} finally {
|
} finally {
|
||||||
@ -53,10 +73,36 @@ const AvatarDropdown = () => {
|
|||||||
};
|
};
|
||||||
}, [supabase]);
|
}, [supabase]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user?.avatar_url) {
|
||||||
|
console.log('Avatar Url:', user.avatar_url);
|
||||||
|
if (user.avatar_url.startsWith('http')) {
|
||||||
|
setPfp(user.avatar_url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Get public URL - this is synchronous
|
||||||
|
const { data } = supabase
|
||||||
|
.storage
|
||||||
|
.from('avatars')
|
||||||
|
.getPublicUrl(user.avatar_url);
|
||||||
|
|
||||||
|
console.log('Avatar URL:', data.publicUrl);
|
||||||
|
setPfp(data.publicUrl);
|
||||||
|
}
|
||||||
|
}, [user, supabase]);
|
||||||
|
|
||||||
|
const getInitials = (fullName: string | undefined): string => {
|
||||||
|
if (!fullName || fullName.trim() === '' || fullName === 'undefined') return 'NA';
|
||||||
|
const nameParts = fullName.trim().split(' ');
|
||||||
|
const firstInitial = nameParts[0]?.charAt(0).toUpperCase() ?? 'N';
|
||||||
|
if (nameParts.length === 1) return 'NA';
|
||||||
|
const lastIntitial = nameParts[nameParts.length -1]?.charAt(0).toUpperCase() ?? 'A';
|
||||||
|
return firstInitial + lastIntitial;
|
||||||
|
};
|
||||||
|
|
||||||
// Handle sign out
|
// Handle sign out
|
||||||
const handleSignOut = async () => {
|
const handleSignOut = async () => {
|
||||||
await supabase.auth.signOut();
|
await supabase.auth.signOut();
|
||||||
redirect('/');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Show nothing while loading
|
// Show nothing while loading
|
||||||
@ -69,18 +115,6 @@ const AvatarDropdown = () => {
|
|||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// If no session, return empty div
|
// If no session, return empty div
|
||||||
if (!session) return <div />;
|
if (!session) return <div />;
|
||||||
return (
|
return (
|
||||||
@ -89,7 +123,7 @@ const AvatarDropdown = () => {
|
|||||||
<DropdownMenuTrigger>
|
<DropdownMenuTrigger>
|
||||||
<Image
|
<Image
|
||||||
src={pfp}
|
src={pfp}
|
||||||
alt='User profile'
|
alt={getInitials(user?.full_name) ?? 'NA'}
|
||||||
width={35}
|
width={35}
|
||||||
height={35}
|
height={35}
|
||||||
className='rounded-full border-2 border-white m-auto mr-1 md:mr-2
|
className='rounded-full border-2 border-white m-auto mr-1 md:mr-2
|
||||||
@ -97,7 +131,7 @@ const AvatarDropdown = () => {
|
|||||||
/>
|
/>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
<DropdownMenuLabel>{name}</DropdownMenuLabel>
|
<DropdownMenuLabel>{user?.full_name}</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Button onClick={handleSignOut} className='w-full text-left'>
|
<Button onClick={handleSignOut} className='w-full text-left'>
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
import React, { createContext, useContext, useState } from 'react';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import type { Session } from '@supabase/supabase-js';
|
|
||||||
import { createClient } from '@/utils/supabase/client';
|
|
||||||
|
|
||||||
interface TVModeContextProps {
|
interface TVModeContextProps {
|
||||||
tvMode: boolean;
|
tvMode: boolean;
|
||||||
@ -34,62 +32,31 @@ export const useTVMode = () => {
|
|||||||
return context;
|
return context;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TVToggle = () => {
|
type TVToggleProps = {
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TVToggle = ({
|
||||||
|
width = 25,
|
||||||
|
height = 25,
|
||||||
|
}: TVToggleProps) => {
|
||||||
const { tvMode, toggleTVMode } = useTVMode();
|
const { tvMode, toggleTVMode } = useTVMode();
|
||||||
const supabase = createClient();
|
|
||||||
const [session, setSession] = useState<Session | null>(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 <div />;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button onClick={toggleTVMode} className='mr-4 mt-1'>
|
<button onClick={toggleTVMode} className='mr-4 mt-1'>
|
||||||
{tvMode ? (
|
{tvMode ? (
|
||||||
<Image
|
<Image
|
||||||
src='/images/exit_fullscreen.svg'
|
src='/images/exit_fullscreen.svg'
|
||||||
alt='Exit TV Mode'
|
alt='Exit TV Mode'
|
||||||
width={25}
|
width={width}
|
||||||
height={25}
|
height={height}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Image
|
<Image
|
||||||
src='/images/fullscreen.svg'
|
src='/images/fullscreen.svg'
|
||||||
alt='Enter TV Mode'
|
alt='Enter TV Mode'
|
||||||
width={25}
|
width={width}
|
||||||
height={25}
|
height={height}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,20 +1,69 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { TVToggle, useTVMode } from '@/components/context/TVMode';
|
import { TVToggle, useTVMode } from '@/components/context/TVMode';
|
||||||
import { ThemeToggle } from '@/components/context/Theme';
|
import { ThemeToggle } from '@/components/context/Theme';
|
||||||
import AvatarDropdown from '@/components/auth/AvatarDropdown';
|
import AvatarDropdown from '@/components/auth/AvatarDropdown';
|
||||||
|
import { createClient } from '@/utils/supabase/client';
|
||||||
|
import type { Session } from '@supabase/supabase-js';
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const { tvMode } = useTVMode();
|
const { tvMode } = useTVMode();
|
||||||
|
|
||||||
|
const supabase = createClient();
|
||||||
|
const [session, setSession] = useState<Session | null>(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 (tvMode) {
|
if (tvMode) {
|
||||||
return (
|
return (
|
||||||
<div className='w-full flex flex-row items-end justify-end'>
|
<div className='w-full flex flex-row items-end justify-end'>
|
||||||
<div className='flex flex-row my-auto items-center pt-2 pr-0 sm:pr-8 sm:pt-4'>
|
<div className='flex flex-row my-auto items-center
|
||||||
<AvatarDropdown />
|
justify-center pt-2 pr-0 sm:pt-4 sm:pr-8'
|
||||||
<div className='mb-0.5 ml-2'>
|
>
|
||||||
<TVToggle />
|
|
||||||
</div>
|
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
|
{session && !loading && (
|
||||||
|
<div className='flex flex-row my-auto items-center
|
||||||
|
justify-center'
|
||||||
|
>
|
||||||
|
<div className='mb-0.5 ml-4'>
|
||||||
|
<TVToggle width={22} height={22} />
|
||||||
|
</div>
|
||||||
|
<AvatarDropdown />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -22,15 +71,20 @@ const Header = () => {
|
|||||||
return (
|
return (
|
||||||
<header className='w-full min-h-[10vh]'>
|
<header className='w-full min-h-[10vh]'>
|
||||||
<div className='w-full flex flex-row items-end justify-end'>
|
<div className='w-full flex flex-row items-end justify-end'>
|
||||||
<div
|
<div className='flex flex-row my-auto items-center
|
||||||
className='flex flex-row my-auto items-center
|
justify-center pt-2 pr-0 sm:pt-4 sm:pr-8'
|
||||||
pt-2 pr-0 sm:pt-4 sm:pr-8'
|
|
||||||
>
|
>
|
||||||
<AvatarDropdown />
|
|
||||||
<div className='mb-0.5 ml-2'>
|
|
||||||
<TVToggle />
|
|
||||||
</div>
|
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
|
{session && !loading && (
|
||||||
|
<div className='flex flex-row my-auto items-center
|
||||||
|
justify-center'
|
||||||
|
>
|
||||||
|
<div className='mb-0.5 ml-4'>
|
||||||
|
<TVToggle width={22} height={22} />
|
||||||
|
</div>
|
||||||
|
<AvatarDropdown />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -4,6 +4,7 @@ export type User = {
|
|||||||
email: string;
|
email: string;
|
||||||
avatar_url?: string;
|
avatar_url?: string;
|
||||||
provider: string;
|
provider: string;
|
||||||
|
updated_at?: Date;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Status = {
|
export type Status = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user