(broken) juggle the iframe states proper... still needs fixing

This commit is contained in:
Gigiaj 2025-05-10 20:42:21 -05:00
parent 1b89831c10
commit 6601c47abc

View file

@ -1,8 +1,8 @@
import React, { createContext, ReactNode, useEffect, useMemo, useRef } from 'react'; import React, { createContext, ReactNode, useCallback, useEffect, useMemo, useRef } from 'react';
import { logger } from 'matrix-js-sdk/lib/logger'; import { logger } from 'matrix-js-sdk/lib/logger';
import { ClientWidgetApi } from 'matrix-widget-api'; import { ClientWidgetApi } from 'matrix-widget-api';
import { Box } from 'folds'; import { Box } from 'folds';
import { Outlet, useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useCallState } from '../client/CallProvider'; import { useCallState } from '../client/CallProvider';
import { import {
createVirtualWidget, createVirtualWidget,
@ -19,27 +19,28 @@ interface PersistentCallContainerProps {
children: ReactNode; children: ReactNode;
} }
export const RefContext = createContext(null); export const PrimaryRefContext = createContext(null);
export const BackupRefContext = createContext(null);
export function PersistentCallContainer({ children }: PersistentCallContainerProps) { export function PersistentCallContainer({ children }: PersistentCallContainerProps) {
const iframeRef = useRef<HTMLIFrameElement | null>(null); const primaryIframeRef = useRef<HTMLIFrameElement | null>(null);
const widgetApiRef = useRef<ClientWidgetApi | null>(null); const primaryWidgetApiRef = useRef<ClientWidgetApi | null>(null);
const smallWidgetRef = useRef<SmallWidget | null>(null); const primarySmallWidgetRef = useRef<SmallWidget | null>(null);
const backupIframeRef = useRef<HTMLIFrameElement | null>(null); const backupIframeRef = useRef<HTMLIFrameElement | null>(null);
const backupWidgetApiRef = useRef<ClientWidgetApi | null>(null); const backupWidgetApiRef = useRef<ClientWidgetApi | null>(null);
const backupSmallWidgetRef = useRef<SmallWidget | null>(null); const backupSmallWidgetRef = useRef<SmallWidget | null>(null);
const { const {
activeCallRoomId, activeCallRoomId,
viewedCallRoomId,
isChatOpen, isChatOpen,
isCallActive, isCallActive,
isPrimaryIframe, isPrimaryIframe,
registerActiveClientWidgetApi, registerActiveClientWidgetApi,
registerViewedClientWidgetApi,
} = useCallState(); } = useCallState();
const mx = useMatrixClient(); const mx = useMatrixClient();
const roomId = useSelectedRoom();
const clientConfig = useClientConfig(); const clientConfig = useClientConfig();
const room = mx.getRoom(roomId) ?? null;
const screenSize = useScreenSizeContext(); const screenSize = useScreenSizeContext();
const isMobile = screenSize === ScreenSize.Mobile; const isMobile = screenSize === ScreenSize.Mobile;
const { roomIdOrAlias: viewedRoomId } = useParams(); const { roomIdOrAlias: viewedRoomId } = useParams();
@ -48,167 +49,212 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
[activeCallRoomId, viewedRoomId] [activeCallRoomId, viewedRoomId]
); );
logger.info(room); // logger.error('RANDOM LOG RANDOM LOG RANDOM LOG\n\n\n\n\n\n');
// logger.error(room?.normalizedName);
const setupWidget = (widgetApiRef, smallWidgetRef, iframeRef, skipLobby) => { const setupWidget = useCallback(
const cleanupRoomId = smallWidgetRef.current?.roomId; (widgetApiRef, smallWidgetRef, iframeRef, skipLobby) => {
logger.debug(`PersistentCallContainer effect running. activeCallRoomId: ${activeCallRoomId}`); const cleanupRoomId = smallWidgetRef.current?.roomId;
logger.debug(`PersistentCallContainer effect running. activeCallRoomId: ${activeCallRoomId}`);
/** /**
* TODO: * TODO:
* Need proper shutdown handling. Events from the previous widget can still come through it seems. Might need * Need proper shutdown handling. Events from the previous widget can still come through it seems. Might need
* to queue the events and then allow the join to actually occur. This will *work* for now as it does handle switching technically. * to queue the events and then allow the join to actually occur. This will *work* for now as it does handle switching technically.
* Need to look closer at it. * Need to look closer at it.
* *
* Might also be able to keep the iframe alive and instead navigate to a new "room" to make the transition smoother * Might also be able to keep the iframe alive and instead navigate to a new "room" to make the transition smoother
*/ */
const cleanup = () => { const cleanup = () => {
//logger.error(`CallContext: Cleaning up for previous room: ${cleanupRoomId}`); // logger.error(`CallContext: Cleaning up for previous room: ${cleanupRoomId}`);
if (smallWidgetRef.current) { if (smallWidgetRef.current) {
// smallWidgetRef.current.stopMessaging(); // smallWidgetRef.current.stopMessaging();
}
// Potentially call widgetApi.stop() or similar if the API instance has it
if (widgetApiRef.current) {
// widgetApiRef.current.stop?.();
}
//widgetApiRef.current = null;
//smallWidgetRef.current = null;
//if (iframeRef.current) iframeRef.current.src = 'about:blank';
};
if (activeCallRoomId && mx?.getUserId()) {
if (cleanupRoomId !== activeCallRoomId && !isCallActive) {
const newUrl = getWidgetUrl(mx, roomId, clientConfig.elementCallUrl ?? '', {
skipLobby,
returnToLobby: 'true',
perParticipentE2EE: 'true',
});
if (iframeRef.current && iframeRef.current.src !== newUrl.toString()) {
logger.info(
`PersistentCallContainer: Updating iframe src for ${activeCallRoomId} to ${newUrl.toString()}`
);
cleanup();
iframeRef.current.src = newUrl.toString();
} else if (iframeRef.current && !iframeRef.current.src) {
logger.info(
`PersistentCallContainer: Setting initial iframe src for ${activeCallRoomId} to ${newUrl.toString()}`
);
iframeRef.current.src = newUrl.toString();
} }
// Potentially call widgetApi.stop() or similar if the API instance has it
const iframeElement = iframeRef.current; if (widgetApiRef.current) {
if (!iframeElement) { // widgetApiRef.current.stop?.();
logger.error('PersistentCallContainer: iframeRef is null, cannot setup API.');
return cleanup;
} }
// widgetApiRef.current = null;
// smallWidgetRef.current = null;
// if (iframeRef.current) iframeRef.current.src = 'about:blank';
};
const userId = mx.getUserId() ?? ''; if (mx?.getUserId()) {
const app = createVirtualWidget( if (
mx, (isCallActive && activeCallRoomId !== viewedCallRoomId) ||
`element-call-${activeCallRoomId}-${Date.now()}`, // && backupIframeRef.current && primaryIframeRef.current.src
userId, (cleanupRoomId !== activeCallRoomId && !isCallActive)
'Element Call', ) {
'm.call', //logger.error('PersistentCallContainer Re-render');
newUrl, const roomIdToSet = skipLobby ? activeCallRoomId : viewedCallRoomId;
false, const newUrl = getWidgetUrl(mx, roomIdToSet, clientConfig.elementCallUrl ?? '', {
getWidgetData(mx, activeCallRoomId, {}, { skipLobby: true }), skipLobby: skipLobby.toString(),
activeCallRoomId returnToLobby: 'true',
); perParticipentE2EE: 'true',
logger.debug(
`PersistentCallContainer: Creating new SmallWidget/API for ${activeCallRoomId}`
);
const smallWidget = new SmallWidget(app);
smallWidgetRef.current = smallWidget;
try {
const widgetApiInstance = smallWidget.startMessaging(iframeElement);
widgetApiRef.current = widgetApiInstance;
if (skipLobby) registerActiveClientWidgetApi(activeCallRoomId, widgetApiRef.current);
widgetApiInstance.once('ready', () => {
logger.info(`PersistentCallContainer: Widget for ${activeCallRoomId} is ready.`);
}); });
} catch (error) { if (iframeRef.current && iframeRef.current.src !== newUrl.toString()) {
logger.error( logger.info(
`PersistentCallContainer: Error initializing widget messaging for ${activeCallRoomId}:`, `PersistentCallContainer: Updating iframe src for ${roomIdToSet} to ${newUrl.toString()}`
error );
cleanup();
iframeRef.current.src = newUrl.toString();
} else if (iframeRef.current && !iframeRef.current.src) {
logger.info(
`PersistentCallContainer: Setting initial iframe src for ${roomIdToSet} to ${newUrl.toString()}`
);
iframeRef.current.src = newUrl.toString();
}
const iframeElement = iframeRef.current;
if (!iframeElement) {
logger.error('PersistentCallContainer: iframeRef is null, cannot setup API.');
return cleanup;
}
const userId = mx.getUserId() ?? '';
const app = createVirtualWidget(
mx,
`element-call-${roomIdToSet}-${Date.now()}`,
userId,
'Element Call',
'm.call',
newUrl,
true,
getWidgetData(mx, roomIdToSet, {}, { skipLobby: true }),
roomIdToSet
); );
cleanup();
} logger.error(`PersistentCallContainer: Creating new SmallWidget/API for ${roomIdToSet}`);
} else { const smallWidget = new SmallWidget(app);
/* smallWidgetRef.current = smallWidget;
try {
const widgetApiInstance = smallWidget.startMessaging(iframeElement);
widgetApiRef.current = widgetApiInstance;
logger.error('Pre-register');
logger.error(`This is our check: ${skipLobby}`);
if (skipLobby) {
registerActiveClientWidgetApi(activeCallRoomId, widgetApiRef.current);
} else {
registerViewedClientWidgetApi(viewedCallRoomId, widgetApiRef.current);
logger.error('Post view register');
}
widgetApiInstance.once('ready', () => {
logger.info(`PersistentCallContainer: Widget for ${roomIdToSet} is ready.`);
});
} catch (error) {
logger.error(
`PersistentCallContainer: Error initializing widget messaging for ${roomIdToSet}:`,
error
);
cleanup();
}
} else {
/*
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';
} }
*/ */
cleanup(); cleanup();
}
} }
} return cleanup;
return cleanup; },
}; [
activeCallRoomId,
mx,
viewedCallRoomId,
isCallActive,
clientConfig.elementCallUrl,
registerActiveClientWidgetApi,
registerViewedClientWidgetApi,
]
);
useEffect(() => { useEffect(() => {
setupWidget(widgetApiRef, smallWidgetRef, iframeRef, true); logger.error(`This is our param: ${isPrimaryIframe}`);
setupWidget(backupWidgetApiRef, backupSmallWidgetRef, backupIframeRef, false); setupWidget(primaryWidgetApiRef, primarySmallWidgetRef, primaryIframeRef, isPrimaryIframe);
}); setupWidget(backupWidgetApiRef, backupSmallWidgetRef, backupIframeRef, !isPrimaryIframe);
}, [
setupWidget,
primaryWidgetApiRef,
primarySmallWidgetRef,
primaryIframeRef,
backupWidgetApiRef,
backupSmallWidgetRef,
backupIframeRef,
registerActiveClientWidgetApi,
registerViewedClientWidgetApi,
activeCallRoomId,
viewedCallRoomId,
isCallActive,
isPrimaryIframe,
]);
const memoizedIframeRef = useMemo(() => primaryIframeRef, [primaryIframeRef]);
const memoizedBackupIframeRef = useMemo(() => backupIframeRef, [backupIframeRef]);
return ( return (
<RefContext.Provider value={{ iframeRef, backupIframeRef }}> <PrimaryRefContext.Provider value={memoizedIframeRef}>
<Box grow="No"> <BackupRefContext.Provider value={memoizedBackupIframeRef}>
<Box <Box grow="No">
direction="Column"
style={{
position: 'relative',
zIndex: 0,
display: isMobile && isChatOpen ? 'none' : 'flex',
width: isMobile && isChatOpen ? '0%' : '100%',
height: isMobile && isChatOpen ? '0%' : '100%',
}}
>
<Box <Box
grow="Yes" direction="Column"
style={{ style={{
position: 'relative', position: 'relative',
zIndex: 0,
display: isMobile && isChatOpen ? 'none' : 'flex',
width: isMobile && isChatOpen ? '0%' : '100%',
height: isMobile && isChatOpen ? '0%' : '100%',
}} }}
> >
<iframe <Box
ref={iframeRef} grow="Yes"
style={{ style={{
position: 'absolute', position: 'relative',
top: 0,
left: 0,
display: isPrimaryIframe || isViewingActiveCall ? 'flex' : 'none',
width: '100%',
height: '100%',
border: 'none',
}} }}
title={`Persistent Element Call`} >
sandbox="allow-forms allow-scripts allow-same-origin allow-popups allow-modals allow-downloads" <iframe
allow="microphone; camera; display-capture; autoplay; clipboard-write;" ref={primaryIframeRef}
src="about:blank" style={{
/> position: 'absolute',
<iframe top: 0,
ref={backupIframeRef} left: 0,
style={{ display: isPrimaryIframe || isViewingActiveCall ? 'flex' : 'none',
position: 'absolute', width: '100%',
top: 0, height: '100%',
left: 0, border: 'none',
width: '100%', }}
height: '100%', title={`Persistent Element Call`}
border: 'none', sandbox="allow-forms allow-scripts allow-same-origin allow-popups allow-modals allow-downloads"
display: !isPrimaryIframe || isViewingActiveCall ? 'flex' : 'none', allow="microphone; camera; display-capture; autoplay; clipboard-write;"
}} src="about:blank"
title={`Persistent Element Call`} />
sandbox="allow-forms allow-scripts allow-same-origin allow-popups allow-modals allow-downloads" <iframe
allow="microphone; camera; display-capture; autoplay; clipboard-write;" ref={backupIframeRef}
src="about:blank" style={{
/> position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
border: 'none',
display: !isPrimaryIframe && isViewingActiveCall ? 'flex' : 'none',
}}
title={`Persistent Element Call`}
sandbox="allow-forms allow-scripts allow-same-origin allow-popups allow-modals allow-downloads"
allow="microphone; camera; display-capture; autoplay; clipboard-write;"
src="about:blank"
/>
</Box>
</Box> </Box>
</Box> </Box>
</Box> {children}
{children} </BackupRefContext.Provider>
</RefContext.Provider> </PrimaryRefContext.Provider>
); );
} }