import { Room } from 'matrix-js-sdk'; import React, { useContext, useCallback, useEffect, useRef, MouseEventHandler, useState, ReactNode } from 'react'; import { Box, Button, config, Spinner, Text } from 'folds'; import { useCallState } from '../../pages/client/call/CallProvider'; import { useCallMembers } from '../../hooks/useCallMemberships'; import { CallRefContext, } from '../../pages/client/call/PersistentCallContainer'; import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize'; import { useDebounce } from '../../hooks/useDebounce'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { CallViewUser } from './CallViewUser'; import { useRoomNavigate } from '../../hooks/useRoomNavigate'; import { getMemberDisplayName } from '../../utils/room'; import { getMxIdLocalPart } from '../../utils/matrix'; import * as css from "./CallView.css" type OriginalStyles = { position?: string; top?: string; left?: string; width?: string; height?: string; zIndex?: string; display?: string; visibility?: string; pointerEvents?: string; border?: string; }; export function CallViewUserGrid({ children }: { children: ReactNode }) { return ( {children} ); } export function CallView({ room }: { room: Room }) { const callIframeRef = useContext(CallRefContext); const iframeHostRef = useRef(null); const originalIframeStylesRef = useRef(null); const mx = useMatrixClient(); const [visibleCallNames, setVisibleCallNames] = useState("") const { isActiveCallReady, activeCallRoomId, isChatOpen, setActiveCallRoomId, hangUp, setViewedCallRoomId } = useCallState(); const isActiveCallRoom = activeCallRoomId === room.roomId const callIsCurrentAndReady = isActiveCallRoom && isActiveCallReady; const callMembers = useCallMembers(mx, room.roomId) const getName = (userId: string) => getMemberDisplayName(room, userId) ?? getMxIdLocalPart(userId); const memberDisplayNames = callMembers.map(callMembership => getName(callMembership.sender ?? '')) const { navigateRoom } = useRoomNavigate(); const screenSize = useScreenSizeContext(); const isMobile = screenSize === ScreenSize.Mobile; const activeIframeDisplayRef = callIframeRef const applyFixedPositioningToIframe = useCallback(() => { const iframeElement = activeIframeDisplayRef?.current; const hostElement = iframeHostRef?.current; if (iframeElement && hostElement) { if (!originalIframeStylesRef.current) { const computed = window.getComputedStyle(iframeElement); originalIframeStylesRef.current = { position: iframeElement.style.position || computed.position, top: iframeElement.style.top || computed.top, left: iframeElement.style.left || computed.left, width: iframeElement.style.width || computed.width, height: iframeElement.style.height || computed.height, zIndex: iframeElement.style.zIndex || computed.zIndex, display: iframeElement.style.display || computed.display, visibility: iframeElement.style.visibility || computed.visibility, pointerEvents: iframeElement.style.pointerEvents || computed.pointerEvents, border: iframeElement.style.border || computed.border, }; } const hostRect = hostElement.getBoundingClientRect(); iframeElement.style.position = 'fixed'; iframeElement.style.top = `${hostRect.top}px`; iframeElement.style.left = `${hostRect.left}px`; iframeElement.style.width = `${hostRect.width}px`; iframeElement.style.height = `${hostRect.height}px`; iframeElement.style.border = 'none'; iframeElement.style.zIndex = '1000'; iframeElement.style.display = room.isCallRoom() ? 'block' : 'none'; iframeElement.style.visibility = 'visible'; iframeElement.style.pointerEvents = 'auto'; } }, [activeIframeDisplayRef, room]); const debouncedApplyFixedPositioning = useDebounce(applyFixedPositioningToIframe, { wait: 50, immediate: false, }); useEffect(() => { const iframeElement = activeIframeDisplayRef?.current; const hostElement = iframeHostRef?.current; if (room.isCallRoom() || (callIsCurrentAndReady && iframeElement && hostElement)) { applyFixedPositioningToIframe(); const resizeObserver = new ResizeObserver(debouncedApplyFixedPositioning); if (hostElement) resizeObserver.observe(hostElement); window.addEventListener('scroll', debouncedApplyFixedPositioning, true); return () => { resizeObserver.disconnect(); window.removeEventListener('scroll', debouncedApplyFixedPositioning, true); if (iframeElement && originalIframeStylesRef.current) { const originalStyles = originalIframeStylesRef.current; (Object.keys(originalStyles) as Array).forEach((key) => { if (key in iframeElement.style) { iframeElement.style[key as any] = originalStyles[key] || ''; } }); } originalIframeStylesRef.current = null; }; } return undefined; }, [ activeIframeDisplayRef, applyFixedPositioningToIframe, debouncedApplyFixedPositioning, callIsCurrentAndReady, room, ]); const handleJoinVCClick: MouseEventHandler = (evt) => { if (isMobile) { evt.stopPropagation(); setViewedCallRoomId(room.roomId); navigateRoom(room.roomId); } if (!callIsCurrentAndReady) { hangUp(); setActiveCallRoomId(room.roomId); } }; const isCallViewVisible = room.isCallRoom() && (screenSize === ScreenSize.Desktop || !isChatOpen); useEffect(() => { if(memberDisplayNames.length <= 2){ setVisibleCallNames(memberDisplayNames.join(" and ")) } else { const visible = memberDisplayNames.slice(0, 2); const remaining = memberDisplayNames.length - 2; setVisibleCallNames(`${visible.join(", ")}, and ${remaining} other${remaining > 1 ? "s" : ""}`) } }, [memberDisplayNames]) return (
{callMembers.map(callMember => ( )).slice(0, 6)} {room.name} {visibleCallNames !== "" ? visibleCallNames : "No one"} {memberDisplayNames.length > 1 ? "are" : "is"} currently in voice ); }