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 { Avatar, AvatarFallback } from "@/components/ui/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(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 initials = 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 getInitials(otherHuman.email); } } if (latestChild) { const creator = network?.humans?.find( (h) => h.id === latestChild.created_by_human_id, ); if (creator) return getInitials(creator.email); } return particle.properties.name.slice(0, 2).toUpperCase(); }, [ 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 (
{ 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 */}
{hasActiveHuddle && (
)} {shortcutKey && ( {shortcutKey} )} {latestChild ? ( ) : (

No messages yet

)} {/* Transcript overlay for media with transcripts */} {transcript && (

{transcript}

)}
{/* Info bar */}
{initials} {particle.properties.name}
{hasActiveHuddle && ( {huddleCount} )} {latestChild && ( )} {isUnseen && ( )}
); });