From abe79ceb6638af52286cfb1c4758af866f9a5f41 Mon Sep 17 00:00:00 2001 From: Gigiaj Date: Thu, 8 May 2025 17:57:57 -0500 Subject: [PATCH] Add CallView --- src/app/features/room/CallView.tsx | 142 +++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 src/app/features/room/CallView.tsx diff --git a/src/app/features/room/CallView.tsx b/src/app/features/room/CallView.tsx new file mode 100644 index 00000000..250de98c --- /dev/null +++ b/src/app/features/room/CallView.tsx @@ -0,0 +1,142 @@ +import { Room } from 'matrix-js-sdk'; +import React from 'react'; +import { useCallback, useEffect, useRef } from 'react'; +import { useOutletContext } from 'react-router-dom'; +import { Box } from 'folds'; +import { RoomViewHeader } from './RoomViewHeader'; + +function debounce any>(func: F, waitFor: number) { + let timeoutId: ReturnType | null = null; + return (...args: Parameters): void => { + if (timeoutId) { + clearTimeout(timeoutId); + } + timeoutId = setTimeout(() => func(...args), waitFor); + }; +} + +type OriginalStyles = { + position?: string; + top?: string; + left?: string; + width?: string; + height?: string; + zIndex?: string; + display?: string; + visibility?: string; + pointerEvents?: string; + border?: string; +}; + +export function CallView({ room, eventId }: { room: Room; eventId?: string }) { + const { iframeRef } = useOutletContext<{ + iframeRef: React.RefObject; + }>(); + const iframeHostRef = useRef(null); + + const originalIframeStylesRef = useRef(null); + + const shouldDisplayCallIFrame = + room && typeof room.isCallRoom === 'function' && room.isCallRoom(); + + const applyFixedPositioningToIframe = useCallback(() => { + const iframeElement = iframeRef?.current; + const hostElement = iframeHostRef?.current; + + if (iframeElement && hostElement) { + // Save original styles only ONCE per "portaling" session + 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(); + + // Apply fixed positioning relative to the viewport, but aligned with the host + 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'; // Ensure it's on top + iframeElement.style.display = 'block'; + iframeElement.style.visibility = 'visible'; + iframeElement.style.pointerEvents = 'auto'; + } + }, [iframeRef, iframeHostRef]); + + const debouncedApplyFixedPositioning = useCallback(debounce(applyFixedPositioningToIframe, 50), [ + applyFixedPositioningToIframe, + ]); + + useEffect(() => { + const iframeElement = iframeRef?.current; + const hostElement = iframeHostRef?.current; + + if (shouldDisplayCallIFrame && iframeElement && hostElement) { + applyFixedPositioningToIframe(); + + const resizeObserver = new ResizeObserver(debouncedApplyFixedPositioning); + 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) => { + iframeElement.style[key] = originalStyles[key] || ''; + }); + } + originalIframeStylesRef.current = null; + }; + } + if (iframeElement && originalIframeStylesRef.current) { + const originalStyles = originalIframeStylesRef.current; + (Object.keys(originalStyles) as Array).forEach((key) => { + iframeElement.style[key] = originalStyles[key] || ''; + }); + originalIframeStylesRef.current = null; + } + }, [ + shouldDisplayCallIFrame, + iframeRef, + applyFixedPositioningToIframe, + debouncedApplyFixedPositioning, + ]); + + return ( + + +
+ + ); +}