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