remove unused component
This commit is contained in:
@@ -1,191 +0,0 @@
|
||||
import { forwardRef, useMemo } from 'react';
|
||||
import { Headphones } from 'lucide-react';
|
||||
import { cn, getInitials } from '@/lib/utils';
|
||||
import { useLiveLatestChild } from '@/hooks/use-particle';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { particlePath } from '@/lib/particle-path';
|
||||
import type { Particle, StreamProperties } from '@/api/types';
|
||||
import { useStreamAutoplay } from '@/hooks/use-stream-autoplay';
|
||||
import { ParticlePreview } from '@/features/particles/particle-preview';
|
||||
import { useNetwork } from '@/hooks/use-networks';
|
||||
import { HumanAvatar } from '@/components/human-avatar';
|
||||
import { RelativeTimestamp } from '@/components/relative-timestamp';
|
||||
import { Small } from '@/components/ui/typography';
|
||||
|
||||
interface StreamCardProps {
|
||||
particle: Particle & { type: 'stream'; properties: StreamProperties };
|
||||
networkId: string;
|
||||
onClick: () => void;
|
||||
isSelected?: boolean;
|
||||
shortcutKey?: number;
|
||||
}
|
||||
|
||||
export const StreamCard = forwardRef<HTMLDivElement, StreamCardProps>(
|
||||
function StreamCard(
|
||||
{ particle, networkId, onClick, isSelected, shortcutKey },
|
||||
ref,
|
||||
) {
|
||||
const streamPath = particlePath(networkId, [particle.id]);
|
||||
const { latestChild } = useLiveLatestChild(streamPath);
|
||||
const userId = useAuthStore((s) => s.user?.id) ?? '';
|
||||
const network = useNetwork(networkId);
|
||||
|
||||
useStreamAutoplay(latestChild, particle, networkId, network ?? undefined);
|
||||
|
||||
const hasActiveHuddle =
|
||||
particle.huddle_active_participants &&
|
||||
particle.huddle_active_participants.length > 0;
|
||||
const huddleCount = particle.huddle_active_participants?.length ?? 0;
|
||||
|
||||
const isDM =
|
||||
particle.visible_to.length === 2 &&
|
||||
particle.visible_to.every((v) => v.startsWith('human:'));
|
||||
|
||||
const avatar = useMemo(() => {
|
||||
if (isDM) {
|
||||
const otherEntry = particle.visible_to.find(
|
||||
(v) => v !== `human:${userId}`,
|
||||
);
|
||||
if (otherEntry) {
|
||||
const otherId = otherEntry.replace('human:', '');
|
||||
const otherHuman = network?.humans?.find((h) => h.id === otherId);
|
||||
if (otherHuman) {
|
||||
return {
|
||||
initials: getInitials(otherHuman.email),
|
||||
avatarObjectId: otherHuman.avatar_object_id ?? null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (latestChild) {
|
||||
const creator = network?.humans?.find(
|
||||
(h) => h.id === latestChild.created_by_human_id,
|
||||
);
|
||||
if (creator) {
|
||||
return {
|
||||
initials: getInitials(creator.email),
|
||||
avatarObjectId: creator.avatar_object_id ?? null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
initials: particle.properties.name.slice(0, 2).toUpperCase(),
|
||||
avatarObjectId: null,
|
||||
};
|
||||
}, [
|
||||
isDM,
|
||||
particle.visible_to,
|
||||
particle.properties.name,
|
||||
userId,
|
||||
latestChild,
|
||||
network,
|
||||
]);
|
||||
|
||||
const isUnseen = useMemo(() => {
|
||||
if (!latestChild) return false;
|
||||
const latestChildTimestamp = latestChild.created_at.getTime();
|
||||
const userPlaybackPosition =
|
||||
particle.playback_markers?.[userId]?.getTime() ?? 0;
|
||||
return latestChildTimestamp > userPlaybackPosition;
|
||||
}, [latestChild, particle.playback_markers, userId]);
|
||||
|
||||
// For media particles with a transcript, show it as an overlay on the preview
|
||||
const transcript =
|
||||
latestChild?.type === 'media'
|
||||
? latestChild.properties.transcript?.transcript
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={onClick}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') onClick();
|
||||
}}
|
||||
className={cn(
|
||||
'cursor-pointer overflow-hidden rounded-xl ring-1 ring-foreground/10 transition-all hover:ring-foreground/20',
|
||||
isUnseen && 'ring-2 ring-primary',
|
||||
isSelected && 'ring-2 ring-ring',
|
||||
hasActiveHuddle && 'ring-2 ring-red-500/70',
|
||||
)}
|
||||
>
|
||||
{/* Preview area */}
|
||||
<div className="relative aspect-[4/3] overflow-hidden bg-muted">
|
||||
{hasActiveHuddle && (
|
||||
<div className="pointer-events-none absolute inset-0 z-10 bg-gradient-to-b from-red-500/15 to-transparent" />
|
||||
)}
|
||||
{shortcutKey && (
|
||||
<kbd className="absolute top-1.5 left-1.5 z-10 flex size-5 items-center justify-center rounded bg-black/50 font-mono text-xs text-white/70">
|
||||
{shortcutKey}
|
||||
</kbd>
|
||||
)}
|
||||
{latestChild ? (
|
||||
<ParticlePreview particle={latestChild} />
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<p className="text-muted-foreground text-xs italic">
|
||||
No messages yet
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Transcript overlay for media with transcripts */}
|
||||
{transcript && (
|
||||
<div className="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/80 to-transparent px-2.5 pt-6 pb-2">
|
||||
<p className="line-clamp-2 text-md leading-snug text-white/90">
|
||||
{transcript}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Info bar */}
|
||||
<div className="flex items-center gap-2 px-2.5 py-2">
|
||||
<HumanAvatar
|
||||
className={cn('size-6 shrink-0', isUnseen && 'ring-2 ring-primary')}
|
||||
avatarObjectId={avatar.avatarObjectId}
|
||||
initials={avatar.initials}
|
||||
fallbackClassName="bg-primary/10 text-primary text-[10px] font-medium"
|
||||
/>
|
||||
<Small
|
||||
className={cn(
|
||||
'min-w-0 truncate',
|
||||
isUnseen
|
||||
? 'font-semibold text-foreground'
|
||||
: 'font-medium text-muted-foreground',
|
||||
)}
|
||||
>
|
||||
{particle.properties.name}
|
||||
</Small>
|
||||
<div className="ml-auto flex shrink-0 items-center gap-1.5">
|
||||
{hasActiveHuddle && (
|
||||
<span className="flex items-center gap-1 rounded-full bg-red-500/15 px-1.5 py-0.5">
|
||||
<Headphones className="size-3 text-red-400" />
|
||||
<span className="text-[10px] font-medium text-red-400">
|
||||
{huddleCount}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
{latestChild && (
|
||||
<Small
|
||||
className={cn(
|
||||
'text-[10px]',
|
||||
isUnseen ? 'text-primary' : 'text-muted-foreground',
|
||||
)}
|
||||
>
|
||||
<RelativeTimestamp date={latestChild.created_at} />
|
||||
</Small>
|
||||
)}
|
||||
{isUnseen && (
|
||||
<span className="size-2 shrink-0 rounded-full bg-primary" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user