diff --git a/src/components/context/Auth.tsx b/src/components/context/Auth.tsx index 5d66f04..529b8ee 100644 --- a/src/components/context/Auth.tsx +++ b/src/components/context/Auth.tsx @@ -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(undefined); export const AuthProvider = ({ children }: { children: ReactNode }) => { const [user, setUser] = useState(null); const [profile, setProfile] = useState(null); - const [avatarUrl, setAvatarUrl] = useState(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, diff --git a/src/components/default/auth/buttons/SignInWithApple.tsx b/src/components/default/auth/buttons/SignInWithApple.tsx index 306b856..7c3cc1c 100644 --- a/src/components/default/auth/buttons/SignInWithApple.tsx +++ b/src/components/default/auth/buttons/SignInWithApple.tsx @@ -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!`); } diff --git a/src/components/default/auth/buttons/SignInWithMicrosoft.tsx b/src/components/default/auth/buttons/SignInWithMicrosoft.tsx index 3c6e61e..a35bb90 100644 --- a/src/components/default/auth/buttons/SignInWithMicrosoft.tsx +++ b/src/components/default/auth/buttons/SignInWithMicrosoft.tsx @@ -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!`); } diff --git a/src/components/default/header/AvatarDropdown.tsx b/src/components/default/header/AvatarDropdown.tsx index 787eca1..7378279 100644 --- a/src/components/default/header/AvatarDropdown.tsx +++ b/src/components/default/header/AvatarDropdown.tsx @@ -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 ( - - {avatarUrl ? ( - - ) : ( - - {profile?.full_name ? ( - getInitials(profile.full_name) - ) : ( - - )} - - )} - + diff --git a/src/components/default/header/index.tsx b/src/components/default/header/index.tsx index 97ce36b..2b8cecb 100644 --- a/src/components/default/header/index.tsx +++ b/src/components/default/header/index.tsx @@ -11,16 +11,17 @@ const Header = () => { const { isAuthenticated } = useAuth(); return tvMode ? (
-
-
+
+
+ {isAuthenticated && }
) : ( -
+
-
+
{isAuthenticated && }
diff --git a/src/components/default/profile/AvatarUpload.tsx b/src/components/default/profile/AvatarUpload.tsx index 1696a6c..14f46ff 100644 --- a/src/components/default/profile/AvatarUpload.tsx +++ b/src/components/default/profile/AvatarUpload.tsx @@ -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; }; 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 (
@@ -56,24 +45,13 @@ export const AvatarUpload = ({ onAvatarUploaded }: AvatarUploadProps) => { className='relative group cursor-pointer mb-4' onClick={handleAvatarClick} > - - {avatarUrl ? ( - - ) : ( - - {profile?.full_name ? ( - getInitials(profile.full_name) - ) : ( - - )} - - )} - +
{ - 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 = ({ )}
- - {userWithStatus.user.avatar_url ? ( - - ): ( - - )} - +

{userWithStatus.user.full_name ?? 'Unknown User'}

-

{userWithStatus.avatar_url}

diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx index d1ba2c9..39ae5e5 100644 --- a/src/components/ui/avatar.tsx +++ b/src/components/ui/avatar.tsx @@ -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 & { + 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 ( + + {src ? ( + + ) : ( + + {fullName ? ( + fullName + .split(' ') + .map((n) => n[0]) + .join('') + .toUpperCase() + ) : ( + + )} + + )} + + ); +} + function Avatar({ className, ...props @@ -22,25 +76,6 @@ function Avatar({ ); } -type BasedAvatarImageProps = React.ComponentProps & { - fullName: string; -}; - -function BasedAvatarImage({ - fullName, - className, - ...props -}: BasedAvatarImageProps) { - return ( - n[0]).join('').toUpperCase()} - {...props} - /> - ); -} - function AvatarImage({ className, ...props @@ -54,38 +89,6 @@ function AvatarImage({ ); } -type BasedAvatarFallbackProps = -React.ComponentProps & { - fullName?: string | null; -}; - -function BasedAvatarFallback({ - fullName = null, - className, - ...props -}: BasedAvatarFallbackProps) { - return ( - - {fullName ? ( - fullName - .split(' ') - .map((n) => n[0]) - .join('') - .toUpperCase() - ) : ( - - )} - - ); -} - function AvatarFallback({ className, ...props @@ -102,4 +105,4 @@ function AvatarFallback({ ); } -export { Avatar, AvatarImage, BasedAvatarImage, AvatarFallback, BasedAvatarFallback }; +export { Avatar, BasedAvatar, AvatarImage, AvatarFallback }; diff --git a/src/lib/actions/auth.ts b/src/lib/actions/auth.ts index 1864833..3896cf3 100644 --- a/src/lib/actions/auth.ts +++ b/src/lib/actions/auth.ts @@ -3,8 +3,7 @@ import 'server-only'; import { createServerClient } from '@/utils/supabase'; import { headers } from 'next/headers'; -import type { User } from '@/utils/supabase'; -import type { Result } from '.'; +import type { User, Result } from '@/utils/supabase'; export const signUp = async ( formData: FormData, @@ -58,31 +57,37 @@ export const signIn = async (formData: FormData): Promise> => { } }; -export const signInWithMicrosoft = async (): Promise> => { +type OAuthReturn = { + provider: string; + url: string; +}; + +export const signInWithMicrosoft = async (): Promise> => { const supabase = await createServerClient(); const origin = (await headers()).get('origin'); const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'azure', options: { - scopes: 'openid, profile email offline_access', + scopes: 'openid profile email offline_access', redirectTo: `${origin}/auth/callback?redirect_to=/auth/success`, }, }); if (error) return { success: false, error: error.message }; - return { success: true, data: data.url }; + return { success: true, data }; }; -export const signInWithApple = async (): Promise> => { +export const signInWithApple = async (): Promise> => { const supabase = await createServerClient(); const origin = process.env.BASE_URL!; const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'apple', options: { + scopes: 'openid profile email offline_access', redirectTo: `${origin}/auth/callback?redirect_to=/auth/success`, }, }); if (error) return { success: false, error: error.message }; - return { success: true, data: data.url }; + return { success: true, data }; }; export const forgotPassword = async ( diff --git a/src/lib/actions/public.ts b/src/lib/actions/public.ts index 2fdf3fb..acef04a 100644 --- a/src/lib/actions/public.ts +++ b/src/lib/actions/public.ts @@ -1,22 +1,37 @@ 'use server'; -import 'server-only'; import { createServerClient, type Profile } from '@/utils/supabase'; -import { getUser } from '@/lib/actions'; +import { getSignedUrl, getUser } from '@/lib/hooks'; import type { Result } from '.'; -export const getProfile = async (): Promise> => { +export const getProfile = async ( + userId: string | null = null +): Promise> => { try { - const user = await getUser(); - if (!user.success || user.data === undefined) - throw new Error('User not found'); + if (userId === null) { + const user = await getUser(); + if (!user.success || user.data === undefined) + throw new Error('User not found'); + userId = user.data.id; + } const supabase = await createServerClient(); const { data, error } = await supabase .from('profiles') .select('*') - .eq('id', user.data.id) + .eq('id', userId) .single(); if (error) throw error; + + if (data.avatar_url) { + const avatarUrl = await getSignedUrl({ + bucket: 'avatars', + url: data.avatar_url, + transform: { width: 128, height: 128 }, + }); + if (avatarUrl.success) { + data.avatar_url = avatarUrl.data; + } + } return { success: true, data: data as Profile }; } catch (error) { return { @@ -33,20 +48,22 @@ type updateProfileProps = { full_name?: string; email?: string; avatar_url?: string; + provider?: string }; export const updateProfile = async ({ full_name, email, avatar_url, + provider, }: updateProfileProps): Promise> => { try { if ( full_name === undefined && email === undefined && - avatar_url === undefined - ) - throw new Error('No profile data provided'); + avatar_url === undefined && + provider === undefined + ) throw new Error('No profile data provided'); const userResponse = await getUser(); if (!userResponse.success || userResponse.data === undefined) @@ -59,11 +76,21 @@ export const updateProfile = async ({ ...(full_name !== undefined && { full_name }), ...(email !== undefined && { email }), ...(avatar_url !== undefined && { avatar_url }), + ...(provider !== undefined && { provider }), }) .eq('id', userResponse.data.id) .select() .single(); if (error) throw error; + + if (data.avatar_url) { + const avatarUrl = await getSignedUrl({ + bucket: 'avatars', + url: data.avatar_url, + transform: { width: 128, height: 128 }, + }); + if (avatarUrl.success) data.avatar_url = avatarUrl.data; + } return { success: true, data: data as Profile, diff --git a/src/lib/actions/status.ts b/src/lib/actions/status.ts index a7b305a..cbc096a 100644 --- a/src/lib/actions/status.ts +++ b/src/lib/actions/status.ts @@ -1,7 +1,7 @@ 'use server'; import { createServerClient } from '@/utils/supabase'; import type { Profile, Result } from '@/utils/supabase'; -import { getUser, getProfile } from '@/lib/hooks'; +import { getUser, getProfile, getSignedUrl } from '@/lib/actions'; export type UserWithStatus = { id?: string; @@ -30,14 +30,12 @@ export const getRecentUsersWithStatuses = async (): Promise< const { data, error } = (await supabase .from('statuses') - .select( - ` + .select(` user:profiles!user_id(*), status, created_at, updated_by:profiles!updated_by_id(*) - `, - ) + `) .gte('created_at', oneDayAgo.toISOString()) .order('created_at', { ascending: false })) as { data: UserWithStatus[]; @@ -47,7 +45,6 @@ export const getRecentUsersWithStatuses = async (): Promise< if (error) throw error as Error; if (!data?.length) return { success: true, data: [] }; - // 3️⃣ client-side dedupe: keep the first status you see per user const seen = new Set(); const filtered = data.filter((row) => { if (seen.has(row.user.id)) return false; @@ -55,7 +52,34 @@ export const getRecentUsersWithStatuses = async (): Promise< return true; }); - return { success: true, data: filtered }; + const filteredWithAvatars = new Array(); + + for (const userWithStatus of filtered) { + + if (userWithStatus.user.avatar_url) { + const avatarResponse = await getSignedUrl({ + bucket: 'avatars', + url: userWithStatus.user.avatar_url, + }); + if (avatarResponse.success) { + userWithStatus.user.avatar_url = avatarResponse.data; + } else userWithStatus.user.avatar_url = null; + } else userWithStatus.user.avatar_url = null; + + if (userWithStatus.updated_by?.avatar_url) { + const updatedByAvatarResponse = await getSignedUrl({ + bucket: 'avatars', + url: userWithStatus.updated_by.avatar_url ?? '', + }); + if (updatedByAvatarResponse.success) { + userWithStatus.updated_by.avatar_url = updatedByAvatarResponse.data; + } else userWithStatus.updated_by.avatar_url = null; + } else { + if (userWithStatus.updated_by) userWithStatus.updated_by.avatar_url = null; + } + filteredWithAvatars.push(userWithStatus); + } + return { success: true, data: filteredWithAvatars }; } catch (error) { return { success: false, error: `Error: ${error as Error}` }; } @@ -96,17 +120,14 @@ export const updateStatuses = async ( ): Promise> => { try { const supabase = await createServerClient(); - const userResponse = await getUser(); - if (!userResponse.success) throw new Error('Not authenticated!'); const profileResponse = await getProfile(); - if (!profileResponse.success) throw new Error(profileResponse.error); - const user = userResponse.data; + if (!profileResponse.success) throw new Error('Not authenticated!'); const userProfile = profileResponse.data; - const inserts = userIds.map((usersId) => ({ - user_id: usersId, + const inserts = userIds.map((userId) => ({ + user_id: userId, status, - updated_by_id: user.id, + updated_by_id: userProfile.id, })); const { data: insertedStatuses, error: insertedStatusesError } = @@ -116,13 +137,9 @@ export const updateStatuses = async ( if (insertedStatuses) { const broadcastArray = new Array(insertedStatuses.length); for (const insertedStatus of insertedStatuses) { - const { data: profile, error: profileError } = await supabase - .from('profiles') - .select('*') - .eq('id', insertedStatus.user_id) - .single(); - if (profileError) throw profileError as Error; - + const profileResponse = await getProfile(insertedStatus.user_id) + if (!profileResponse.success) throw new Error(profileResponse.error); + const profile = profileResponse.data; if (profile) { broadcastArray.push({ user: profile, @@ -148,21 +165,17 @@ export const updateUserStatus = async ( ): Promise> => { try { const supabase = await createServerClient(); - const userResponse = await getUser(); - if (!userResponse.success) - throw new Error(`Not authenticated! ${userResponse.error}`); const profileResponse = await getProfile(); if (!profileResponse.success) - throw new Error(`Could not get profile! ${profileResponse.error}`); - const user = userResponse.data; + throw new Error(`Not authenticated! ${profileResponse.error}`); const userProfile = profileResponse.data; const { data: insertedStatus, error: insertedStatusError } = await supabase .from('statuses') .insert({ - user_id: user.id, + user_id: userProfile.id, status, - updated_by_id: user.id, + updated_by_id: userProfile.id, }) .select() .single(); @@ -172,6 +185,7 @@ export const updateUserStatus = async ( user: userProfile, status: insertedStatus.status, created_at: insertedStatus.created_at, + updated_by: userProfile, }; await broadcastStatusUpdates([userStatus]); @@ -220,14 +234,6 @@ export const getUserHistory = async ( }; if (statusesError) throw statusesError as Error; - const { data: profile, error: profileError } = (await supabase - .from('profiles') - .select('*') - .eq('id', userId) - .single()) as { data: Profile; error: unknown }; - if (profileError) throw profileError as Error; - if (!profile) throw new Error('User profile not found!'); - const totalCount = count ?? 0; const totalPages = Math.ceil(totalCount / perPage); diff --git a/src/lib/hooks/auth.ts b/src/lib/hooks/auth.ts index 7ce6c15..7e9ead9 100644 --- a/src/lib/hooks/auth.ts +++ b/src/lib/hooks/auth.ts @@ -54,7 +54,12 @@ export const signIn = async (formData: FormData): Promise> => { } }; -export const signInWithMicrosoft = async (): Promise> => { +type OAuthReturn = { + provider: string; + url: string; +}; + +export const signInWithMicrosoft = async (): Promise> => { const supabase = createClient(); const origin = process.env.NEXT_PUBLIC_SITE_URL!; const { data, error } = await supabase.auth.signInWithOAuth({ @@ -65,20 +70,21 @@ export const signInWithMicrosoft = async (): Promise> => { }, }); if (error) return { success: false, error: error.message }; - return { success: true, data: data.url }; + return { success: true, data }; }; -export const signInWithApple = async (): Promise> => { +export const signInWithApple = async (): Promise> => { const supabase = createClient(); const origin = process.env.NEXT_PUBLIC_SITE_URL!; const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'apple', options: { + scopes: 'openid profile email offline_access', redirectTo: `${origin}/auth/callback?redirect_to=/auth/success`, }, }); if (error) return { success: false, error: error.message }; - return { success: true, data: data.url }; + return { success: true, data }; }; export const forgotPassword = async ( diff --git a/src/lib/hooks/public.ts b/src/lib/hooks/public.ts index 7329d33..f799c32 100644 --- a/src/lib/hooks/public.ts +++ b/src/lib/hooks/public.ts @@ -1,21 +1,37 @@ 'use client'; import { createClient, type Profile } from '@/utils/supabase'; -import { getUser } from '@/lib/hooks'; +import { getSignedUrl, getUser } from '@/lib/hooks'; import type { Result } from '.'; -export const getProfile = async (): Promise> => { +export const getProfile = async ( + userId: string | null = null +): Promise> => { try { - const user = await getUser(); - if (!user.success || user.data === undefined) - throw new Error('User not found'); + if (userId === null) { + const user = await getUser(); + if (!user.success || user.data === undefined) + throw new Error('User not found'); + userId = user.data.id; + } const supabase = createClient(); const { data, error } = await supabase .from('profiles') .select('*') - .eq('id', user.data.id) + .eq('id', userId) .single(); if (error) throw error; + + if (data.avatar_url) { + const avatarUrl = await getSignedUrl({ + bucket: 'avatars', + url: data.avatar_url, + transform: { width: 128, height: 128 }, + }); + if (avatarUrl.success) { + data.avatar_url = avatarUrl.data; + } + } return { success: true, data: data as Profile }; } catch (error) { return { @@ -32,20 +48,22 @@ type updateProfileProps = { full_name?: string; email?: string; avatar_url?: string; + provider?: string; }; export const updateProfile = async ({ full_name, email, avatar_url, + provider, }: updateProfileProps): Promise> => { try { if ( full_name === undefined && email === undefined && - avatar_url === undefined - ) - throw new Error('No profile data provided'); + avatar_url === undefined && + provider === undefined + ) throw new Error('No profile data provided'); const userResponse = await getUser(); if (!userResponse.success || userResponse.data === undefined) @@ -58,11 +76,21 @@ export const updateProfile = async ({ ...(full_name !== undefined && { full_name }), ...(email !== undefined && { email }), ...(avatar_url !== undefined && { avatar_url }), + ...(provider !== undefined && { provider }), }) .eq('id', userResponse.data.id) .select() .single(); if (error) throw error; + + if (data.avatar_url) { + const avatarUrl = await getSignedUrl({ + bucket: 'avatars', + url: data.avatar_url, + transform: { width: 128, height: 128 }, + }); + if (avatarUrl.success) data.avatar_url = avatarUrl.data; + } return { success: true, data: data as Profile, diff --git a/src/lib/hooks/status.ts b/src/lib/hooks/status.ts index ab15dc1..ce53572 100644 --- a/src/lib/hooks/status.ts +++ b/src/lib/hooks/status.ts @@ -7,7 +7,6 @@ export type UserWithStatus = { id?: string; user: Profile; status: string; - avatar_url?: string; created_at: string; updated_by?: Profile; }; @@ -54,20 +53,33 @@ export const getRecentUsersWithStatuses = async (): Promise< return true; }); - const filteredWithAvatars: UserWithStatus[] = filtered; + const filteredWithAvatars = new Array(); - for (let userWithStatus of filteredWithAvatars) { - if (!userWithStatus.user.avatar_url) continue; - const avatarResponse = await getSignedUrl({ - bucket: 'avatars', - url: userWithStatus.user.avatar_url, - transform: { width: 128, height: 128 }, - }); - if (!avatarResponse.success) continue; - else userWithStatus = { ...userWithStatus, avatar_url: avatarResponse.data }; + for (const userWithStatus of filtered) { + + if (userWithStatus.user.avatar_url) { + const avatarResponse = await getSignedUrl({ + bucket: 'avatars', + url: userWithStatus.user.avatar_url, + }); + if (avatarResponse.success) { + userWithStatus.user.avatar_url = avatarResponse.data; + } else userWithStatus.user.avatar_url = null; + } else userWithStatus.user.avatar_url = null; + + if (userWithStatus.updated_by?.avatar_url) { + const updatedByAvatarResponse = await getSignedUrl({ + bucket: 'avatars', + url: userWithStatus.updated_by.avatar_url ?? '', + }); + if (updatedByAvatarResponse.success) { + userWithStatus.updated_by.avatar_url = updatedByAvatarResponse.data; + } else userWithStatus.updated_by.avatar_url = null; + } else { + if (userWithStatus.updated_by) userWithStatus.updated_by.avatar_url = null; + } + filteredWithAvatars.push(userWithStatus); } - console.log('filteredWithAvatars', filteredWithAvatars); - return { success: true, data: filteredWithAvatars }; } catch (error) { return { success: false, error: `Error: ${error as Error}` }; @@ -109,17 +121,14 @@ export const updateStatuses = async ( ): Promise> => { try { const supabase = createClient(); - const userResponse = await getUser(); - if (!userResponse.success) throw new Error('Not authenticated!'); const profileResponse = await getProfile(); - if (!profileResponse.success) throw new Error(profileResponse.error); - const user = userResponse.data; + if (!profileResponse.success) throw new Error('Not authenticated!'); const userProfile = profileResponse.data; - const inserts = userIds.map((usersId) => ({ - user_id: usersId, + const inserts = userIds.map((userId) => ({ + user_id: userId, status, - updated_by_id: user.id, + updated_by_id: userProfile.id, })); const { data: insertedStatuses, error: insertedStatusesError } = @@ -129,13 +138,9 @@ export const updateStatuses = async ( if (insertedStatuses) { const broadcastArray = new Array(insertedStatuses.length); for (const insertedStatus of insertedStatuses) { - const { data: profile, error: profileError } = await supabase - .from('profiles') - .select('*') - .eq('id', insertedStatus.user_id) - .single(); - if (profileError) throw profileError as Error; - + const profileResponse = await getProfile(insertedStatus.user_id) + if (!profileResponse.success) throw new Error(profileResponse.error); + const profile = profileResponse.data; if (profile) { broadcastArray.push({ user: profile, @@ -161,21 +166,17 @@ export const updateUserStatus = async ( ): Promise> => { try { const supabase = createClient(); - const userResponse = await getUser(); - if (!userResponse.success) - throw new Error(`Not authenticated! ${userResponse.error}`); const profileResponse = await getProfile(); if (!profileResponse.success) - throw new Error(`Could not get profile! ${profileResponse.error}`); - const user = userResponse.data; + throw new Error(`Not authenticated! ${profileResponse.error}`); const userProfile = profileResponse.data; const { data: insertedStatus, error: insertedStatusError } = await supabase .from('statuses') .insert({ - user_id: user.id, + user_id: userProfile.id, status, - updated_by_id: user.id, + updated_by_id: userProfile.id, }) .select() .single(); @@ -234,14 +235,6 @@ export const getUserHistory = async ( }; if (statusesError) throw statusesError as Error; - const { data: profile, error: profileError } = (await supabase - .from('profiles') - .select('*') - .eq('id', userId) - .single()) as { data: Profile; error: unknown }; - if (profileError) throw profileError as Error; - if (!profile) throw new Error('User profile not found!'); - const totalCount = count ?? 0; const totalPages = Math.ceil(totalCount / perPage);