// Pure presentation. This component knows nothing about the Chime SDK, the transport, or any network // call - it binds a tile id to a DOM element and gets out of the way. That separation is what lets a // live call move between the docked and floating surfaces (Tasks 8/9) without dropping: the media // lives in `ChimeCallSession`, not here - and the unbind below names the element it bound, so a // cleanup from the surface being replaced cannot tear down the element of the one replacing it. import React, { useEffect, useRef, useState } from 'react'; import { ChevronLeft, ChevronRight, Minus, Video } from 'lucide-react'; import { useVideoServiceContext } from '../../services/VideoService'; import { initialsOf, resolveParticipantLabel } from './participantIdentity'; import { useParticipantNames } from './useParticipantNames'; import './VideoTileGrid.css'; const VideoTile: React.FC<{ tileId: number }> = ({ tileId }) => { const { bindTile, unbindTile } = useVideoServiceContext(); const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; bindTile(tileId, el); // Load-bearing, in both directions. An element that is never unbound keeps its media // stream attached after the tile is gone - a leak the user sees as a frozen frame and the // browser sees as a live camera. But the SDK unbinds by TILE ID, not by element, so an // unqualified unbind here would kill whatever element holds this tile at cleanup time - and // when two surfaces are mounted at once, that can be the OTHER surface's live element. // Naming the captured element lets the session refuse exactly that case. return () => { unbindTile(tileId, el); }; }, [tileId, bindTile, unbindTile]); return (