import { RoomEvent } from "livekit-client"; import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { useCall, getRoom } from "../../store"; import Base from "../../components/modals/base"; const SECONDARY_ROW_HEIGHT_PX = 180; const STACK_GAP_PX = 12; export default function View() { const room = getRoom(); const layoutVersion = useCall((state) => state.layoutVersion); const usersInFocusedViewHidden = useCall( (state) => state.usersInFocusedViewHidden, ); const callIsFullscreen = useCall((state) => state.callIsFullscreen); const focusedParticipantId = useCall((state) => state.focusedParticipantId); const focusedParticipantType = useCall( (state) => state.focusedParticipantType, ); const activeScreenShareParticipantIds = useCall( (state) => state.activeScreenShareParticipantIds, ); const containerRef = useRef(null); const focusedTileRef = useRef(null); const [isFocusedTileFlush, setIsFocusedTileFlush] = useState(false); const [participantVersion, setParticipantVersion] = useState(0); useEffect(() => { const syncParticipants = () => { setParticipantVersion((version) => version + 1); }; syncParticipants(); room.on(RoomEvent.ParticipantConnected, syncParticipants); room.on(RoomEvent.ParticipantDisconnected, syncParticipants); room.on(RoomEvent.TrackPublished, syncParticipants); room.on(RoomEvent.TrackUnpublished, syncParticipants); room.on(RoomEvent.TrackSubscribed, syncParticipants); room.on(RoomEvent.TrackUnsubscribed, syncParticipants); room.on(RoomEvent.ParticipantAttributesChanged, syncParticipants); return () => { room.off(RoomEvent.ParticipantConnected, syncParticipants); room.off(RoomEvent.ParticipantDisconnected, syncParticipants); room.off(RoomEvent.TrackPublished, syncParticipants); room.off(RoomEvent.TrackUnpublished, syncParticipants); room.off(RoomEvent.TrackSubscribed, syncParticipants); room.off(RoomEvent.TrackUnsubscribed, syncParticipants); room.off(RoomEvent.ParticipantAttributesChanged, syncParticipants); }; }, [room]); const users = useMemo(() => { const participants = [ ...room.remoteParticipants.values(), room.localParticipant, ]; return participants.filter((participant) => { const participantId = Number(participant.identity); return Number.isInteger(participantId) && participantId > 0; }); // eslint-disable-next-line }, [participantVersion, room]); const userIds = useMemo( () => users.map((participant) => Number(participant.identity)), [users], ); const activeScreenShareParticipantIdSet = useMemo( () => new Set(activeScreenShareParticipantIds), [activeScreenShareParticipantIds], ); const tiles = useMemo( () => [ ...activeScreenShareParticipantIds .filter((id) => id !== focusedParticipantId) .map((participantId) => ({ key: `stream:${participantId}`, kind: "stream" as const, participantId, })), ...userIds .filter((id) => id !== focusedParticipantId) .filter((id) => !activeScreenShareParticipantIdSet.has(id)) .map((participantId) => ({ key: `user:${participantId}`, kind: "user" as const, participantId, })), ], [ activeScreenShareParticipantIds, activeScreenShareParticipantIdSet, userIds, focusedParticipantId, ], ); useLayoutEffect(() => { const container = containerRef.current; const focusedTile = focusedTileRef.current; if (!container || !focusedTile) { return; } let frameId = 0; const syncTileLayout = () => { const containerWidth = Math.floor( container.getBoundingClientRect().width, ); const tileWidth = Math.floor(focusedTile.getBoundingClientRect().width); setIsFocusedTileFlush(Math.abs(containerWidth - tileWidth) <= 1); }; const scheduleSyncTileLayout = () => { cancelAnimationFrame(frameId); frameId = requestAnimationFrame(syncTileLayout); }; syncTileLayout(); const observer = new ResizeObserver(() => { scheduleSyncTileLayout(); }); observer.observe(container); observer.observe(focusedTile); window.addEventListener("resize", scheduleSyncTileLayout); return () => { observer.disconnect(); cancelAnimationFrame(frameId); window.removeEventListener("resize", scheduleSyncTileLayout); }; }, [ layoutVersion, tiles.length, focusedParticipantId, usersInFocusedViewHidden, ]); if (focusedParticipantId == null) { return null; } function getParticipantById(participantId: number) { if (Number(room.localParticipant.identity) === participantId) { return room.localParticipant; } return room.getParticipantByIdentity(String(participantId)); } const focusedParticipant = getParticipantById(focusedParticipantId); const focusedParticipantHasActiveScreenShare = activeScreenShareParticipantIdSet.has(focusedParticipantId); const focusedTileType: "user" | "stream" = focusedParticipantType === "stream" && focusedParticipantHasActiveScreenShare ? "stream" : "user"; const isImmersiveFocusedView = callIsFullscreen && usersInFocusedViewHidden; return (
0 && !isImmersiveFocusedView ? STACK_GAP_PX : 0, }} >
{tiles.length > 0 && ( )}
); }