diff --git a/package-lock.json b/package-lock.json
index 934bb070..8545a5ba 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -74,7 +74,7 @@
"ua-parser-js": "1.0.35"
},
"devDependencies": {
- "@element-hq/element-call-embedded": "0.12.2",
+ "@element-hq/element-call-embedded": "0.16.3",
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
"@rollup/plugin-inject": "5.0.3",
"@rollup/plugin-wasm": "6.1.1",
@@ -1661,9 +1661,9 @@
}
},
"node_modules/@element-hq/element-call-embedded": {
- "version": "0.12.2",
- "resolved": "https://registry.npmjs.org/@element-hq/element-call-embedded/-/element-call-embedded-0.12.2.tgz",
- "integrity": "sha512-2u5/bOARcjc5TFq4929x1R0tvsNbeVA58FBtiW05GlIJCapxzPSOeeGhbqEcJ1TW3/hLGpiKMcw0QwRBQVNzQA==",
+ "version": "0.16.3",
+ "resolved": "https://registry.npmjs.org/@element-hq/element-call-embedded/-/element-call-embedded-0.16.3.tgz",
+ "integrity": "sha512-OViKJonDaDNVBUW9WdV9mk78/Ruh34C7XsEgt3O8D9z+64C39elbIgllHSoH5S12IRlv9RYrrV37FZLo6QWsDQ==",
"dev": true
},
"node_modules/@emotion/hash": {
diff --git a/package.json b/package.json
index 44385104..a7719566 100644
--- a/package.json
+++ b/package.json
@@ -86,7 +86,7 @@
"ua-parser-js": "1.0.35"
},
"devDependencies": {
- "@element-hq/element-call-embedded": "0.12.2",
+ "@element-hq/element-call-embedded": "0.16.3",
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
"@rollup/plugin-inject": "5.0.3",
"@rollup/plugin-wasm": "6.1.1",
@@ -119,5 +119,4 @@
"vite-plugin-static-copy": "1.0.4",
"vite-plugin-top-level-await": "1.4.4"
}
-
}
diff --git a/src/app/features/call/CallView.css.ts b/src/app/features/call/CallView.css.ts
new file mode 100644
index 00000000..ba1146e2
--- /dev/null
+++ b/src/app/features/call/CallView.css.ts
@@ -0,0 +1,37 @@
+import { style } from '@vanilla-extract/css';
+import { DefaultReset, config } from 'folds';
+import { ContainerColor } from '../../styles/ContainerColor.css';
+
+export const CallViewUserGrid = style({
+ display: 'flex',
+ flexWrap: 'wrap',
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginInline: "20px",
+ gap: config.space.S400,
+})
+
+export const CallViewUser = style([
+ DefaultReset,
+ ContainerColor({ variant: 'SurfaceVariant' }),
+ {
+ height: "90px",
+ width: "150px",
+ borderRadius: config.radii.R500,
+ },
+])
+
+export const UserLink = style({
+ color: 'inherit',
+ minWidth: 0,
+ cursor: 'pointer',
+ flexGrow: 0,
+ transition: "all ease-out 200ms",
+ ':hover': {
+ transform: "translateY(-3px)",
+ textDecoration: 'unset',
+ },
+ ':focus': {
+ outline: 'none',
+ },
+});
\ No newline at end of file
diff --git a/src/app/features/call/CallView.tsx b/src/app/features/call/CallView.tsx
index e5118024..3ea2396e 100644
--- a/src/app/features/call/CallView.tsx
+++ b/src/app/features/call/CallView.tsx
@@ -1,13 +1,21 @@
import { Room } from 'matrix-js-sdk';
-import React, { useContext, useMemo, useCallback, useEffect, useRef } from 'react';
-import { Box } from 'folds';
+import React, { useContext, useMemo, useCallback, useEffect, useRef, MouseEventHandler, useState, ReactNode } from 'react';
+import { Box, Button, Spinner, Text } from 'folds';
import { useCallState } from '../../pages/client/call/CallProvider';
+import { useCallMembers } from '../../hooks/useCallMemberships';
+
import {
PrimaryRefContext,
- BackupRefContext,
} from '../../pages/client/call/PersistentCallContainer';
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
import { useDebounce } from '../../hooks/useDebounce';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
+import { CallViewUser } from './CallViewUser';
+import { useRoomNavigate } from '../../hooks/useRoomNavigate';
+import { getMemberDisplayName } from '../../utils/room';
+import { getMxIdLocalPart } from '../../utils/matrix';
+import * as css from "./CallView.css"
+
type OriginalStyles = {
position?: string;
@@ -22,27 +30,49 @@ type OriginalStyles = {
border?: string;
};
+export function CallViewUserGrid({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+
export function CallView({ room }: { room: Room }) {
const primaryIframeRef = useContext(PrimaryRefContext);
- const backupIframeRef = useContext(BackupRefContext);
const iframeHostRef = useRef(null);
const originalIframeStylesRef = useRef(null);
- const { activeCallRoomId, isPrimaryIframe, isChatOpen } = useCallState();
- const isViewingActiveCall = useMemo(
- () => activeCallRoomId !== null && activeCallRoomId === room.roomId,
- [activeCallRoomId, room.roomId]
- );
+ const mx = useMatrixClient();
+
+ const [visibleCallNames, setVisibleCallNames] = useState("")
+ const {
+ isActiveCallReady,
+ activeCallRoomId,
+ isChatOpen,
+ setActiveCallRoomId,
+ hangUp,
+ setViewedCallRoomId
+ } = useCallState();
+
+ const isActiveCallRoom = activeCallRoomId === room.roomId
+ const shouldDisplayCall = isActiveCallRoom && isActiveCallReady;
+ const callMembers = useCallMembers(mx, room.roomId)
+
+ const getName = (userId: string) => getMemberDisplayName(room, userId) ?? getMxIdLocalPart(userId);
+
+ const memberDisplayNames = callMembers.map(callMembership => getName(callMembership.sender ?? ''))
+
+ const { navigateRoom } = useRoomNavigate();
const screenSize = useScreenSizeContext();
+ const isMobile = screenSize === ScreenSize.Mobile;
+
/* eslint-disable-next-line no-nested-ternary */
- const activeIframeDisplayRef = isPrimaryIframe
- ? isViewingActiveCall
- ? primaryIframeRef
- : backupIframeRef
- : isViewingActiveCall
- ? backupIframeRef
- : primaryIframeRef;
+ const activeIframeDisplayRef = primaryIframeRef
const applyFixedPositioningToIframe = useCallback(() => {
const iframeElement = activeIframeDisplayRef?.current;
@@ -88,7 +118,7 @@ export function CallView({ room }: { room: Room }) {
const iframeElement = activeIframeDisplayRef?.current;
const hostElement = iframeHostRef?.current;
- if (room.isCallRoom() || (isViewingActiveCall && iframeElement && hostElement)) {
+ if (room.isCallRoom() || (shouldDisplayCall && iframeElement && hostElement)) {
applyFixedPositioningToIframe();
const resizeObserver = new ResizeObserver(debouncedApplyFixedPositioning);
@@ -116,13 +146,36 @@ export function CallView({ room }: { room: Room }) {
activeIframeDisplayRef,
applyFixedPositioningToIframe,
debouncedApplyFixedPositioning,
- isPrimaryIframe,
- isViewingActiveCall,
+ shouldDisplayCall,
room,
]);
+
+ const handleJoinVCClick: MouseEventHandler = (evt) => {
+ if (isMobile) {
+ evt.stopPropagation();
+ setViewedCallRoomId(room.roomId);
+ navigateRoom(room.roomId);
+ }
+ if (!shouldDisplayCall) {
+ hangUp(room.roomId);
+ setActiveCallRoomId(room.roomId);
+ }
+ };
+
const isCallViewVisible = room.isCallRoom() && (screenSize === ScreenSize.Desktop || !isChatOpen);
+ useEffect(() => {
+ if(memberDisplayNames.length <= 2){
+ setVisibleCallNames(memberDisplayNames.join(" and "))
+ } else {
+ const visible = memberDisplayNames.slice(0, 2);
+ const remaining = memberDisplayNames.length - 2;
+
+ setVisibleCallNames(`${visible.join(", ")}, and ${remaining} other${remaining > 1 ? "s" : ""}`)
+ }
+ }, [memberDisplayNames])
+
return (
+
+
+ {callMembers.map(callMember => (
+
+ )).slice(0, 6)}
+
+
+
+ {room.name}
+ {visibleCallNames !== "" ? visibleCallNames : "No one"} {memberDisplayNames.length > 1 ? "are" : "is"} currently in voice
+
+
+
);
}
diff --git a/src/app/features/call/CallViewUser.tsx b/src/app/features/call/CallViewUser.tsx
new file mode 100644
index 00000000..8d55f77e
--- /dev/null
+++ b/src/app/features/call/CallViewUser.tsx
@@ -0,0 +1,69 @@
+import { as, Avatar, Box, Icon, Icons, Text } from 'folds';
+import React from 'react';
+import classNames from 'classnames';
+import { Room } from 'matrix-js-sdk';
+import { CallMembership } from 'matrix-js-sdk/lib/matrixrtc/CallMembership';
+import { UserAvatar } from '../../components/user-avatar';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
+import { getMxIdLocalPart } from '../../utils/matrix';
+import { getMemberAvatarMxc, getMemberDisplayName } from '../../utils/room';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
+import { openProfileViewer } from '../../../client/action/navigation';
+import * as css from './CallView.css';
+
+type CallViewUserProps = {
+ room: Room;
+ callMembership: CallMembership;
+};
+
+
+export const UserProfileButton = as<'button'>(
+ ({ as: AsUserProfileButton = 'button', className, ...props }, ref) => (
+
+ )
+);
+
+export const CallViewUserBase = as<'div'>(({ className, ...props }, ref) => (
+
+));
+
+export function CallViewUser({ room, callMembership }: CallViewUserProps) {
+ const mx = useMatrixClient();
+ const useAuthentication = useMediaAuthentication();
+ const userId = callMembership.sender ?? '';
+ const avatarMxcUrl = getMemberAvatarMxc(room, userId);
+ const avatarUrl = avatarMxcUrl
+ ? mx.mxcUrlToHttp(avatarMxcUrl, 32, 32, 'crop', undefined, false, useAuthentication)
+ : undefined;
+ const getName = getMemberDisplayName(room, userId) ?? getMxIdLocalPart(userId);
+
+ const handleUserClick = () => {
+ openProfileViewer(userId, room.roomId);
+ };
+
+ return (
+
+
+
+
+ }
+ />
+
+
+ {getName}
+
+
+
+
+ );
+}
diff --git a/src/app/features/call/SmallWidget.ts b/src/app/features/call/SmallWidget.ts
index 2b24e978..fd5a1a4a 100644
--- a/src/app/features/call/SmallWidget.ts
+++ b/src/app/features/call/SmallWidget.ts
@@ -52,11 +52,12 @@ export const getWidgetUrl = (
embed: 'true',
widgetId,
appPrompt: 'false',
- preload: 'false',
- skipLobby: setParams.skipLobby ?? 'true',
+ skipLobby: setParams.skipLobby ?? 'true', // TODO: skipLobby is deprecated, use intent instead (intent doesn't produce the same effect?)
returnToLobby: setParams.returnToLobby ?? 'true',
perParticipantE2EE: setParams.perParticipantE2EE ?? 'true',
- hideHeader: 'true',
+ header: 'none',
+ confineToRoom: 'true',
+ theme: setParams.theme ?? 'dark',
userId: mx.getUserId()!,
deviceId: mx.getDeviceId()!,
roomId,
@@ -137,6 +138,7 @@ export class SmallWidget extends EventEmitter {
// Populate the map of "read up to" events for this widget with the current event in every room.
// This is a bit inefficient, but should be okay. We do this for all rooms in case the widget
// requests timeline capabilities in other rooms down the road. It's just easier to manage here.
+ // eslint-disable-next-line no-restricted-syntax
for (const room of this.client.getRooms()) {
// Timelines are most recent last
const events = room.getLiveTimeline()?.getEvents() || [];
diff --git a/src/app/features/room-nav/RoomCallNavStatus.tsx b/src/app/features/room-nav/RoomCallNavStatus.tsx
index d3f23794..ce3657a7 100644
--- a/src/app/features/room-nav/RoomCallNavStatus.tsx
+++ b/src/app/features/room-nav/RoomCallNavStatus.tsx
@@ -9,7 +9,7 @@ export function CallNavStatus() {
activeCallRoomId,
isAudioEnabled,
isVideoEnabled,
- isCallActive,
+ isActiveCallReady,
toggleAudio,
toggleVideo,
hangUp,
@@ -21,7 +21,7 @@ export function CallNavStatus() {
navigateRoom(activeCallRoomId);
}
};
- if (!isCallActive) {
+ if (!isActiveCallReady) {
return null;
}
diff --git a/src/app/features/room-nav/RoomNavItem.tsx b/src/app/features/room-nav/RoomNavItem.tsx
index b666c48b..ce9c0006 100644
--- a/src/app/features/room-nav/RoomNavItem.tsx
+++ b/src/app/features/room-nav/RoomNavItem.tsx
@@ -227,7 +227,7 @@ export function RoomNavItem({
const [menuAnchor, setMenuAnchor] = useState();
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
const {
- isCallActive,
+ isActiveCallReady,
activeCallRoomId,
setActiveCallRoomId,
setViewedCallRoomId,
@@ -238,7 +238,7 @@ export function RoomNavItem({
const typingMember = useRoomTypingMember(room.roomId).filter(
(receipt) => receipt.userId !== mx.getUserId()
);
- const isActiveCall = isCallActive && activeCallRoomId === room.roomId;
+ const isActiveCall = isActiveCallReady && activeCallRoomId === room.roomId;
const callMemberships = useCallMembers(mx, room.roomId);
const { navigateRoom } = useRoomNavigate();
const { roomIdOrAlias: viewedRoomId } = useParams();
@@ -269,7 +269,7 @@ export function RoomNavItem({
}
if (room.isCallRoom()) {
if (!isMobile) {
- if (activeCallRoomId !== room.roomId) {
+ if (!isActiveCall) {
if (mx.getRoom(viewedRoomId)?.isCallRoom()) {
navigateRoom(room.roomId);
}
diff --git a/src/app/features/room-nav/RoomNavUser.tsx b/src/app/features/room-nav/RoomNavUser.tsx
index 169ebde4..5a9fed5e 100644
--- a/src/app/features/room-nav/RoomNavUser.tsx
+++ b/src/app/features/room-nav/RoomNavUser.tsx
@@ -18,8 +18,8 @@ type RoomNavUserProps = {
export function RoomNavUser({ room, callMembership }: RoomNavUserProps) {
const mx = useMatrixClient();
const useAuthentication = useMediaAuthentication();
- const { isCallActive, activeCallRoomId } = useCallState();
- const isActiveCall = isCallActive && activeCallRoomId === room.roomId;
+ const { isActiveCallReady, activeCallRoomId } = useCallState();
+ const isActiveCall = isActiveCallReady && activeCallRoomId === room.roomId;
const userId = callMembership.sender ?? '';
const avatarMxcUrl = getMemberAvatarMxc(room, userId);
const avatarUrl = avatarMxcUrl
diff --git a/src/app/pages/client/call/CallProvider.tsx b/src/app/pages/client/call/CallProvider.tsx
index 364670eb..c8022899 100644
--- a/src/app/pages/client/call/CallProvider.tsx
+++ b/src/app/pages/client/call/CallProvider.tsx
@@ -38,13 +38,6 @@ interface CallContextState {
clientWidgetApi: ClientWidgetApi | null,
clientWidget: SmallWidget
) => void;
- viewedClientWidgetApi: ClientWidgetApi | null;
- viewedClientWidget: SmallWidget | null;
- registerViewedClientWidgetApi: (
- roomId: string | null,
- clientWidgetApi: ClientWidgetApi | null,
- clientWidget: SmallWidget
- ) => void;
sendWidgetAction: (
action: WidgetApiToWidgetAction | string,
data: T
@@ -52,12 +45,10 @@ interface CallContextState {
isAudioEnabled: boolean;
isVideoEnabled: boolean;
isChatOpen: boolean;
- isCallActive: boolean;
- isPrimaryIframe: boolean;
+ isActiveCallReady: boolean;
toggleAudio: () => Promise;
toggleVideo: () => Promise;
toggleChat: () => Promise;
- toggleIframe: () => Promise;
}
const CallContext = createContext(undefined);
@@ -66,11 +57,10 @@ interface CallProviderProps {
children: ReactNode;
}
-const DEFAULT_AUDIO_ENABLED = true;
+const DEFAULT_AUDIO_ENABLED = false;
const DEFAULT_VIDEO_ENABLED = false;
const DEFAULT_CHAT_OPENED = false;
const DEFAULT_CALL_ACTIVE = false;
-const DEFAULT_PRIMARY_IFRAME = true;
export function CallProvider({ children }: CallProviderProps) {
const [activeCallRoomId, setActiveCallRoomIdState] = useState(null);
@@ -82,22 +72,13 @@ export function CallProvider({ children }: CallProviderProps) {
const [activeClientWidgetApiRoomId, setActiveClientWidgetApiRoomId] = useState(
null
);
- const [viewedClientWidgetApi, setViewedClientWidgetApiState] = useState(
- null
- );
- const [viewedClientWidget, setViewedClientWidget] = useState(null);
- const [viewedClientWidgetApiRoomId, setViewedClientWidgetApiRoomId] = useState(
- null
- );
const [isAudioEnabled, setIsAudioEnabledState] = useState(DEFAULT_AUDIO_ENABLED);
const [isVideoEnabled, setIsVideoEnabledState] = useState(DEFAULT_VIDEO_ENABLED);
const [isChatOpen, setIsChatOpenState] = useState(DEFAULT_CHAT_OPENED);
- const [isCallActive, setIsCallActive] = useState(DEFAULT_CALL_ACTIVE);
- const [isPrimaryIframe, setIsPrimaryIframe] = useState(DEFAULT_PRIMARY_IFRAME);
+ const [isActiveCallReady, setIsCallActive] = useState(DEFAULT_CALL_ACTIVE);
const { roomIdOrAlias: viewedRoomId } = useParams<{ roomIdOrAlias: string }>();
- const [lastViewedRoomDuringCall, setLastViewedRoomDuringCall] = useState(null);
const resetMediaState = useCallback(() => {
logger.debug('CallContext: Resetting media state to defaults.');
@@ -115,19 +96,13 @@ export function CallProvider({ children }: CallProviderProps) {
logger.debug(`CallContext: Active call room changed, resetting media state.`);
resetMediaState();
}
-
- if (roomId === null || roomId !== activeClientWidgetApiRoomId) {
- logger.warn(
- `CallContext: Clearing active clientWidgetApi because active room changed to ${roomId} or was cleared.`
- );
- }
},
- [activeClientWidgetApiRoomId, resetMediaState, activeCallRoomId]
+ [resetMediaState, activeCallRoomId]
);
const setViewedCallRoomId = useCallback(
(roomId: string | null) => {
- logger.warn(`CallContext: Setting activeCallRoomId to ${roomId}`);
+ logger.warn(`CallContext: Setting viewedCallRoomId to ${roomId}`);
setViewedCallRoomIdState(roomId);
},
[setViewedCallRoomIdState]
@@ -167,72 +142,18 @@ export function CallProvider({ children }: CallProviderProps) {
[activeClientWidgetApi, activeClientWidgetApiRoomId, setActiveClientWidgetApi, resetMediaState]
);
- const setViewedClientWidgetApi = useCallback(
- (
- clientWidgetApi: ClientWidgetApi | null,
- clientWidget: SmallWidget | null,
- roomId: string | null
- ) => {
- setViewedClientWidgetApiState(clientWidgetApi);
- setViewedClientWidget(clientWidget);
- setViewedClientWidgetApiRoomId(roomId);
- },
- []
- );
-
- const registerViewedClientWidgetApi = useCallback(
- (
- roomId: string | null,
- clientWidgetApi: ClientWidgetApi | null,
- clientWidget: SmallWidget | null
- ) => {
- if (viewedClientWidgetApi && viewedClientWidgetApi !== clientWidgetApi) {
- logger.debug(`CallContext: Cleaning up listeners for previous clientWidgetApi instance.`);
- }
-
- if (roomId && clientWidgetApi) {
- logger.debug(`CallContext: Registering viewed clientWidgetApi for room ${roomId}.`);
- setViewedClientWidgetApi(clientWidgetApi, clientWidget, roomId);
- } else if (roomId === viewedClientWidgetApiRoomId || roomId === null) {
- logger.debug(
- `CallContext: Clearing viewed clientWidgetApi for room ${viewedClientWidgetApiRoomId}.`
- );
- setViewedClientWidgetApi(null, null, null);
- }
- },
- [viewedClientWidgetApi, viewedClientWidgetApiRoomId, setViewedClientWidgetApi]
- );
-
const hangUp = useCallback(
- (nextRoom: string) => {
- if (typeof nextRoom === 'string') {
- logger.debug('1 Hangup');
- setActiveClientWidgetApi(null, null, null);
- setActiveCallRoomIdState(null);
- activeClientWidgetApi?.transport.send(`${WIDGET_HANGUP_ACTION}`, {});
- } else if (viewedRoomId !== activeCallRoomId) {
- logger.debug('2 Hangup');
- setActiveClientWidgetApi(null, null, null);
- setActiveCallRoomIdState(null);
- activeClientWidgetApi?.transport.send(`${WIDGET_HANGUP_ACTION}`, {});
- } else if (activeClientWidget) {
- logger.debug('3 Hangup');
- const iframeDoc =
- activeClientWidget?.iframe?.contentDocument ||
- activeClientWidget?.iframe?.contentWindow.document;
- const button = iframeDoc.querySelector('[data-testid="incall_leave"]');
- button.click();
- }
+ () => {
+ setActiveClientWidgetApi(null, null, null);
+ setActiveCallRoomIdState(null);
+ activeClientWidgetApi?.transport.send(`${WIDGET_HANGUP_ACTION}`, {});
setIsCallActive(false);
logger.debug(`CallContext: Hang up called.`);
},
[
- activeCallRoomId,
- activeClientWidget,
activeClientWidgetApi?.transport,
setActiveClientWidgetApi,
- viewedRoomId,
]
);
@@ -240,22 +161,15 @@ export function CallProvider({ children }: CallProviderProps) {
if (!activeCallRoomId && !viewedCallRoomId) {
return;
}
- if (!lastViewedRoomDuringCall) {
- if (activeCallRoomId)
- setLastViewedRoomDuringCall((prevLastRoom) => prevLastRoom || activeCallRoomId);
- }
- if (
- lastViewedRoomDuringCall &&
- lastViewedRoomDuringCall !== viewedRoomId &&
- activeCallRoomId &&
- isCallActive
- ) {
- setLastViewedRoomDuringCall(activeCallRoomId);
+
+ const api = activeClientWidgetApi;
+ if (!api) {
+ return;
}
const handleHangup = (ev: CustomEvent) => {
ev.preventDefault();
- if (isCallActive && ev.detail.widgetId === activeClientWidgetApi?.widget.id) {
+ if (isActiveCallReady && ev.detail.widgetId === activeClientWidgetApi?.widget.id) {
activeClientWidgetApi?.transport.reply(ev.detail, {});
}
logger.debug(
@@ -287,107 +201,46 @@ export function CallProvider({ children }: CallProviderProps) {
const handleOnScreenStateUpdate = (ev: CustomEvent) => {
ev.preventDefault();
- if (isPrimaryIframe) {
- activeClientWidgetApi?.transport.reply(ev.detail, {});
- } else {
- viewedClientWidgetApi?.transport.reply(ev.detail, {});
- }
+ api.transport.reply(ev.detail, {});
};
const handleOnTileLayout = (ev: CustomEvent) => {
ev.preventDefault();
- if (isPrimaryIframe) {
- activeClientWidgetApi?.transport.reply(ev.detail, {});
- } else {
- viewedClientWidgetApi?.transport.reply(ev.detail, {});
- }
+
+ api.transport.reply(ev.detail, {});
};
const handleJoin = (ev: CustomEvent) => {
ev.preventDefault();
- const setViewedAsActive = () => {
- if (viewedCallRoomId !== activeCallRoomId) setIsPrimaryIframe(!isPrimaryIframe);
- setActiveClientWidgetApi(viewedClientWidgetApi, viewedClientWidget, viewedCallRoomId);
- setActiveCallRoomIdState(viewedCallRoomId);
- setIsCallActive(true);
- const iframeDoc =
- viewedClientWidget?.iframe?.contentDocument ||
- viewedClientWidget?.iframe?.contentWindow.document;
- const observer = new MutationObserver(() => {
- const button = iframeDoc.querySelector('[data-testid="incall_leave"]');
- if (button) {
- button.addEventListener('click', () => {
- setIsCallActive(false);
- });
- }
- observer.disconnect();
- });
- observer.observe(iframeDoc, { childList: true, subtree: true });
- };
- if (ev.detail.widgetId === activeClientWidgetApi?.widget.id) {
- activeClientWidgetApi?.transport.reply(ev.detail, {});
- const iframeDoc =
- activeClientWidget?.iframe?.contentDocument ||
- activeClientWidget?.iframe?.contentWindow.document;
- const observer = new MutationObserver(() => {
- const button = iframeDoc.querySelector('[data-testid="incall_leave"]');
- if (button) {
- button.addEventListener('click', () => {
- setIsCallActive(false);
- });
- }
- observer.disconnect();
- });
- logger.debug('1 Join');
- observer.observe(iframeDoc, { childList: true, subtree: true });
- setIsCallActive(true);
- return;
- }
- if (
- lastViewedRoomDuringCall &&
- viewedRoomId === activeCallRoomId &&
- lastViewedRoomDuringCall === activeCallRoomId
- ) {
- logger.debug('2 Join');
- setIsCallActive(true);
- return;
- }
- if (activeClientWidgetApi) {
- if (isCallActive && viewedClientWidgetApi && viewedCallRoomId) {
- activeClientWidgetApi?.removeAllListeners();
- activeClientWidgetApi?.transport.send(WIDGET_HANGUP_ACTION, {}).then(() => {
- logger.debug('3 Join');
- setViewedAsActive();
+ api.transport.reply(ev.detail, {});
+ const iframeDoc =
+ api.iframe?.contentDocument ||
+ api.iframe?.contentWindow.document;
+ const observer = new MutationObserver(() => {
+ const button = iframeDoc.querySelector('[data-testid="incall_leave"]');
+ if (button) {
+ button.addEventListener('click', () => {
+ hangUp()
});
- } else {
- logger.debug('4 Join');
- setViewedAsActive();
- setIsCallActive(true);
}
- } else if (viewedCallRoomId !== viewedRoomId) {
- logger.debug('5 Join');
- setIsCallActive(true);
- } else {
- logger.debug('6 Join');
- setViewedAsActive();
- }
+ observer.disconnect();
+ });
+ logger.debug('Join Call');
+ observer.observe(iframeDoc, { childList: true, subtree: true });
+ setIsCallActive(true);
};
-
+
logger.debug(
`CallContext: Setting up listeners for clientWidgetApi in room ${activeCallRoomId}`
);
- activeClientWidgetApi?.on(`action:${WIDGET_HANGUP_ACTION}`, handleHangup);
- activeClientWidgetApi?.on(`action:${WIDGET_MEDIA_STATE_UPDATE_ACTION}`, handleMediaStateUpdate);
- viewedClientWidgetApi?.on(`action:${WIDGET_TILE_UPDATE}`, handleOnTileLayout);
- activeClientWidgetApi?.on(`action:${WIDGET_ON_SCREEN_ACTION}`, handleOnScreenStateUpdate);
- activeClientWidgetApi?.on(`action:${WIDGET_JOIN_ACTION}`, handleJoin);
- viewedClientWidgetApi?.on(`action:${WIDGET_JOIN_ACTION}`, handleJoin);
- viewedClientWidgetApi?.on(`action:${WIDGET_MEDIA_STATE_UPDATE_ACTION}`, handleMediaStateUpdate);
- viewedClientWidgetApi?.on(`action:${WIDGET_TILE_UPDATE}`, handleOnTileLayout);
- viewedClientWidgetApi?.on(`action:${WIDGET_ON_SCREEN_ACTION}`, handleOnScreenStateUpdate);
- viewedClientWidgetApi?.on(`action:${WIDGET_HANGUP_ACTION}`, handleHangup);
+ api.on(`action:${WIDGET_HANGUP_ACTION}`, handleHangup);
+ api.on(`action:${WIDGET_MEDIA_STATE_UPDATE_ACTION}`, handleMediaStateUpdate);
+ api.on(`action:${WIDGET_TILE_UPDATE}`, handleOnTileLayout);
+ api.on(`action:${WIDGET_ON_SCREEN_ACTION}`, handleOnScreenStateUpdate);
+ api.on(`action:${WIDGET_JOIN_ACTION}`, handleJoin);
+
}, [
activeClientWidgetApi,
activeCallRoomId,
@@ -396,16 +249,10 @@ export function CallProvider({ children }: CallProviderProps) {
isChatOpen,
isAudioEnabled,
isVideoEnabled,
- isCallActive,
+ isActiveCallReady,
viewedRoomId,
- viewedClientWidgetApi,
- isPrimaryIframe,
viewedCallRoomId,
- setViewedClientWidgetApi,
- setActiveClientWidgetApi,
- viewedClientWidget,
setViewedCallRoomId,
- lastViewedRoomDuringCall,
activeClientWidget?.iframe?.contentDocument,
activeClientWidget?.iframe?.contentWindow?.document,
]);
@@ -424,6 +271,8 @@ export function CallProvider({ children }: CallProviderProps) {
);
return Promise.reject(new Error('Mismatched active call clientWidgetApi'));
}
+
+
logger.debug(
`CallContext: Sending action '${action}' via active clientWidgetApi (room: ${activeClientWidgetApiRoomId}) with data:`,
data
@@ -470,11 +319,6 @@ export function CallProvider({ children }: CallProviderProps) {
setIsChatOpenState(newState);
}, [isChatOpen]);
- const toggleIframe = useCallback(async () => {
- const newState = !isPrimaryIframe;
- setIsPrimaryIframe(newState);
- }, [isPrimaryIframe]);
-
const contextValue = useMemo(
() => ({
activeCallRoomId,
@@ -485,19 +329,14 @@ export function CallProvider({ children }: CallProviderProps) {
activeClientWidgetApi,
registerActiveClientWidgetApi,
activeClientWidget,
- viewedClientWidgetApi,
- registerViewedClientWidgetApi,
- viewedClientWidget,
sendWidgetAction,
isChatOpen,
isAudioEnabled,
isVideoEnabled,
- isCallActive,
- isPrimaryIframe,
+ isActiveCallReady,
toggleAudio,
toggleVideo,
- toggleChat,
- toggleIframe,
+ toggleChat
}),
[
activeCallRoomId,
@@ -508,19 +347,14 @@ export function CallProvider({ children }: CallProviderProps) {
activeClientWidgetApi,
registerActiveClientWidgetApi,
activeClientWidget,
- viewedClientWidgetApi,
- registerViewedClientWidgetApi,
- viewedClientWidget,
sendWidgetAction,
isChatOpen,
isAudioEnabled,
isVideoEnabled,
- isCallActive,
- isPrimaryIframe,
+ isActiveCallReady,
toggleAudio,
toggleVideo,
- toggleChat,
- toggleIframe,
+ toggleChat
]
);
diff --git a/src/app/pages/client/call/PersistentCallContainer.tsx b/src/app/pages/client/call/PersistentCallContainer.tsx
index bf35b45e..eb8b3ff6 100644
--- a/src/app/pages/client/call/PersistentCallContainer.tsx
+++ b/src/app/pages/client/call/PersistentCallContainer.tsx
@@ -1,4 +1,4 @@
-import React, { createContext, ReactNode, useCallback, useEffect, useMemo, useRef } from 'react';
+import React, { createContext, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { logger } from 'matrix-js-sdk/lib/logger';
import { ClientWidgetApi } from 'matrix-widget-api';
import { Box } from 'folds';
@@ -13,42 +13,38 @@ import {
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { useClientConfig } from '../../../hooks/useClientConfig';
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
+import { ThemeKind, useTheme } from '../../../hooks/useTheme';
interface PersistentCallContainerProps {
children: ReactNode;
}
export const PrimaryRefContext = createContext(null);
-export const BackupRefContext = createContext(null);
export function PersistentCallContainer({ children }: PersistentCallContainerProps) {
const primaryIframeRef = useRef(null);
const primaryWidgetApiRef = useRef(null);
const primarySmallWidgetRef = useRef(null);
- const backupIframeRef = useRef(null);
- const backupWidgetApiRef = useRef(null);
- const backupSmallWidgetRef = useRef(null);
const {
activeCallRoomId,
viewedCallRoomId,
isChatOpen,
- isCallActive,
- isPrimaryIframe,
+ isActiveCallReady,
registerActiveClientWidgetApi,
activeClientWidget,
- registerViewedClientWidgetApi,
- viewedClientWidget,
} = useCallState();
const mx = useMatrixClient();
const clientConfig = useClientConfig();
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 */
const setupWidget = useCallback(
@@ -56,13 +52,16 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
widgetApiRef: { current: ClientWidgetApi },
smallWidgetRef: { current: SmallWidget },
iframeRef: { current: { src: string } },
- skipLobby: { toString: () => any }
+ skipLobby: { toString: () => any },
+ themeKind: ThemeKind | null
) => {
if (mx?.getUserId()) {
+ logger.debug(`CallContextJ: iframe src - ${iframeRef.current.src}`)
+ logger.debug(`CallContextJ: activeCallRoomId: ${activeCallRoomId} viewedId: ${viewedCallRoomId} isactive: ${isActiveCallReady}`)
if (
- (activeCallRoomId !== viewedCallRoomId && isCallActive) ||
- (activeCallRoomId && !isCallActive) ||
- (!activeCallRoomId && viewedCallRoomId && !isCallActive)
+ (activeCallRoomId !== viewedCallRoomId && isActiveCallReady) ||
+ (activeCallRoomId && !isActiveCallReady) ||
+ (!activeCallRoomId && viewedCallRoomId && !isActiveCallReady)
) {
const roomIdToSet = (skipLobby ? activeCallRoomId : viewedCallRoomId) ?? '';
if (roomIdToSet === '') {
@@ -78,27 +77,18 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
skipLobby: skipLobby.toString(),
returnToLobby: 'true',
perParticipantE2EE: 'true',
+ theme: themeKind
}
);
if (
- (primarySmallWidgetRef.current?.roomId || backupSmallWidgetRef.current?.roomId) &&
- (skipLobby
- ? activeClientWidget?.roomId &&
- //activeCallRoomId === activeClientWidget.roomId &&
- (activeClientWidget.roomId === primarySmallWidgetRef.current?.roomId ||
- activeClientWidget.roomId === backupSmallWidgetRef.current?.roomId)
- : viewedClientWidget?.roomId &&
- viewedCallRoomId === viewedClientWidget.roomId &&
- (viewedClientWidget.roomId === primarySmallWidgetRef.current?.roomId ||
- viewedClientWidget.roomId === backupSmallWidgetRef.current?.roomId))
+ primarySmallWidgetRef.current?.roomId &&
+ (activeClientWidget?.roomId && activeClientWidget.roomId === primarySmallWidgetRef.current?.roomId)
) {
return;
}
- if (iframeRef.current && iframeRef.current.src !== newUrl.toString()) {
- iframeRef.current.src = newUrl.toString();
- } else if (iframeRef.current && !iframeRef.current.src) {
+ if (iframeRef.current && (!iframeRef.current.src || iframeRef.current.src !== newUrl.toString())) {
iframeRef.current.src = newUrl.toString();
}
@@ -125,12 +115,8 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
const widgetApiInstance = smallWidget.startMessaging(iframeElement);
widgetApiRef.current = widgetApiInstance;
- if (skipLobby) {
- registerActiveClientWidgetApi(activeCallRoomId, widgetApiRef.current, smallWidget);
- } else {
- registerViewedClientWidgetApi(viewedCallRoomId, widgetApiRef.current, smallWidget);
- }
-
+ registerActiveClientWidgetApi(roomIdToSet, widgetApiRef.current, smallWidget);
+
widgetApiInstance.once('ready', () => {
logger.info(`PersistentCallContainer: Widget for ${roomIdToSet} is ready.`);
});
@@ -141,43 +127,37 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
mx,
activeCallRoomId,
viewedCallRoomId,
- isCallActive,
+ isActiveCallReady,
clientConfig.elementCallUrl,
- viewedClientWidget,
activeClientWidget,
- viewedRoomId,
registerActiveClientWidgetApi,
- registerViewedClientWidgetApi,
]
);
useEffect(() => {
- if ((activeCallRoomId && !viewedCallRoomId) || (activeCallRoomId && viewedCallRoomId))
- setupWidget(primaryWidgetApiRef, primarySmallWidgetRef, primaryIframeRef, isPrimaryIframe);
- if ((!activeCallRoomId && viewedCallRoomId) || (viewedCallRoomId && activeCallRoomId))
- setupWidget(backupWidgetApiRef, backupSmallWidgetRef, backupIframeRef, !isPrimaryIframe);
+ 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}`)
+ }
}, [
+ theme,
setupWidget,
primaryWidgetApiRef,
primarySmallWidgetRef,
primaryIframeRef,
- backupWidgetApiRef,
- backupSmallWidgetRef,
- backupIframeRef,
registerActiveClientWidgetApi,
- registerViewedClientWidgetApi,
activeCallRoomId,
viewedCallRoomId,
- isCallActive,
- isPrimaryIframe,
+ isActiveCallReady
]);
const memoizedIframeRef = useMemo(() => primaryIframeRef, [primaryIframeRef]);
- const memoizedBackupIframeRef = useMemo(() => backupIframeRef, [backupIframeRef]);
return (
-
-
{children}
-
);
}
diff --git a/src/app/pages/client/space/Space.tsx b/src/app/pages/client/space/Space.tsx
index 36909e8b..3148a31a 100644
--- a/src/app/pages/client/space/Space.tsx
+++ b/src/app/pages/client/space/Space.tsx
@@ -300,7 +300,7 @@ export function Space() {
const selectedRoomId = useSelectedRoom();
const lobbySelected = useSpaceLobbySelected(spaceIdOrAlias);
const searchSelected = useSpaceSearchSelected(spaceIdOrAlias);
- const { isCallActive, activeCallRoomId } = useCallState();
+ const { isActiveCallReady, activeCallRoomId } = useCallState();
const [closedCategories, setClosedCategories] = useAtom(useClosedNavCategoriesAtom());
@@ -325,10 +325,10 @@ export function Space() {
const showRoomAnyway =
roomToUnread.has(roomId) ||
roomId === selectedRoomId ||
- (isCallActive && activeCallRoomId === roomId);
+ (isActiveCallReady && activeCallRoomId === roomId);
return !showRoomAnyway;
},
- [space.roomId, closedCategories, roomToUnread, selectedRoomId, activeCallRoomId, isCallActive]
+ [space.roomId, closedCategories, roomToUnread, selectedRoomId, activeCallRoomId, isActiveCallReady]
),
useCallback(
(sId) => closedCategories.has(makeNavCategoryId(space.roomId, sId)),