import { useActionQuery } from "@agent-native/core/client/hooks"; import { useFormatters, useT } from "@agent-native/core/client/i18n"; import { useState } from "react"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { cn } from "@/lib/utils"; interface ClipViewRecord { id: string; viewerEmail: string | null; viewerName: string | null; viewedAt: string; } export interface ViewedByPopoverProps { recordingId: string; /** Rendered as the click target — usually the existing "N views" text. */ children: React.ReactNode; className?: string; } /** * Wraps the aggregate view count with a click-to-open popover listing * individual view records (who viewed, and when), most recent first. * Owner-only data — `list-clip-views` is access-checked server-side, so a * non-owner opening this (if ever rendered for them) simply sees an error * state, never other viewers' identities. */ export function ViewedByPopover({ recordingId, children, className, }: ViewedByPopoverProps) { const t = useT(); const { formatDate, formatRelativeTime } = useFormatters(); const [open, setOpen] = useState(false); const q = useActionQuery<{ views: ClipViewRecord[] }>( "list-clip-views", { recordingId, limit: 50 }, { enabled: open }, ); const relative = (iso: string) => { const date = new Date(iso); const diff = (date.getTime() - Date.now()) / 1000; const abs = Math.abs(diff); if (abs < 60) return formatRelativeTime(Math.round(diff), "second"); if (abs < 3600) return formatRelativeTime(Math.round(diff / 60), "minute"); if (abs < 86400) return formatRelativeTime(Math.round(diff / 3600), "hour"); if (abs < 604800) return formatRelativeTime(Math.round(diff / 86400), "day"); return formatDate(date); }; const views = q.data?.views ?? []; return ( e.stopPropagation()}> e.stopPropagation()} >
{t("recordingInsights.viewedBy")}
{q.isLoading ? (

{t("recordingInsights.loading")}

) : views.length === 0 ? (

{t("recordingInsights.noViewsYet")}

) : (
    {views.map((v) => { const label = v.viewerName || (v.viewerEmail ? v.viewerEmail.split("@")[0] : t("recordingInsights.someone")); return (
  • {initials(v.viewerName || v.viewerEmail || "?")}
    {label}
    {relative(v.viewedAt)}
  • ); })}
)}
); } function initials(s: string): string { return s .split(/\s+|@/) .slice(0, 2) .map((p) => p[0]?.toUpperCase() ?? "") .join(""); }