redo roomcallnavstatus ui, force user preferred mute/video states when first joining calls, update variable names and remove unnecessary logic

This commit is contained in:
YoJames2019
2026-02-09 22:17:28 -05:00
parent e481116b04
commit 990a92a32c
4 changed files with 175 additions and 169 deletions

View File

@@ -65,39 +65,24 @@ const DEFAULT_CALL_ACTIVE = false;
export function CallProvider({ children }: CallProviderProps) {
const [activeCallRoomId, setActiveCallRoomIdState] = useState<string | null>(null);
const [viewedCallRoomId, setViewedCallRoomIdState] = useState<string | null>(null);
const [activeClientWidgetApi, setActiveClientWidgetApiState] = useState<ClientWidgetApi | null>(
null
);
const [activeClientWidgetApi, setActiveClientWidgetApiState] = useState<ClientWidgetApi | null>(null);
const [activeClientWidget, setActiveClientWidget] = useState<SmallWidget | null>(null);
const [activeClientWidgetApiRoomId, setActiveClientWidgetApiRoomId] = useState<string | null>(
null
);
const [activeClientWidgetApiRoomId, setActiveClientWidgetApiRoomId] = useState<string | null>(null);
const [isAudioEnabled, setIsAudioEnabledState] = useState<boolean>(DEFAULT_AUDIO_ENABLED);
const [isVideoEnabled, setIsVideoEnabledState] = useState<boolean>(DEFAULT_VIDEO_ENABLED);
const [isChatOpen, setIsChatOpenState] = useState<boolean>(DEFAULT_CHAT_OPENED);
const [isActiveCallReady, setIsCallActive] = useState<boolean>(DEFAULT_CALL_ACTIVE);
const [isActiveCallReady, setIsActiveCallReady] = useState<boolean>(DEFAULT_CALL_ACTIVE);
const { roomIdOrAlias: viewedRoomId } = useParams<{ roomIdOrAlias: string }>();
const resetMediaState = useCallback(() => {
logger.debug('CallContext: Resetting media state to defaults.');
setIsAudioEnabledState(DEFAULT_AUDIO_ENABLED);
setIsVideoEnabledState(DEFAULT_VIDEO_ENABLED);
}, []);
const setActiveCallRoomId = useCallback(
(roomId: string | null) => {
logger.warn(`CallContext: Setting activeCallRoomId to ${roomId}`);
const previousRoomId = activeCallRoomId;
setActiveCallRoomIdState(roomId);
if (roomId !== previousRoomId) {
logger.debug(`CallContext: Active call room changed, resetting media state.`);
resetMediaState();
}
},
[resetMediaState, activeCallRoomId]
[]
);
const setViewedCallRoomId = useCallback(
@@ -136,10 +121,9 @@ export function CallProvider({ children }: CallProviderProps) {
setActiveClientWidgetApi(clientWidgetApi, clientWidget, roomId);
} else if (roomId === activeClientWidgetApiRoomId || roomId === null) {
setActiveClientWidgetApi(null, null, null);
resetMediaState();
}
},
[activeClientWidgetApi, activeClientWidgetApiRoomId, setActiveClientWidgetApi, resetMediaState]
[activeClientWidgetApi, activeClientWidgetApiRoomId, setActiveClientWidgetApi]
);
const hangUp = useCallback(
@@ -147,7 +131,7 @@ export function CallProvider({ children }: CallProviderProps) {
setActiveClientWidgetApi(null, null, null);
setActiveCallRoomIdState(null);
activeClientWidgetApi?.transport.send(`${WIDGET_HANGUP_ACTION}`, {});
setIsCallActive(false);
setIsActiveCallReady(false);
logger.debug(`CallContext: Hang up called.`);
},
@@ -157,6 +141,71 @@ export function CallProvider({ children }: CallProviderProps) {
]
);
const sendWidgetAction = useCallback(
async <T = unknown,>(action: WidgetApiToWidgetAction | string, data: T): Promise<void> => {
if (!activeClientWidgetApi) {
logger.warn(
`CallContext: Cannot send action '${action}', no active API clientWidgetApi registered.`
);
return Promise.reject(new Error('No active call clientWidgetApi'));
}
if (!activeClientWidgetApiRoomId || activeClientWidgetApiRoomId !== activeCallRoomId) {
logger.debug(
`CallContext: Cannot send action '${action}', clientWidgetApi room (${activeClientWidgetApiRoomId}) does not match active call room (${activeCallRoomId}). Stale clientWidgetApi?`
);
return Promise.reject(new Error('Mismatched active call clientWidgetApi'));
}
logger.debug(
`CallContext: Sending action '${action}' via active clientWidgetApi (room: ${activeClientWidgetApiRoomId}) with data:`,
data
);
await activeClientWidgetApi.transport.send(action as WidgetApiAction, data);
},
[activeClientWidgetApi, activeCallRoomId, activeClientWidgetApiRoomId]
);
const toggleAudio = useCallback(async () => {
const newState = !isAudioEnabled;
logger.debug(`CallContext: Toggling audio. New state: enabled=${newState}`);
setIsAudioEnabledState(newState);
if(isActiveCallReady) {
try {
await sendWidgetAction(WIDGET_MEDIA_STATE_UPDATE_ACTION, {
audio_enabled: newState,
video_enabled: isVideoEnabled,
});
logger.debug(`CallContext: Successfully sent audio toggle action.`);
} catch (error) {
setIsAudioEnabledState(!newState);
throw error;
}
}
}, [isAudioEnabled, isVideoEnabled, sendWidgetAction, isActiveCallReady]);
const toggleVideo = useCallback(async () => {
const newState = !isVideoEnabled;
logger.debug(`CallContext: Toggling video. New state: enabled=${newState}`);
setIsVideoEnabledState(newState);
if(isActiveCallReady){
try {
await sendWidgetAction(WIDGET_MEDIA_STATE_UPDATE_ACTION, {
audio_enabled: isAudioEnabled,
video_enabled: newState,
});
logger.debug(`CallContext: Successfully sent video toggle action.`);
} catch (error) {
setIsVideoEnabledState(!newState);
throw error;
}
}
}, [isVideoEnabled, isAudioEnabled, sendWidgetAction, isActiveCallReady]);
useEffect(() => {
if (!activeCallRoomId && !viewedCallRoomId) {
return;
@@ -179,6 +228,7 @@ export function CallProvider({ children }: CallProviderProps) {
};
const handleMediaStateUpdate = (ev: CustomEvent<MediaStatePayload>) => {
if(!isActiveCallReady) return;
ev.preventDefault();
logger.debug(
`CallContext: Received media state update from widget in room ${activeCallRoomId}:`,
@@ -228,13 +278,19 @@ export function CallProvider({ children }: CallProviderProps) {
});
logger.debug('Join Call');
observer.observe(iframeDoc, { childList: true, subtree: true });
setIsCallActive(true);
setIsActiveCallReady(true);
};
logger.debug(
`CallContext: Setting up listeners for clientWidgetApi in room ${activeCallRoomId}`
);
sendWidgetAction(WIDGET_MEDIA_STATE_UPDATE_ACTION, {
audio_enabled: isAudioEnabled,
video_enabled: isVideoEnabled,
});
api.on(`action:${WIDGET_HANGUP_ACTION}`, handleHangup);
api.on(`action:${WIDGET_MEDIA_STATE_UPDATE_ACTION}`, handleMediaStateUpdate);
api.on(`action:${WIDGET_TILE_UPDATE}`, handleOnTileLayout);
@@ -255,65 +311,9 @@ export function CallProvider({ children }: CallProviderProps) {
setViewedCallRoomId,
activeClientWidget?.iframe?.contentDocument,
activeClientWidget?.iframe?.contentWindow?.document,
sendWidgetAction
]);
const sendWidgetAction = useCallback(
async <T = unknown,>(action: WidgetApiToWidgetAction | string, data: T): Promise<void> => {
if (!activeClientWidgetApi) {
logger.warn(
`CallContext: Cannot send action '${action}', no active API clientWidgetApi registered.`
);
return Promise.reject(new Error('No active call clientWidgetApi'));
}
if (!activeClientWidgetApiRoomId || activeClientWidgetApiRoomId !== activeCallRoomId) {
logger.debug(
`CallContext: Cannot send action '${action}', clientWidgetApi room (${activeClientWidgetApiRoomId}) does not match active call room (${activeCallRoomId}). Stale clientWidgetApi?`
);
return Promise.reject(new Error('Mismatched active call clientWidgetApi'));
}
logger.debug(
`CallContext: Sending action '${action}' via active clientWidgetApi (room: ${activeClientWidgetApiRoomId}) with data:`,
data
);
await activeClientWidgetApi.transport.send(action as WidgetApiAction, data);
},
[activeClientWidgetApi, activeCallRoomId, activeClientWidgetApiRoomId]
);
const toggleAudio = useCallback(async () => {
const newState = !isAudioEnabled;
logger.debug(`CallContext: Toggling audio. New state: enabled=${newState}`);
setIsAudioEnabledState(newState);
try {
await sendWidgetAction(WIDGET_MEDIA_STATE_UPDATE_ACTION, {
audio_enabled: newState,
video_enabled: isVideoEnabled,
});
logger.debug(`CallContext: Successfully sent audio toggle action.`);
} catch (error) {
setIsAudioEnabledState(!newState);
throw error;
}
}, [isAudioEnabled, isVideoEnabled, sendWidgetAction]);
const toggleVideo = useCallback(async () => {
const newState = !isVideoEnabled;
logger.debug(`CallContext: Toggling video. New state: enabled=${newState}`);
setIsVideoEnabledState(newState);
try {
await sendWidgetAction(WIDGET_MEDIA_STATE_UPDATE_ACTION, {
audio_enabled: isAudioEnabled,
video_enabled: newState,
});
logger.debug(`CallContext: Successfully sent video toggle action.`);
} catch (error) {
setIsVideoEnabledState(!newState);
throw error;
}
}, [isVideoEnabled, isAudioEnabled, sendWidgetAction]);
const toggleChat = useCallback(async () => {
const newState = !isChatOpen;
setIsChatOpenState(newState);

View File

@@ -19,12 +19,12 @@ interface PersistentCallContainerProps {
children: ReactNode;
}
export const PrimaryRefContext = createContext(null);
export const CallRefContext = createContext(null);
export function PersistentCallContainer({ children }: PersistentCallContainerProps) {
const primaryIframeRef = useRef<HTMLIFrameElement | null>(null);
const primaryWidgetApiRef = useRef<ClientWidgetApi | null>(null);
const primarySmallWidgetRef = useRef<SmallWidget | null>(null);
const callIframeRef = useRef<HTMLIFrameElement | null>(null);
const callWidgetApiRef = useRef<ClientWidgetApi | null>(null);
const callSmallWidgetRef = useRef<SmallWidget | null>(null);
const {
activeCallRoomId,
@@ -39,11 +39,6 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
const screenSize = useScreenSizeContext();
const theme = useTheme()
const isMobile = screenSize === ScreenSize.Mobile;
const { roomIdOrAlias: viewedRoomId } = useParams();
const isViewingActiveCall = useMemo(
() => activeCallRoomId !== null && activeCallRoomId === viewedRoomId,
[activeCallRoomId, viewedRoomId]
);
/* eslint-disable no-param-reassign */
@@ -56,17 +51,19 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
themeKind: ThemeKind | null
) => {
if (mx?.getUserId()) {
logger.debug(`CallContextJ: iframe src - ${iframeRef.current.src}`)
logger.debug(`CallContextJ: activeCallRoomId: ${activeCallRoomId} viewedId: ${viewedCallRoomId} isactive: ${isActiveCallReady}`)
logger.debug(`PersistentCallContainer: (setupWidget) activeCallRoomId: ${activeCallRoomId} viewedId: ${viewedCallRoomId} isactive: ${isActiveCallReady}`)
if (
(activeCallRoomId !== viewedCallRoomId && isActiveCallReady) ||
(activeCallRoomId && !isActiveCallReady) ||
(!activeCallRoomId && viewedCallRoomId && !isActiveCallReady)
) {
const roomIdToSet = (skipLobby ? activeCallRoomId : viewedCallRoomId) ?? '';
if (roomIdToSet === '') {
return;
}
const widgetId = `element-call-${roomIdToSet}-${Date.now()}`;
const newUrl = getWidgetUrl(
mx,
@@ -82,8 +79,8 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
);
if (
primarySmallWidgetRef.current?.roomId &&
(activeClientWidget?.roomId && activeClientWidget.roomId === primarySmallWidgetRef.current?.roomId)
callSmallWidgetRef.current?.roomId &&
(activeClientWidget?.roomId && activeClientWidget.roomId === callSmallWidgetRef.current?.roomId)
) {
return;
}
@@ -134,30 +131,27 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
]
);
useEffect(() => {
logger.debug(`CallContextJ: ${isActiveCallReady} ${isViewingActiveCall}`)
}, [isActiveCallReady, isViewingActiveCall])
useEffect(() => {
if (activeCallRoomId){
setupWidget(primaryWidgetApiRef, primarySmallWidgetRef, primaryIframeRef, true, theme.kind);
logger.debug(`CallContextJ: set primary widget: ${primaryWidgetApiRef.current?.eventNames()} ${primarySmallWidgetRef.current} ${primaryIframeRef.current?.baseURI}`)
setupWidget(callWidgetApiRef, callSmallWidgetRef, callIframeRef, true, theme.kind);
logger.debug(`PersistentCallContainer: set call widget: ${callWidgetApiRef.current?.eventNames()} ${callSmallWidgetRef.current} ${callIframeRef.current?.baseURI}`)
}
}, [
theme,
setupWidget,
primaryWidgetApiRef,
primarySmallWidgetRef,
primaryIframeRef,
callWidgetApiRef,
callSmallWidgetRef,
callIframeRef,
registerActiveClientWidgetApi,
activeCallRoomId,
viewedCallRoomId,
isActiveCallReady
]);
const memoizedIframeRef = useMemo(() => primaryIframeRef, [primaryIframeRef]);
const memoizedIframeRef = useMemo(() => callIframeRef, [callIframeRef]);
return (
<PrimaryRefContext.Provider value={memoizedIframeRef}>
<CallRefContext.Provider value={memoizedIframeRef}>
<Box grow="No">
<Box
direction="Column"
@@ -176,7 +170,7 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
}}
>
<iframe
ref={primaryIframeRef}
ref={callIframeRef}
style={{
position: 'absolute',
top: 0,
@@ -195,6 +189,6 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
</Box>
</Box>
{children}
</PrimaryRefContext.Provider>
</CallRefContext.Provider>
);
}