Just making a mess mostly I think
This commit is contained in:
@ -10,7 +10,6 @@ import React, {
|
||||
} from 'react';
|
||||
import {
|
||||
getProfile,
|
||||
getSignedUrl,
|
||||
getUser,
|
||||
updateProfile as updateProfileAction,
|
||||
} from '@/lib/hooks';
|
||||
@ -20,7 +19,6 @@ import { toast } from 'sonner';
|
||||
type AuthContextType = {
|
||||
user: User | null;
|
||||
profile: Profile | null;
|
||||
avatarUrl: string | null;
|
||||
isLoading: boolean;
|
||||
isAuthenticated: boolean;
|
||||
updateProfile: (data: {
|
||||
@ -36,7 +34,6 @@ const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||
export const AuthProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [profile, setProfile] = useState<Profile | null>(null);
|
||||
const [avatarUrl, setAvatarUrl] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const fetchingRef = useRef(false);
|
||||
@ -58,27 +55,11 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
|
||||
if (!userResponse.success || !profileResponse.success) {
|
||||
setUser(null);
|
||||
setProfile(null);
|
||||
setAvatarUrl(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setUser(userResponse.data);
|
||||
setProfile(profileResponse.data);
|
||||
|
||||
// Get avatar URL if available
|
||||
if (profileResponse.data.avatar_url) {
|
||||
const avatarResponse = await getSignedUrl({
|
||||
bucket: 'avatars',
|
||||
url: profileResponse.data.avatar_url,
|
||||
});
|
||||
if (avatarResponse.success) {
|
||||
setAvatarUrl(avatarResponse.data);
|
||||
} else {
|
||||
setAvatarUrl(null);
|
||||
}
|
||||
} else {
|
||||
setAvatarUrl(null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Auth fetch error: ',
|
||||
@ -118,7 +99,6 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
|
||||
} else if (event === 'SIGNED_OUT') {
|
||||
setUser(null);
|
||||
setProfile(null);
|
||||
setAvatarUrl(null);
|
||||
setIsLoading(false);
|
||||
} else if (event === 'TOKEN_REFRESHED') {
|
||||
// Silent refresh - don't show loading
|
||||
@ -158,7 +138,6 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
|
||||
} else if (event === 'SIGNED_OUT') {
|
||||
setUser(null);
|
||||
setProfile(null);
|
||||
setAvatarUrl(null);
|
||||
setIsLoading(false);
|
||||
} else if (event === 'TOKEN_REFRESHED') {
|
||||
console.log('Token refreshed, updating user data');
|
||||
@ -184,18 +163,6 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
|
||||
throw new Error(result.error ?? 'Failed to update profile');
|
||||
}
|
||||
setProfile(result.data);
|
||||
|
||||
// If avatar was updated, refresh the avatar URL
|
||||
if (data.avatar_url && result.data.avatar_url) {
|
||||
const avatarResponse = await getSignedUrl({
|
||||
bucket: 'avatars',
|
||||
url: result.data.avatar_url,
|
||||
transform: { width: 128, height: 128 },
|
||||
});
|
||||
if (avatarResponse.success) {
|
||||
setAvatarUrl(avatarResponse.data);
|
||||
}
|
||||
}
|
||||
toast.success('Profile updated successfully!');
|
||||
return { success: true, data: result.data };
|
||||
} catch (error) {
|
||||
@ -216,7 +183,6 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
|
||||
const value = {
|
||||
user,
|
||||
profile,
|
||||
avatarUrl,
|
||||
isLoading,
|
||||
isAuthenticated: !!user,
|
||||
updateProfile,
|
||||
|
@ -1,5 +1,5 @@
|
||||
'use client';
|
||||
import { signInWithApple } from '@/lib/actions';
|
||||
import { signInWithApple, getProfile, updateProfile } from '@/lib/actions';
|
||||
import { StatusMessage, SubmitButton } from '@/components/default';
|
||||
import { useAuth } from '@/components/context';
|
||||
import { useRouter } from 'next/navigation';
|
||||
@ -34,8 +34,23 @@ export const SignInWithApple = ({
|
||||
const result = await signInWithApple();
|
||||
|
||||
if (result?.success && result.data) {
|
||||
const profileResponse = await getProfile();
|
||||
if (profileResponse.success) {
|
||||
const profile = profileResponse.data;
|
||||
if (!profile.provider) {
|
||||
const updateResponse = await updateProfile({
|
||||
provider: result.data.provider,
|
||||
})
|
||||
if (!updateResponse.success) throw new Error('Could not update provider!');
|
||||
} else {
|
||||
const updateResponse = await updateProfile({
|
||||
provider: profile.provider + ' ' + result.data.provider,
|
||||
})
|
||||
if (!updateResponse.success) throw new Error('Could not update provider!');
|
||||
}
|
||||
}
|
||||
// Redirect to Apple OAuth page
|
||||
window.location.href = result.data;
|
||||
window.location.href = result.data.url;
|
||||
} else {
|
||||
setStatusMessage(`Error signing in with Apple!`);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import Image from 'next/image';
|
||||
import { type buttonVariants } from '@/components/ui';
|
||||
import { type ComponentProps } from 'react';
|
||||
import { type VariantProps } from 'class-variance-authority';
|
||||
import { getProfile, updateProfile } from '@/lib/hooks';
|
||||
|
||||
type SignInWithMicrosoftProps = {
|
||||
className?: ComponentProps<'div'>['className'];
|
||||
@ -32,8 +33,22 @@ export const SignInWithMicrosoft = ({
|
||||
const result = await signInWithMicrosoft();
|
||||
|
||||
if (result?.success && result.data) {
|
||||
// Redirect to Microsoft OAuth page
|
||||
window.location.href = result.data;
|
||||
const profileResponse = await getProfile();
|
||||
if (profileResponse.success) {
|
||||
const profile = profileResponse.data;
|
||||
if (!profile.provider) {
|
||||
const updateResponse = await updateProfile({
|
||||
provider: result.data.provider,
|
||||
})
|
||||
if (!updateResponse.success) throw new Error('Could not update provider!');
|
||||
} else {
|
||||
const updateResponse = await updateProfile({
|
||||
provider: profile.provider + ' ' + result.data.provider,
|
||||
})
|
||||
if (!updateResponse.success) throw new Error('Could not update provider!');
|
||||
}
|
||||
}
|
||||
window.location.href = result.data.url;
|
||||
} else {
|
||||
setStatusMessage(`Error: Could not sign in with Microsoft!`);
|
||||
}
|
||||
|
@ -2,9 +2,7 @@
|
||||
|
||||
import Link from 'next/link';
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
BasedAvatar,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
@ -15,10 +13,9 @@ import {
|
||||
import { useAuth, useTVMode } from '@/components/context';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { signOut } from '@/lib/actions';
|
||||
import { User } from 'lucide-react';
|
||||
|
||||
const AvatarDropdown = () => {
|
||||
const { profile, avatarUrl, refreshUserData } = useAuth();
|
||||
const { profile, refreshUserData } = useAuth();
|
||||
const router = useRouter();
|
||||
const { toggleTVMode, tvMode } = useTVMode();
|
||||
|
||||
@ -30,36 +27,16 @@ const AvatarDropdown = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const getInitials = (name: string | null | undefined): string => {
|
||||
if (!name) return '';
|
||||
return name
|
||||
.split(' ')
|
||||
.map((n) => n[0])
|
||||
.join('')
|
||||
.toUpperCase();
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Avatar className='cursor-pointer scale-125'>
|
||||
{avatarUrl ? (
|
||||
<AvatarImage
|
||||
src={avatarUrl}
|
||||
alt={getInitials(profile?.full_name)}
|
||||
width={64}
|
||||
height={64}
|
||||
/>
|
||||
) : (
|
||||
<AvatarFallback className='text-md'>
|
||||
{profile?.full_name ? (
|
||||
getInitials(profile.full_name)
|
||||
) : (
|
||||
<User size={64} />
|
||||
)}
|
||||
</AvatarFallback>
|
||||
)}
|
||||
</Avatar>
|
||||
<BasedAvatar
|
||||
src={profile?.avatar_url}
|
||||
fullName={profile?.full_name}
|
||||
className='h-12 w-12 my-auto'
|
||||
fallbackClassName='text-xl font-semibold'
|
||||
userIconSize={32}
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuLabel className='font-bold'>
|
||||
|
@ -11,16 +11,17 @@ const Header = () => {
|
||||
const { isAuthenticated } = useAuth();
|
||||
return tvMode ? (
|
||||
<div className='w-full py-2 pt-6 md:py-5'>
|
||||
<div className='absolute top-8 right-24'>
|
||||
<div className='flex flex-row my-auto items-center pt-2 pr-0 md:pt-4'>
|
||||
<div className='absolute top-8 right-16'>
|
||||
<div className='flex flex-row my-auto items-center'>
|
||||
<ThemeToggle className='mr-4' />
|
||||
{isAuthenticated && <AvatarDropdown />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<header className='w-full py-2 pt-6 md:py-5'>
|
||||
<header className='w-full py-2 md:py-5'>
|
||||
<div className='absolute top-8 right-16'>
|
||||
<div className='flex flex-row my-auto items-center pt-2 pr-0 md:pt-4 md:pr-8'>
|
||||
<div className='flex flex-row my-auto items-center'>
|
||||
<ThemeToggle className='mr-4' />
|
||||
{isAuthenticated && <AvatarDropdown />}
|
||||
</div>
|
||||
|
@ -1,19 +1,17 @@
|
||||
import { useFileUpload } from '@/lib/hooks/useFileUpload';
|
||||
import { useAuth } from '@/components/context';
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
BasedAvatar,
|
||||
CardContent,
|
||||
} from '@/components/ui';
|
||||
import { Loader2, Pencil, Upload, User } from 'lucide-react';
|
||||
import { Loader2, Pencil, Upload } from 'lucide-react';
|
||||
|
||||
type AvatarUploadProps = {
|
||||
onAvatarUploaded: (path: string) => Promise<void>;
|
||||
};
|
||||
|
||||
export const AvatarUpload = ({ onAvatarUploaded }: AvatarUploadProps) => {
|
||||
const { profile, avatarUrl } = useAuth();
|
||||
const { profile } = useAuth();
|
||||
const { isUploading, fileInputRef, uploadToStorage } = useFileUpload();
|
||||
|
||||
const handleAvatarClick = () => {
|
||||
@ -40,15 +38,6 @@ export const AvatarUpload = ({ onAvatarUploaded }: AvatarUploadProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getInitials = (name: string | null | undefined): string => {
|
||||
if (!name) return '';
|
||||
return name
|
||||
.split(' ')
|
||||
.map((n) => n[0])
|
||||
.join('')
|
||||
.toUpperCase();
|
||||
};
|
||||
|
||||
return (
|
||||
<CardContent>
|
||||
<div className='flex flex-col items-center'>
|
||||
@ -56,24 +45,13 @@ export const AvatarUpload = ({ onAvatarUploaded }: AvatarUploadProps) => {
|
||||
className='relative group cursor-pointer mb-4'
|
||||
onClick={handleAvatarClick}
|
||||
>
|
||||
<Avatar className='h-32 w-32'>
|
||||
{avatarUrl ? (
|
||||
<AvatarImage
|
||||
src={avatarUrl}
|
||||
alt={getInitials(profile?.full_name)}
|
||||
width={128}
|
||||
height={128}
|
||||
/>
|
||||
) : (
|
||||
<AvatarFallback className='text-4xl'>
|
||||
{profile?.full_name ? (
|
||||
getInitials(profile.full_name)
|
||||
) : (
|
||||
<User size={32} />
|
||||
)}
|
||||
</AvatarFallback>
|
||||
)}
|
||||
</Avatar>
|
||||
<BasedAvatar
|
||||
src={profile?.avatar_url}
|
||||
fullName={profile?.full_name}
|
||||
className='h-32 w-32'
|
||||
fallbackClassName='text-4xl font-semibold'
|
||||
userIconSize={100}
|
||||
/>
|
||||
<div
|
||||
className='absolute inset-0 rounded-full bg-black/0 group-hover:bg-black/50
|
||||
transition-all flex items-center justify-center'
|
||||
|
@ -5,17 +5,12 @@ import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { useAuth, useTVMode } from '@/components/context';
|
||||
import {
|
||||
getRecentUsersWithStatuses,
|
||||
getSignedUrl,
|
||||
updateStatuses,
|
||||
updateUserStatus,
|
||||
type UserWithStatus,
|
||||
} from '@/lib/hooks';
|
||||
import {
|
||||
Avatar,
|
||||
AvatarImage,
|
||||
AvatarFallback,
|
||||
BasedAvatarImage,
|
||||
BasedAvatarFallback,
|
||||
BasedAvatar,
|
||||
Drawer,
|
||||
DrawerTrigger,
|
||||
Loading
|
||||
@ -26,7 +21,6 @@ import { HistoryDrawer } from '@/components/status';
|
||||
import type { Profile } from '@/utils/supabase';
|
||||
import type { RealtimeChannel } from '@supabase/supabase-js';
|
||||
import { makeConditionalClassName } from '@/lib/utils';
|
||||
import { User } from 'lucide-react';
|
||||
|
||||
type TechTableProps = {
|
||||
initialStatuses: UserWithStatus[];
|
||||
@ -58,22 +52,6 @@ export const TechTable = ({
|
||||
}
|
||||
}, []);
|
||||
|
||||
const fetchAvatarUrl = useCallback(async (url: string) => {
|
||||
try {
|
||||
const avatarResponse = await getSignedUrl({
|
||||
bucket: 'avatars',
|
||||
url,
|
||||
transform: { width: 128, height: 128 },
|
||||
});
|
||||
if (!avatarResponse.success) {
|
||||
throw new Error(avatarResponse.error);
|
||||
}
|
||||
return avatarResponse.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching avatar URL:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Initial load
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
@ -289,23 +267,11 @@ export const TechTable = ({
|
||||
)}
|
||||
<td className={tdClassName}>
|
||||
<div className='flex'>
|
||||
<Avatar>
|
||||
{userWithStatus.user.avatar_url ? (
|
||||
<BasedAvatarImage
|
||||
src={userWithStatus.avatar_url}
|
||||
fullName={userWithStatus.user.full_name ?? ''}
|
||||
width={64}
|
||||
height={64}
|
||||
/>
|
||||
): (
|
||||
<BasedAvatarFallback
|
||||
className='text-md'
|
||||
fullName={userWithStatus.user.full_name}
|
||||
/>
|
||||
)}
|
||||
</Avatar>
|
||||
<BasedAvatar
|
||||
src={userWithStatus.user.avatar_url}
|
||||
fullName={userWithStatus.user.full_name}
|
||||
/>
|
||||
<p>{userWithStatus.user.full_name ?? 'Unknown User'}</p>
|
||||
<p>{userWithStatus.avatar_url}</p>
|
||||
</div>
|
||||
</td>
|
||||
<td className={tdClassName}>
|
||||
|
@ -3,9 +3,63 @@
|
||||
import * as React from 'react';
|
||||
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
||||
import { User } from 'lucide-react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
|
||||
type BasedAvatarProps = React.ComponentProps<typeof AvatarPrimitive.Root> & {
|
||||
src?: string | null;
|
||||
fullName?: string | null;
|
||||
imageClassName?: string;
|
||||
fallbackClassName?: string;
|
||||
userIconSize?: number;
|
||||
};
|
||||
|
||||
function BasedAvatar({
|
||||
src = null,
|
||||
fullName = null,
|
||||
imageClassName ='',
|
||||
fallbackClassName = '',
|
||||
userIconSize = 32,
|
||||
className,
|
||||
...props
|
||||
}: BasedAvatarProps) {
|
||||
return (
|
||||
<AvatarPrimitive.Root
|
||||
data-slot='avatar'
|
||||
className={cn(
|
||||
'cursor-pointer relative flex size-8 shrink-0 overflow-hidden rounded-full',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{src ? (
|
||||
<AvatarImage
|
||||
src={src}
|
||||
className={imageClassName}
|
||||
/>
|
||||
) : (
|
||||
<AvatarPrimitive.Fallback
|
||||
data-slot='avatar-fallback'
|
||||
className={cn(
|
||||
'bg-muted flex size-full items-center justify-center rounded-full',
|
||||
fallbackClassName,
|
||||
)}
|
||||
>
|
||||
{fullName ? (
|
||||
fullName
|
||||
.split(' ')
|
||||
.map((n) => n[0])
|
||||
.join('')
|
||||
.toUpperCase()
|
||||
) : (
|
||||
<User size={userIconSize} />
|
||||
)}
|
||||
</AvatarPrimitive.Fallback>
|
||||
)}
|
||||
</AvatarPrimitive.Root>
|
||||
);
|
||||
}
|
||||
|
||||
function Avatar({
|
||||
className,
|
||||
...props
|
||||
@ -22,25 +76,6 @@ function Avatar({
|
||||
);
|
||||
}
|
||||
|
||||
type BasedAvatarImageProps = React.ComponentProps<typeof AvatarPrimitive.Image> & {
|
||||
fullName: string;
|
||||
};
|
||||
|
||||
function BasedAvatarImage({
|
||||
fullName,
|
||||
className,
|
||||
...props
|
||||
}: BasedAvatarImageProps) {
|
||||
return (
|
||||
<AvatarPrimitive.Image
|
||||
data-slot='avatar-image'
|
||||
className={cn('aspect-square size-full', className)}
|
||||
alt={fullName.split(' ').map((n) => n[0]).join('').toUpperCase()}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AvatarImage({
|
||||
className,
|
||||
...props
|
||||
@ -54,38 +89,6 @@ function AvatarImage({
|
||||
);
|
||||
}
|
||||
|
||||
type BasedAvatarFallbackProps =
|
||||
React.ComponentProps<typeof AvatarPrimitive.Fallback> & {
|
||||
fullName?: string | null;
|
||||
};
|
||||
|
||||
function BasedAvatarFallback({
|
||||
fullName = null,
|
||||
className,
|
||||
...props
|
||||
}: BasedAvatarFallbackProps) {
|
||||
return (
|
||||
<AvatarPrimitive.Fallback
|
||||
data-slot='avatar-fallback'
|
||||
className={cn(
|
||||
'bg-muted flex size-full items-center justify-center rounded-full',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{fullName ? (
|
||||
fullName
|
||||
.split(' ')
|
||||
.map((n) => n[0])
|
||||
.join('')
|
||||
.toUpperCase()
|
||||
) : (
|
||||
<User size={64} />
|
||||
)}
|
||||
</AvatarPrimitive.Fallback>
|
||||
);
|
||||
}
|
||||
|
||||
function AvatarFallback({
|
||||
className,
|
||||
...props
|
||||
@ -102,4 +105,4 @@ function AvatarFallback({
|
||||
);
|
||||
}
|
||||
|
||||
export { Avatar, AvatarImage, BasedAvatarImage, AvatarFallback, BasedAvatarFallback };
|
||||
export { Avatar, BasedAvatar, AvatarImage, AvatarFallback };
|
||||
|
Reference in New Issue
Block a user