Trying to figure out avatar urls. Not easy.
This commit is contained in:
@ -5,17 +5,28 @@ import { useState, useEffect, useCallback, useRef } from 'react';
|
|||||||
import { useAuth, useTVMode } from '@/components/context';
|
import { useAuth, useTVMode } from '@/components/context';
|
||||||
import {
|
import {
|
||||||
getRecentUsersWithStatuses,
|
getRecentUsersWithStatuses,
|
||||||
|
getSignedUrl,
|
||||||
updateStatuses,
|
updateStatuses,
|
||||||
updateUserStatus,
|
updateUserStatus,
|
||||||
type UserWithStatus,
|
type UserWithStatus,
|
||||||
} from '@/lib/hooks';
|
} from '@/lib/hooks';
|
||||||
import { Drawer, DrawerTrigger, Loading } from '@/components/ui';
|
import {
|
||||||
|
Avatar,
|
||||||
|
AvatarImage,
|
||||||
|
AvatarFallback,
|
||||||
|
BasedAvatarImage,
|
||||||
|
BasedAvatarFallback,
|
||||||
|
Drawer,
|
||||||
|
DrawerTrigger,
|
||||||
|
Loading
|
||||||
|
} from '@/components/ui';
|
||||||
import { SubmitButton } from '@/components/default';
|
import { SubmitButton } from '@/components/default';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { HistoryDrawer } from '@/components/status';
|
import { HistoryDrawer } from '@/components/status';
|
||||||
import type { Profile } from '@/utils/supabase';
|
import type { Profile } from '@/utils/supabase';
|
||||||
import type { RealtimeChannel } from '@supabase/supabase-js';
|
import type { RealtimeChannel } from '@supabase/supabase-js';
|
||||||
import { makeConditionalClassName } from '@/lib/utils';
|
import { makeConditionalClassName } from '@/lib/utils';
|
||||||
|
import { User } from 'lucide-react';
|
||||||
|
|
||||||
type TechTableProps = {
|
type TechTableProps = {
|
||||||
initialStatuses: UserWithStatus[];
|
initialStatuses: UserWithStatus[];
|
||||||
@ -47,6 +58,22 @@ 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
|
// Initial load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
@ -225,7 +252,7 @@ export const TechTable = ({
|
|||||||
/>
|
/>
|
||||||
</th>
|
</th>
|
||||||
)}
|
)}
|
||||||
<th className={thClassName}>Name</th>
|
<th className={thClassName}>Technician</th>
|
||||||
<th className={thClassName}>
|
<th className={thClassName}>
|
||||||
<Drawer>
|
<Drawer>
|
||||||
<DrawerTrigger
|
<DrawerTrigger
|
||||||
@ -261,7 +288,25 @@ export const TechTable = ({
|
|||||||
</td>
|
</td>
|
||||||
)}
|
)}
|
||||||
<td className={tdClassName}>
|
<td className={tdClassName}>
|
||||||
{userWithStatus.user.full_name ?? 'Unknown User'}
|
<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>
|
||||||
|
<p>{userWithStatus.user.full_name ?? 'Unknown User'}</p>
|
||||||
|
<p>{userWithStatus.avatar_url}</p>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className={tdClassName}>
|
<td className={tdClassName}>
|
||||||
<Drawer>
|
<Drawer>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
||||||
|
import { User } from 'lucide-react';
|
||||||
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
@ -21,6 +22,25 @@ 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({
|
function AvatarImage({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
@ -34,10 +54,42 @@ 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({
|
function AvatarFallback({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
}: React.ComponentProps<typeof AvatarPrimitive.Fallback & {fullName: string}>) {
|
||||||
return (
|
return (
|
||||||
<AvatarPrimitive.Fallback
|
<AvatarPrimitive.Fallback
|
||||||
data-slot='avatar-fallback'
|
data-slot='avatar-fallback'
|
||||||
@ -50,4 +102,4 @@ function AvatarFallback({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Avatar, AvatarImage, AvatarFallback };
|
export { Avatar, AvatarImage, BasedAvatarImage, AvatarFallback, BasedAvatarFallback };
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { createClient } from '@/utils/supabase';
|
import { createClient } from '@/utils/supabase';
|
||||||
import type { Profile, Result } from '@/utils/supabase';
|
import type { Profile, Result } from '@/utils/supabase';
|
||||||
import { getUser, getProfile } from '@/lib/hooks';
|
import { getUser, getProfile, getSignedUrl } from '@/lib/hooks';
|
||||||
|
|
||||||
export type UserWithStatus = {
|
export type UserWithStatus = {
|
||||||
id?: string;
|
id?: string;
|
||||||
user: Profile;
|
user: Profile;
|
||||||
status: string;
|
status: string;
|
||||||
|
avatar_url?: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_by?: Profile;
|
updated_by?: Profile;
|
||||||
};
|
};
|
||||||
@ -53,7 +54,21 @@ export const getRecentUsersWithStatuses = async (): Promise<
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
return { success: true, data: filtered };
|
const filteredWithAvatars: UserWithStatus[] = filtered;
|
||||||
|
|
||||||
|
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 };
|
||||||
|
}
|
||||||
|
console.log('filteredWithAvatars', filteredWithAvatars);
|
||||||
|
|
||||||
|
return { success: true, data: filteredWithAvatars };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { success: false, error: `Error: ${error as Error}` };
|
return { success: false, error: `Error: ${error as Error}` };
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user