import { HumanAvatar } from '@/components/human-avatar'; import { Tooltip, TooltipContent, TooltipTrigger, } from '@/components/ui/tooltip'; import type { HumanPresence } from '@/hooks/use-presence-positions'; const MAX_VISIBLE_AVATARS = 3; const PAGE_SIZE = 10; interface PlaybackPageIndicatorProps { total: number; current: number; progress: number; onGoTo: (index: number) => void; presenceBySegment?: Map; /** Set of humanIds currently online in the stream channel. */ onlineHumanIds?: Set; /** Render only avatars or only tracks. Omit to render both. */ layer?: 'avatars' | 'tracks'; } export function PlaybackPageIndicator({ total, current, progress, onGoTo, presenceBySegment, onlineHumanIds, layer, }: PlaybackPageIndicatorProps) { if (total === 0) return null; const showAvatars = layer !== 'tracks'; const showTracks = layer !== 'avatars'; const paginated = total > PAGE_SIZE; const safeCurrent = current < 0 ? 0 : current; const pageStart = paginated ? Math.floor(safeCurrent / PAGE_SIZE) * PAGE_SIZE : 0; const visibleCount = paginated ? Math.min(PAGE_SIZE, total - pageStart) : total; const hasPrevPage = paginated && pageStart > 0; const hasNextPage = paginated && pageStart + PAGE_SIZE < total; return (
{paginated && ( onGoTo(pageStart - 1)} /> )}
{Array.from({ length: visibleCount }, (_, j) => { const i = pageStart + j; const presence = presenceBySegment?.get(i); return (
{showAvatars && presence && presence.length > 0 && ( )} {showTracks && ( )}
); })}
{paginated && ( onGoTo(pageStart + PAGE_SIZE)} /> )}
{paginated && showTracks && current >= 0 && (
{current + 1} / {total}
)}
); } function GhostStub({ visible, interactive, onClick, }: { visible: boolean; interactive: boolean; onClick: () => void; }) { // Always reserve width so segments don't shift when stubs appear/disappear. if (!visible) { return
; } if (!interactive) { // Avatars layer: invisible spacer matching the tracks layer's stub width. return
; } return ( ); } function SegmentPresenceAvatars({ presence, onlineHumanIds, }: { presence: HumanPresence[]; onlineHumanIds?: Set; }) { const visible = presence.slice(0, MAX_VISIBLE_AVATARS); const overflow = presence.length - MAX_VISIBLE_AVATARS; return (
{visible.map((human) => ( {human.email} ))} {overflow > 0 && ( +{overflow} )}
); }