diff --git a/src/app/pages/call/PersistentCallContainer.tsx b/src/app/pages/call/PersistentCallContainer.tsx index 1dfa03e0..7c4b5354 100644 --- a/src/app/pages/call/PersistentCallContainer.tsx +++ b/src/app/pages/call/PersistentCallContainer.tsx @@ -1,209 +1,149 @@ -import React, { useEffect, useRef, useMemo } from 'react'; +import React, { useEffect, useRef } from 'react'; import { logger } from 'matrix-js-sdk/lib/logger'; -import { ClientWidgetApi, IWidgetApiRequest, MatrixCapabilities } from 'matrix-widget-api'; // Assuming imports - -// --- Required Imports (Adjust paths as needed) --- +import { ClientWidgetApi, IWidgetApiRequest } from 'matrix-widget-api'; +import { Box } from 'folds'; import { useCallState } from '../client/CallProvider'; import { createVirtualWidget, - Edget, + SmallWidget, getWidgetData, getWidgetUrl, } from '../../features/room/SmallWidget'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { RoomViewHeader } from '../../features/room/RoomViewHeader'; -import { PowerLevelsContextProvider, usePowerLevels } from '../../hooks/usePowerLevels'; -import { Box } from 'folds'; -import { IsDirectRoomProvider, RoomProvider, useRoom } from '../../hooks/useRoom'; -import { useSelectedRoom } from '../../hooks/router/useSelectedRoom'; import { Page, PageRoot } from '../../components/page'; import { RouteSpaceProvider, Space, SpaceRouteRoomProvider } from '../client/space'; import { MobileFriendlyPageNav } from '../MobileFriendly'; import { SPACE_PATH } from '../paths'; -import { SpaceProvider } from '../../hooks/useSpace'; -import { useSelectedSpace } from '../../hooks/router/useSelectedSpace'; -import { useAtomValue } from 'jotai'; -import { mDirectAtom } from '../../state/mDirectList'; +import { PowerLevelsContextProvider } from '../../hooks/usePowerLevels'; +import { useSelectedRoom } from '../../hooks/router/useSelectedRoom'; -// --- Component Props --- interface PersistentCallContainerProps { - isVisible: boolean; // Prop passed from parent to control display + isVisible: boolean; } -// --- PersistentCallContainer Component --- - export function PersistentCallContainer({ isVisible }: PersistentCallContainerProps) { - // Global state access - const { activeCallRoomId, setActiveCallRoomId } = useCallState(); // Get setter for hangup action + const { activeCallRoomId, setActiveCallRoomId } = useCallState(); const mx = useMatrixClient(); + const room = useSelectedRoom(); + + logger.info(room); - // Refs const iframeRef = useRef(null); const widgetApiRef = useRef(null); - const edgetRef = useRef(null); + const smallWidgetRef = useRef(null); - // Effect to manage iframe src and Widget API lifecycle based SOLELY on activeCallRoomId useEffect(() => { - // Store the room ID associated with the current Edget instance for clearer cleanup logging - const cleanupRoomId = edgetRef.current?.roomId; + const cleanupRoomId = smallWidgetRef.current?.roomId; logger.debug(`PersistentCallContainer effect running. activeCallRoomId: ${activeCallRoomId}`); - // --- Cleanup Function --- - // This runs BEFORE the effect runs again OR when the component unmounts. - // Crucially, it cleans up resources associated with the *previous* activeCallRoomId. const cleanup = () => { - logger.info(`PersistentCallContainer: Cleaning up for previous room: ${cleanupRoomId}`); - if (edgetRef.current) { - // Ensure stopMessaging handles removing listeners etc. - edgetRef.current.stopMessaging(); + logger.error(`PersistentCallContainer: Cleaning up for previous room: ${cleanupRoomId}`); + if (smallWidgetRef.current) { + smallWidgetRef.current.stopMessaging(); } // Potentially call widgetApi.stop() or similar if the API instance has it if (widgetApiRef.current) { - // widgetApiRef.current.stop?.(); // Example + // widgetApiRef.current.stop?.(); } - // Clear refs for the old instances widgetApiRef.current = null; - edgetRef.current = null; + smallWidgetRef.current = null; }; - // --- Setup Logic for NEW activeCallRoomId --- if (activeCallRoomId && mx?.getUserId()) { - // --- 1. Generate new URL and App definition --- - const newUrl = getWidgetUrl(mx, activeCallRoomId); // Use activeCallRoomId + const newUrl = getWidgetUrl(mx, activeCallRoomId); const userId = mx.getUserId() ?? ''; const app = createVirtualWidget( mx, - `element-call-${activeCallRoomId}`, // ID based on active room + `element-call-${activeCallRoomId}`, userId, 'Element Call', 'm.call', newUrl, false, - // Pass activeCallRoomId to getWidgetData - getWidgetData(mx, activeCallRoomId, {}, { skipLobby: true /* other configs */ }), - activeCallRoomId // Pass activeCallRoomId as the room ID for the widget context + getWidgetData(mx, activeCallRoomId, {}, { skipLobby: true }), + activeCallRoomId ); - // --- 2. Update iframe src --- - // This triggers the iframe reload if the src is different. - // The cleanup function from the *previous* effect run will have already cleaned up the old API connection. if (iframeRef.current && iframeRef.current.src !== newUrl.toString()) { logger.info( `PersistentCallContainer: Updating iframe src for ${activeCallRoomId} to ${newUrl.toString()}` ); iframeRef.current.src = newUrl.toString(); } else if (iframeRef.current && !iframeRef.current.src) { - // Handle initial load case if src starts blank logger.info( `PersistentCallContainer: Setting initial iframe src for ${activeCallRoomId} to ${newUrl.toString()}` ); iframeRef.current.src = newUrl.toString(); } - // --- 3. Setup new Widget API connection --- const iframeElement = iframeRef.current; if (!iframeElement) { logger.error('PersistentCallContainer: iframeRef is null, cannot setup API.'); - return cleanup; // Should not happen if iframe is always rendered + return cleanup; } - // Create and store new Edget instance - logger.debug(`PersistentCallContainer: Creating new Edget/API for ${activeCallRoomId}`); - const edget = new Edget(app); - edgetRef.current = edget; // Store ref to new instance + logger.debug(`PersistentCallContainer: Creating new SmallWidget/API for ${activeCallRoomId}`); + const smallWidget = new SmallWidget(app); + smallWidgetRef.current = smallWidget; try { - const widgetApiInstance = edget.startMessaging(iframeElement); - widgetApiRef.current = widgetApiInstance; // Store ref to new instance - - // --- 4. Add necessary listeners to the NEW widgetApiInstance --- + const widgetApiInstance = smallWidget.startMessaging(iframeElement); + widgetApiRef.current = widgetApiInstance; widgetApiInstance.once('ready', () => { logger.info(`PersistentCallContainer: Widget for ${activeCallRoomId} is ready.`); }); - // Example listener for read_events (adjust as needed) + /* Default handling seems bugged on this. Element does not need this in their driver or codebase, but + we do. I believe down the road update_state will be used by element-call and this can be removed. + */ widgetApiInstance.on( 'action:org.matrix.msc2876.read_events', (ev: CustomEvent) => { logger.info(`PersistentCallContainer: Widget requested 'read_events':`, ev.detail.data); - ev.preventDefault(); // Prevent default driver handling - // Use the current widgetApiRef to reply + ev.preventDefault(); widgetApiRef.current?.transport?.reply(ev.detail, { approved: true }); } ); - // Listener for hangup action from the widget widgetApiInstance.on('action:im.vector.hangup', () => { logger.info( `PersistentCallContainer: Received hangup action from widget in room ${activeCallRoomId}.` ); - // Call the global state function to clear the active call - // Check if we are still the active call before clearing, prevents race conditions - if (edgetRef.current?.roomId === activeCallRoomId) { + if (smallWidgetRef.current?.roomId === activeCallRoomId) { setActiveCallRoomId(null); } }); - - // Add other listeners (TURN servers, etc.)... } catch (error) { logger.error( `PersistentCallContainer: Error initializing widget messaging for ${activeCallRoomId}:`, error ); - // Cleanup immediately if setup fails cleanup(); } } else { - // --- No active call --- - // Ensure src is blank and perform cleanup for any previous instance if (iframeRef.current && iframeRef.current.src !== 'about:blank') { logger.info('PersistentCallContainer: No active call, setting src to about:blank'); iframeRef.current.src = 'about:blank'; } - // Run cleanup in case an instance was active before becoming null cleanup(); } - - // Return the cleanup function to be executed when dependencies change or component unmounts return cleanup; - - // CRITICAL Dependencies: This effect manages the lifecycle based on the active call ID }, [activeCallRoomId, mx, setActiveCallRoomId]); - // --- Render --- - // Apply conditional styling based on the isVisible prop passed by the parent const containerStyle: React.CSSProperties = { width: '100%', height: '100%', - // --- Visibility Control --- top: '1', left: '1', - display: isVisible ? 'flex' : 'none', // Use flex/block as appropriate for your layout + display: isVisible ? 'flex' : 'none', flexDirection: 'row', - //overflow: 'hidden', }; - const roomId = useSelectedRoom(); - const room = mx.getRoom(roomId); - const selectedSpaceId = useSelectedSpace(); - const space = mx.getRoom(selectedSpaceId); - const mDirects = useAtomValue(mDirectAtom); - const powerLevels = usePowerLevels(room); - - // Assuming necessary imports: React, Box, Page, PageRoot, MobileFriendlyPageNav, - // Space, RoomViewHeader, iframeRef, PowerLevelsContextProvider, - // RouteSpaceProvider, SpaceRouteRoomProvider, SPACE_PATH, powerLevels, containerStyle return ( - // Outer container div controlled by parent via isVisible prop -
- {/* Context provider(s) needed by components inside */} - {/* Pass actual powerLevels if required */} - - {/* Route/Space specific context providers MUST wrap both Space and Header */} - {/* Main layout container inside providers: Flex Row */} - {/* Assuming Box handles flex layout */} - - {/* --- Left Side (Nav/Space) --- */} + + + {activeCallRoomId && room && ( - {' '} - {/* Example style */} - {/* PageRoot likely renders the nav prop */} - {/* Space component requires the providers above */} } @@ -229,51 +165,43 @@ export function PersistentCallContainer({ isVisible }: PersistentCallContainerPr - {/* --- Right Side (Header + Iframe) --- */} - {/* This Box takes remaining space and arranges header/iframe vertically */} - - {/* Header Area */} - - {' '} - {/* Header doesn't grow/shrink */} - {/* RoomViewHeader requires the providers above */} - - - - - - + )} - {/* Iframe Area (takes remaining space) */} - - {' '} - {/* Use relative positioning for absolute child */} -