Remove unneeded comments and imports and clean up name of Edget to SmallWidget

This commit is contained in:
Gigiaj 2025-04-16 20:13:18 -05:00
parent 9c5fde3258
commit eba9670664

View file

@ -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 { logger } from 'matrix-js-sdk/lib/logger';
import { ClientWidgetApi, IWidgetApiRequest, MatrixCapabilities } from 'matrix-widget-api'; // Assuming imports import { ClientWidgetApi, IWidgetApiRequest } from 'matrix-widget-api';
import { Box } from 'folds';
// --- Required Imports (Adjust paths as needed) ---
import { useCallState } from '../client/CallProvider'; import { useCallState } from '../client/CallProvider';
import { import {
createVirtualWidget, createVirtualWidget,
Edget, SmallWidget,
getWidgetData, getWidgetData,
getWidgetUrl, getWidgetUrl,
} from '../../features/room/SmallWidget'; } from '../../features/room/SmallWidget';
import { useMatrixClient } from '../../hooks/useMatrixClient'; import { useMatrixClient } from '../../hooks/useMatrixClient';
import { RoomViewHeader } from '../../features/room/RoomViewHeader'; 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 { Page, PageRoot } from '../../components/page';
import { RouteSpaceProvider, Space, SpaceRouteRoomProvider } from '../client/space'; import { RouteSpaceProvider, Space, SpaceRouteRoomProvider } from '../client/space';
import { MobileFriendlyPageNav } from '../MobileFriendly'; import { MobileFriendlyPageNav } from '../MobileFriendly';
import { SPACE_PATH } from '../paths'; import { SPACE_PATH } from '../paths';
import { SpaceProvider } from '../../hooks/useSpace'; import { PowerLevelsContextProvider } from '../../hooks/usePowerLevels';
import { useSelectedSpace } from '../../hooks/router/useSelectedSpace'; import { useSelectedRoom } from '../../hooks/router/useSelectedRoom';
import { useAtomValue } from 'jotai';
import { mDirectAtom } from '../../state/mDirectList';
// --- Component Props ---
interface PersistentCallContainerProps { interface PersistentCallContainerProps {
isVisible: boolean; // Prop passed from parent to control display isVisible: boolean;
} }
// --- PersistentCallContainer Component ---
export function PersistentCallContainer({ isVisible }: PersistentCallContainerProps) { export function PersistentCallContainer({ isVisible }: PersistentCallContainerProps) {
// Global state access const { activeCallRoomId, setActiveCallRoomId } = useCallState();
const { activeCallRoomId, setActiveCallRoomId } = useCallState(); // Get setter for hangup action
const mx = useMatrixClient(); const mx = useMatrixClient();
const room = useSelectedRoom();
logger.info(room);
// Refs
const iframeRef = useRef<HTMLIFrameElement | null>(null); const iframeRef = useRef<HTMLIFrameElement | null>(null);
const widgetApiRef = useRef<ClientWidgetApi | null>(null); const widgetApiRef = useRef<ClientWidgetApi | null>(null);
const edgetRef = useRef<Edget | null>(null); const smallWidgetRef = useRef<SmallWidget | null>(null);
// Effect to manage iframe src and Widget API lifecycle based SOLELY on activeCallRoomId
useEffect(() => { useEffect(() => {
// Store the room ID associated with the current Edget instance for clearer cleanup logging const cleanupRoomId = smallWidgetRef.current?.roomId;
const cleanupRoomId = edgetRef.current?.roomId;
logger.debug(`PersistentCallContainer effect running. activeCallRoomId: ${activeCallRoomId}`); 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 = () => { const cleanup = () => {
logger.info(`PersistentCallContainer: Cleaning up for previous room: ${cleanupRoomId}`); logger.error(`PersistentCallContainer: Cleaning up for previous room: ${cleanupRoomId}`);
if (edgetRef.current) { if (smallWidgetRef.current) {
// Ensure stopMessaging handles removing listeners etc. smallWidgetRef.current.stopMessaging();
edgetRef.current.stopMessaging();
} }
// Potentially call widgetApi.stop() or similar if the API instance has it // Potentially call widgetApi.stop() or similar if the API instance has it
if (widgetApiRef.current) { if (widgetApiRef.current) {
// widgetApiRef.current.stop?.(); // Example // widgetApiRef.current.stop?.();
} }
// Clear refs for the old instances
widgetApiRef.current = null; widgetApiRef.current = null;
edgetRef.current = null; smallWidgetRef.current = null;
}; };
// --- Setup Logic for NEW activeCallRoomId ---
if (activeCallRoomId && mx?.getUserId()) { if (activeCallRoomId && mx?.getUserId()) {
// --- 1. Generate new URL and App definition --- const newUrl = getWidgetUrl(mx, activeCallRoomId);
const newUrl = getWidgetUrl(mx, activeCallRoomId); // Use activeCallRoomId
const userId = mx.getUserId() ?? ''; const userId = mx.getUserId() ?? '';
const app = createVirtualWidget( const app = createVirtualWidget(
mx, mx,
`element-call-${activeCallRoomId}`, // ID based on active room `element-call-${activeCallRoomId}`,
userId, userId,
'Element Call', 'Element Call',
'm.call', 'm.call',
newUrl, newUrl,
false, false,
// Pass activeCallRoomId to getWidgetData getWidgetData(mx, activeCallRoomId, {}, { skipLobby: true }),
getWidgetData(mx, activeCallRoomId, {}, { skipLobby: true /* other configs */ }), activeCallRoomId
activeCallRoomId // Pass activeCallRoomId as the room ID for the widget context
); );
// --- 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()) { if (iframeRef.current && iframeRef.current.src !== newUrl.toString()) {
logger.info( logger.info(
`PersistentCallContainer: Updating iframe src for ${activeCallRoomId} to ${newUrl.toString()}` `PersistentCallContainer: Updating iframe src for ${activeCallRoomId} to ${newUrl.toString()}`
); );
iframeRef.current.src = newUrl.toString(); iframeRef.current.src = newUrl.toString();
} else if (iframeRef.current && !iframeRef.current.src) { } else if (iframeRef.current && !iframeRef.current.src) {
// Handle initial load case if src starts blank
logger.info( logger.info(
`PersistentCallContainer: Setting initial iframe src for ${activeCallRoomId} to ${newUrl.toString()}` `PersistentCallContainer: Setting initial iframe src for ${activeCallRoomId} to ${newUrl.toString()}`
); );
iframeRef.current.src = newUrl.toString(); iframeRef.current.src = newUrl.toString();
} }
// --- 3. Setup new Widget API connection ---
const iframeElement = iframeRef.current; const iframeElement = iframeRef.current;
if (!iframeElement) { if (!iframeElement) {
logger.error('PersistentCallContainer: iframeRef is null, cannot setup API.'); 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 SmallWidget/API for ${activeCallRoomId}`);
logger.debug(`PersistentCallContainer: Creating new Edget/API for ${activeCallRoomId}`); const smallWidget = new SmallWidget(app);
const edget = new Edget(app); smallWidgetRef.current = smallWidget;
edgetRef.current = edget; // Store ref to new instance
try { try {
const widgetApiInstance = edget.startMessaging(iframeElement); const widgetApiInstance = smallWidget.startMessaging(iframeElement);
widgetApiRef.current = widgetApiInstance; // Store ref to new instance widgetApiRef.current = widgetApiInstance;
// --- 4. Add necessary listeners to the NEW widgetApiInstance ---
widgetApiInstance.once('ready', () => { widgetApiInstance.once('ready', () => {
logger.info(`PersistentCallContainer: Widget for ${activeCallRoomId} is 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( widgetApiInstance.on(
'action:org.matrix.msc2876.read_events', 'action:org.matrix.msc2876.read_events',
(ev: CustomEvent<IWidgetApiRequest>) => { (ev: CustomEvent<IWidgetApiRequest>) => {
logger.info(`PersistentCallContainer: Widget requested 'read_events':`, ev.detail.data); logger.info(`PersistentCallContainer: Widget requested 'read_events':`, ev.detail.data);
ev.preventDefault(); // Prevent default driver handling ev.preventDefault();
// Use the current widgetApiRef to reply
widgetApiRef.current?.transport?.reply(ev.detail, { approved: true }); widgetApiRef.current?.transport?.reply(ev.detail, { approved: true });
} }
); );
// Listener for hangup action from the widget
widgetApiInstance.on('action:im.vector.hangup', () => { widgetApiInstance.on('action:im.vector.hangup', () => {
logger.info( logger.info(
`PersistentCallContainer: Received hangup action from widget in room ${activeCallRoomId}.` `PersistentCallContainer: Received hangup action from widget in room ${activeCallRoomId}.`
); );
// Call the global state function to clear the active call if (smallWidgetRef.current?.roomId === activeCallRoomId) {
// Check if we are still the active call before clearing, prevents race conditions
if (edgetRef.current?.roomId === activeCallRoomId) {
setActiveCallRoomId(null); setActiveCallRoomId(null);
} }
}); });
// Add other listeners (TURN servers, etc.)...
} catch (error) { } catch (error) {
logger.error( logger.error(
`PersistentCallContainer: Error initializing widget messaging for ${activeCallRoomId}:`, `PersistentCallContainer: Error initializing widget messaging for ${activeCallRoomId}:`,
error error
); );
// Cleanup immediately if setup fails
cleanup(); cleanup();
} }
} else { } else {
// --- No active call ---
// Ensure src is blank and perform cleanup for any previous instance
if (iframeRef.current && iframeRef.current.src !== 'about:blank') { if (iframeRef.current && iframeRef.current.src !== 'about:blank') {
logger.info('PersistentCallContainer: No active call, setting src to about:blank'); logger.info('PersistentCallContainer: No active call, setting src to about:blank');
iframeRef.current.src = 'about:blank'; iframeRef.current.src = 'about:blank';
} }
// Run cleanup in case an instance was active before becoming null
cleanup(); cleanup();
} }
// Return the cleanup function to be executed when dependencies change or component unmounts
return cleanup; return cleanup;
// CRITICAL Dependencies: This effect manages the lifecycle based on the active call ID
}, [activeCallRoomId, mx, setActiveCallRoomId]); }, [activeCallRoomId, mx, setActiveCallRoomId]);
// --- Render ---
// Apply conditional styling based on the isVisible prop passed by the parent
const containerStyle: React.CSSProperties = { const containerStyle: React.CSSProperties = {
width: '100%', width: '100%',
height: '100%', height: '100%',
// --- Visibility Control ---
top: '1', top: '1',
left: '1', left: '1',
display: isVisible ? 'flex' : 'none', // Use flex/block as appropriate for your layout display: isVisible ? 'flex' : 'none',
flexDirection: 'row', 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 ( return (
// Outer container div controlled by parent via isVisible prop <Page style={containerStyle}>
<div style={containerStyle}>
{/* Context provider(s) needed by components inside */}
{/* Pass actual powerLevels if required */}
<PowerLevelsContextProvider value={powerLevels}>
{/* Route/Space specific context providers MUST wrap both Space and Header */}
{/* Main layout container inside providers: Flex Row */}
{/* Assuming Box handles flex layout */}
<Box direction="Row" grow="Yes" style={{ height: '100%', width: '100%' }}> <Box direction="Row" grow="Yes" style={{ height: '100%', width: '100%' }}>
{/* --- Left Side (Nav/Space) --- */} {activeCallRoomId && room && (
<Box <Box
shrink="No" shrink="No"
style={{ style={{
@ -215,13 +155,9 @@ export function PersistentCallContainer({ isVisible }: PersistentCallContainerPr
> >
<RouteSpaceProvider> <RouteSpaceProvider>
<SpaceRouteRoomProvider> <SpaceRouteRoomProvider>
{' '}
{/* Example style */}
{/* PageRoot likely renders the nav prop */}
<PageRoot <PageRoot
nav={ nav={
<MobileFriendlyPageNav path={SPACE_PATH}> <MobileFriendlyPageNav path={SPACE_PATH}>
{/* Space component requires the providers above */}
<Space /> <Space />
</MobileFriendlyPageNav> </MobileFriendlyPageNav>
} }
@ -229,33 +165,28 @@ export function PersistentCallContainer({ isVisible }: PersistentCallContainerPr
</SpaceRouteRoomProvider> </SpaceRouteRoomProvider>
</RouteSpaceProvider> </RouteSpaceProvider>
</Box> </Box>
{/* --- Right Side (Header + Iframe) --- */} )}
{/* This Box takes remaining space and arranges header/iframe vertically */}
<Box <Box
grow="Yes" grow="Yes"
direction="Column" direction="Column"
style={{ height: '100%', width: '100%', overflow: 'hidden' }} style={{ height: '100%', width: '100%', overflow: 'hidden' }}
> >
{/* Header Area */} {activeCallRoomId && room && (
<Box grow="No"> <Box grow="No">
{' '} <PowerLevelsContextProvider value={null}>
{/* Header doesn't grow/shrink */}
{/* RoomViewHeader requires the providers above */}
<RouteSpaceProvider> <RouteSpaceProvider>
<SpaceRouteRoomProvider> <SpaceRouteRoomProvider>
<RoomViewHeader /> <RoomViewHeader />
</SpaceRouteRoomProvider> </SpaceRouteRoomProvider>
</RouteSpaceProvider> </RouteSpaceProvider>
</PowerLevelsContextProvider>
</Box> </Box>
)}
{/* Iframe Area (takes remaining space) */}
<Box grow="Yes" style={{ position: 'relative' }}> <Box grow="Yes" style={{ position: 'relative' }}>
{' '}
{/* Use relative positioning for absolute child */}
<iframe <iframe
ref={iframeRef} ref={iframeRef}
style={{ style={{
// Use absolute positioning to fill the parent Box
position: 'absolute', position: 'absolute',
top: 0, top: 0,
left: 0, left: 0,
@ -266,14 +197,11 @@ export function PersistentCallContainer({ isVisible }: PersistentCallContainerPr
title={`Persistent Element Call`} title={`Persistent Element Call`}
sandbox="allow-forms allow-scripts allow-same-origin allow-popups allow-modals allow-downloads" sandbox="allow-forms allow-scripts allow-same-origin allow-popups allow-modals allow-downloads"
allow="microphone; camera; display-capture; autoplay; clipboard-write;" allow="microphone; camera; display-capture; autoplay; clipboard-write;"
src="about:blank" // useEffect sets the correct src src="about:blank"
/> />
</Box> </Box>
</Box>{' '} </Box>
{/* End Right Side Box */} </Box>
</Box>{' '} </Page>
{/* End Main Layout Box (Row) */}
</PowerLevelsContextProvider>
</div> // End Outer Container Div
); );
} }