diff --git a/package.json b/package.json index de7eec7..750afc3 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "i18next": "^21.10.0", "i18next-browser-languagedetector": "^6.1.8", "i18next-http-backend": "^1.4.4", - "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#3f1c3392d45b0fc054c3788cc6c043cd5b4fb730", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#f46ecf970c658ae34b8d5fc3e73369c31ac79e90", "matrix-widget-api": "^1.0.0", "mermaid": "^8.13.8", "normalize.css": "^8.0.1", diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx index 23d87a4..8eba226 100644 --- a/src/ClientContext.tsx +++ b/src/ClientContext.tsx @@ -25,8 +25,7 @@ import React, { useRef, } from "react"; import { useHistory } from "react-router-dom"; -import { MatrixClient, ClientEvent } from "matrix-js-sdk/src/client"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { MatrixClient } from "matrix-js-sdk/src/client"; import { logger } from "matrix-js-sdk/src/logger"; import { useTranslation } from "react-i18next"; @@ -40,6 +39,7 @@ import { import { widget } from "./widget"; import { PosthogAnalytics, RegistrationType } from "./PosthogAnalytics"; import { translatedError } from "./TranslatedError"; +import { useEventTarget } from "./useEvents"; declare global { interface Window { @@ -55,6 +55,8 @@ export interface Session { tempPassword?: string; } +const loadChannel = new BroadcastChannel("load"); + const loadSession = (): Session => { const data = localStorage.getItem("matrix-auth-store"); if (data) return JSON.parse(data); @@ -292,47 +294,29 @@ export const ClientProvider: FC = ({ children }) => { const { t } = useTranslation(); + // To protect against multiple sessions writing to the same storage + // simultaneously, we send a broadcast message that shuts down all other + // running instances of the app. This isn't necessary if the app is running in + // a widget though, since then it'll be mostly stateless. useEffect(() => { - // To protect against multiple sessions writing to the same storage - // simultaneously, we send a to-device message that shuts down all other - // running instances of the app. This isn't necessary if the app is running - // in a widget though, since then it'll be mostly stateless. - if (!widget && client) { - const loadTime = Date.now(); + if (!widget) loadChannel.postMessage({}); + }, []); - const onToDeviceEvent = (event: MatrixEvent) => { - if (event.getType() !== "org.matrix.call_duplicate_session") return; + useEventTarget( + loadChannel, + "message", + useCallback(() => { + client?.stopClient(); - const content = event.getContent(); - - if (content.session_id === client.getSessionId()) return; - - if (content.timestamp > loadTime) { - client?.stopClient(); - - setState((prev) => ({ - ...prev, - error: translatedError( - "This application has been opened in another tab.", - t - ), - })); - } - }; - - client.on(ClientEvent.ToDeviceEvent, onToDeviceEvent); - - client.sendToDevice("org.matrix.call_duplicate_session", { - [client.getUserId()]: { - "*": { session_id: client.getSessionId(), timestamp: loadTime }, - }, - }); - - return () => { - client?.removeListener(ClientEvent.ToDeviceEvent, onToDeviceEvent); - }; - } - }, [client, t]); + setState((prev) => ({ + ...prev, + error: translatedError( + "This application has been opened in another tab.", + t + ), + })); + }, [client, setState, t]) + ); const context = useMemo( () => ({ diff --git a/src/Facepile.tsx b/src/Facepile.tsx index 86c5c6f..79e2788 100644 --- a/src/Facepile.tsx +++ b/src/Facepile.tsx @@ -32,7 +32,7 @@ const overlapMap: Partial> = { interface Props extends HTMLAttributes { className: string; client: MatrixClient; - participants: RoomMember[]; + members: RoomMember[]; max?: number; size?: Size; } @@ -40,7 +40,7 @@ interface Props extends HTMLAttributes { export function Facepile({ className, client, - participants, + members, max = 3, size = Size.XS, ...rest @@ -51,14 +51,14 @@ export function Facepile({ const _overlap = overlapMap[size]; const title = useMemo(() => { - return participants.reduce( + return members.reduce( (prev, curr) => prev === null ? curr.name : t("{{names}}, {{name}}", { names: prev, name: curr.name }), null ) as string; - }, [participants, t]); + }, [members, t]); return (
- {participants.slice(0, max).map((member, i) => { + {members.slice(0, max).map((member, i) => { const avatarUrl = member.getMxcAvatarUrl(); return ( ); })} - {participants.length > max && ( + {members.length > max && ( diff --git a/src/home/CallList.tsx b/src/home/CallList.tsx index ddf6382..5581961 100644 --- a/src/home/CallList.tsx +++ b/src/home/CallList.tsx @@ -92,7 +92,7 @@ function CallTile({ )}
diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 7da620b..b101b57 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -79,7 +79,6 @@ export function GroupCallView({ isScreensharing, screenshareFeeds, participants, - calls, unencryptedEventsFromUsers, } = useGroupCall(groupCall); @@ -173,9 +172,14 @@ export function GroupCallView({ const onLeave = useCallback(() => { setLeft(true); + let participantCount = 0; + for (const deviceMap of groupCall.participants.values()) { + participantCount += deviceMap.size; + } + PosthogAnalytics.instance.eventCallEnded.track( groupCall.room.name, - groupCall.participants.length + participantCount ); leave(); @@ -187,14 +191,7 @@ export function GroupCallView({ if (!isPasswordlessUser && !isEmbedded) { history.push("/"); } - }, [ - groupCall.room.name, - groupCall.participants.length, - leave, - isPasswordlessUser, - isEmbedded, - history, - ]); + }, [groupCall, leave, isPasswordlessUser, isEmbedded, history]); useEffect(() => { if (widget && state === GroupCallState.Entered) { @@ -236,7 +233,6 @@ export function GroupCallView({ roomName={groupCall.room.name} avatarUrl={avatarUrl} participants={participants} - calls={calls} microphoneMuted={microphoneMuted} localVideoMuted={localVideoMuted} toggleLocalVideoMuted={toggleLocalVideoMuted} @@ -253,12 +249,6 @@ export function GroupCallView({ /> ); } - } else if (state === GroupCallState.Entering) { - return ( - -

{t("Entering room…")}

-
- ); } else if (left) { if (isPasswordlessUser) { return ; diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index a75da5f..431d8b0 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -14,13 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { - useEffect, - useCallback, - useMemo, - useRef, - useState, -} from "react"; +import React, { useEffect, useCallback, useMemo, useRef } from "react"; import { usePreventScroll } from "@react-aria/overlays"; import useMeasure from "react-use-measure"; import { ResizeObserver } from "@juggle/resize-observer"; @@ -31,11 +25,6 @@ import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed"; import classNames from "classnames"; import { useTranslation } from "react-i18next"; import { JoinRule } from "matrix-js-sdk/src/@types/partials"; -import { - CallEvent, - CallState, - MatrixCall, -} from "matrix-js-sdk/src/webrtc/call"; import type { IWidgetApiRequest } from "matrix-widget-api"; import styles from "./InCallView.module.css"; @@ -73,6 +62,7 @@ import { widget, ElementWidgetActions } from "../widget"; import { useJoinRule } from "./useJoinRule"; import { useUrlParams } from "../UrlParams"; import { usePrefersReducedMotion } from "../usePrefersReducedMotion"; +import { ConnectionState, ParticipantInfo } from "./useGroupCall"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // There is currently a bug in Safari our our code with cloning and sending MediaStreams @@ -83,8 +73,7 @@ const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); interface Props { client: MatrixClient; groupCall: GroupCall; - participants: RoomMember[]; - calls: MatrixCall[]; + participants: Map>; roomName: string; avatarUrl: string; microphoneMuted: boolean; @@ -93,7 +82,7 @@ interface Props { toggleMicrophoneMuted: () => void; toggleScreensharing: () => void; userMediaFeeds: CallFeed[]; - activeSpeaker: string; + activeSpeaker: CallFeed | null; onLeave: () => void; isScreensharing: boolean; screenshareFeeds: CallFeed[]; @@ -102,12 +91,6 @@ interface Props { hideHeader: boolean; } -export enum ConnectionState { - EstablishingCall = "establishing call", // call hasn't been established yet - WaitMedia = "wait_media", // call is set up, waiting for ICE to connect - Connected = "connected", // media is flowing -} - // Represents something that should get a tile on the layout, // ie. a user's video feed or a screen share feed. export interface TileDescriptor { @@ -124,7 +107,6 @@ export function InCallView({ client, groupCall, participants, - calls, roomName, avatarUrl, microphoneMuted, @@ -174,50 +156,6 @@ export function InCallView({ const { hideScreensharing } = useUrlParams(); - const makeConnectionStatesMap = useCallback(() => { - const newConnStates = new Map(); - for (const participant of participants) { - const userCall = groupCall.getCallByUserId(participant.userId); - const feed = userMediaFeeds.find((f) => f.userId === participant.userId); - let connectionState = ConnectionState.EstablishingCall; - if (feed && feed.isLocal()) { - connectionState = ConnectionState.Connected; - } else if (userCall) { - if (userCall.state === CallState.Connected) { - connectionState = ConnectionState.Connected; - } else if (userCall.state === CallState.Connecting) { - connectionState = ConnectionState.WaitMedia; - } - } - newConnStates.set(participant.userId, connectionState); - } - return newConnStates; - }, [groupCall, participants, userMediaFeeds]); - - const [connStates, setConnStates] = useState( - new Map() - ); - - const updateConnectionStates = useCallback(() => { - setConnStates(makeConnectionStatesMap()); - }, [setConnStates, makeConnectionStatesMap]); - - useEffect(() => { - for (const call of calls) { - call.on(CallEvent.State, updateConnectionStates); - } - - return () => { - for (const call of calls) { - call.off(CallEvent.State, updateConnectionStates); - } - }; - }, [calls, updateConnectionStates]); - - useEffect(() => { - updateConnectionStates(); - }, [participants, updateConnectionStates]); - useEffect(() => { widget?.api.transport.send( layout === "freedom" @@ -256,59 +194,57 @@ export function InCallView({ const items = useMemo(() => { const tileDescriptors: TileDescriptor[] = []; + const localUserId = client.getUserId()!; + const localDeviceId = client.getDeviceId()!; - // one tile for each participants, to start with (we want a tile for everyone we - // think should be in the call, even if we don't have a media feed for them yet) - for (const p of participants) { - const userMediaFeed = userMediaFeeds.find((f) => f.userId === p.userId); + // One tile for each participant, to start with (we want a tile for everyone we + // think should be in the call, even if we don't have a call feed for them yet) + for (const [member, participantMap] of participants) { + for (const [deviceId, { connectionState, presenter }] of participantMap) { + const callFeed = userMediaFeeds.find( + (f) => f.userId === member.userId && f.deviceId === deviceId + ); - // NB. this assumes that the same user can't join more than once from multiple - // devices, but the participants are just RoomMembers, so this assumption is baked - // into GroupCall itself. - tileDescriptors.push({ - id: p.userId, - member: p, - callFeed: userMediaFeed, - focused: screenshareFeeds.length === 0 && p.userId === activeSpeaker, - isLocal: p.userId === client.getUserId(), - presenter: false, - connectionState: connStates.get(p.userId), - }); + tileDescriptors.push({ + id: `${member.userId} ${deviceId}`, + member, + callFeed, + focused: screenshareFeeds.length === 0 && callFeed === activeSpeaker, + isLocal: member.userId === localUserId && deviceId === localDeviceId, + presenter, + connectionState, + }); + } } PosthogAnalytics.instance.eventCallEnded.cacheParticipantCountChanged( - participants.length + tileDescriptors.length ); - // add the screenshares too + + // Add the screenshares too for (const screenshareFeed of screenshareFeeds) { - const userMediaItem = tileDescriptors.find( - (item) => item.member.userId === screenshareFeed.userId - ); + const member = screenshareFeed.getMember()!; + const connectionState = participants + .get(member) + ?.get(screenshareFeed.deviceId!)?.connectionState; - if (userMediaItem) { - userMediaItem.presenter = true; + // If the participant has left, their screenshare feed is stale and we + // shouldn't bother showing it + if (connectionState !== undefined) { + tileDescriptors.push({ + id: screenshareFeed.stream.id, + member, + callFeed: screenshareFeed, + focused: true, + isLocal: screenshareFeed.isLocal(), + presenter: false, + connectionState, + }); } - - tileDescriptors.push({ - id: screenshareFeed.stream.id, - member: screenshareFeed.getMember()!, - callFeed: screenshareFeed, - focused: true, - isLocal: screenshareFeed.isLocal(), - presenter: false, - connectionState: connStates.get(screenshareFeed.userId), - }); } return tileDescriptors; - }, [ - client, - participants, - userMediaFeeds, - activeSpeaker, - screenshareFeeds, - connStates, - ]); + }, [client, participants, userMediaFeeds, activeSpeaker, screenshareFeeds]); // The maximised participant: either the participant that the user has // manually put in fullscreen, or the focused (active) participant if the diff --git a/src/room/PTTCallView.tsx b/src/room/PTTCallView.tsx index 922bd14..d1c41e0 100644 --- a/src/room/PTTCallView.tsx +++ b/src/room/PTTCallView.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useEffect } from "react"; +import React, { useEffect, useMemo } from "react"; import useMeasure from "react-use-measure"; import { ResizeObserver } from "@juggle/resize-observer"; import i18n from "i18next"; @@ -43,6 +43,7 @@ import { PTTClips } from "../sound/PTTClips"; import { GroupCallInspector } from "./GroupCallInspector"; import { OverflowMenu } from "./OverflowMenu"; import { Size } from "../Avatar"; +import { ParticipantInfo } from "./useGroupCall"; function getPromptText( networkWaiting: boolean, @@ -100,7 +101,7 @@ interface Props { roomName: string; avatarUrl: string; groupCall: GroupCall; - participants: RoomMember[]; + participants: Map>; userMediaFeeds: CallFeed[]; onLeave: () => void; isEmbedded: boolean; @@ -152,6 +153,15 @@ export const PTTCallView: React.FC = ({ connected, } = usePTT(client, groupCall, userMediaFeeds, playClip); + const participatingMembers = useMemo(() => { + const members: RoomMember[] = []; + for (const [member, deviceMap] of participants) { + // Repeat the member for as many devices as they're using + for (let i = 0; i < deviceMap.size; i++) members.push(member); + } + return members; + }, [participants]); + const [talkingExpected, enqueueTalkingExpected, setTalkingExpected] = useDelayedState(false); const showTalkOverError = pttButtonHeld && transmitBlocked; @@ -205,7 +215,7 @@ export const PTTCallView: React.FC = ({

{t("{{count}} people connected", { - count: participants.length, + count: participatingMembers.length, })}

= ({ max={8} className={styles.facepile} client={client} - participants={participants} + members={participatingMembers} />
diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts index 14e40d1..ec496ff 100644 --- a/src/room/useGroupCall.ts +++ b/src/room/useGroupCall.ts @@ -23,7 +23,11 @@ import { GroupCallUnknownDeviceError, GroupCallError, } from "matrix-js-sdk/src/webrtc/groupCall"; -import { MatrixCall } from "matrix-js-sdk/src/webrtc/call"; +import { + CallState, + MatrixCall, + CallEvent, +} from "matrix-js-sdk/src/webrtc/call"; import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { useTranslation } from "react-i18next"; @@ -36,11 +40,21 @@ import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget"; import { getSetting } from "../settings/useSetting"; import { useEventTarget } from "../useEvents"; +export enum ConnectionState { + EstablishingCall = "establishing call", // call hasn't been established yet + WaitMedia = "wait_media", // call is set up, waiting for ICE to connect + Connected = "connected", // media is flowing +} + +export interface ParticipantInfo { + connectionState: ConnectionState; + presenter: boolean; +} + export interface UseGroupCallReturnType { state: GroupCallState; - calls: MatrixCall[]; localCallFeed: CallFeed; - activeSpeaker: string; + activeSpeaker: CallFeed | null; userMediaFeeds: CallFeed[]; microphoneMuted: boolean; localVideoMuted: boolean; @@ -55,16 +69,15 @@ export interface UseGroupCallReturnType { isScreensharing: boolean; screenshareFeeds: CallFeed[]; localDesktopCapturerSourceId: string; // XXX: This looks unused? - participants: RoomMember[]; + participants: Map>; hasLocalParticipant: boolean; unencryptedEventsFromUsers: Set; } interface State { state: GroupCallState; - calls: MatrixCall[]; localCallFeed: CallFeed; - activeSpeaker: string; + activeSpeaker: CallFeed | null; userMediaFeeds: CallFeed[]; error: TranslatedError | null; microphoneMuted: boolean; @@ -73,15 +86,51 @@ interface State { localDesktopCapturerSourceId: string; isScreensharing: boolean; requestingScreenshare: boolean; - participants: RoomMember[]; + participants: Map>; hasLocalParticipant: boolean; } +function getParticipants( + groupCall: GroupCall +): Map> { + const participants = new Map>(); + + for (const [member, participantsStateMap] of groupCall.participants) { + const callMap = groupCall.calls.get(member); + const participantInfoMap = new Map(); + participants.set(member, participantInfoMap); + + for (const [deviceId, participant] of participantsStateMap) { + const call = callMap?.get(deviceId); + const feed = groupCall.userMediaFeeds.find( + (f) => f.userId === member.userId && f.deviceId === deviceId + ); + + let connectionState = ConnectionState.EstablishingCall; + if (feed?.isLocal()) { + connectionState = ConnectionState.Connected; + } else if (call !== undefined) { + if (call.state === CallState.Connected) { + connectionState = ConnectionState.Connected; + } else if (call.state === CallState.Connecting) { + connectionState = ConnectionState.WaitMedia; + } + } + + participantInfoMap.set(deviceId, { + connectionState, + presenter: participant.screensharing, + }); + } + } + + return participants; +} + export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { const [ { state, - calls, localCallFeed, activeSpeaker, userMediaFeeds, @@ -98,7 +147,6 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { setState, ] = useState({ state: GroupCallState.LocalCallFeedUninitialized, - calls: [], localCallFeed: null, activeSpeaker: null, userMediaFeeds: [], @@ -109,7 +157,7 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { screenshareFeeds: [], localDesktopCapturerSourceId: null, requestingScreenshare: false, - participants: [], + participants: new Map(), hasLocalParticipant: false, }); @@ -120,29 +168,30 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { new Set() ); - const updateState = (state: Partial) => - setState((prevState) => ({ ...prevState, ...state })); + const updateState = useCallback( + (state: Partial) => setState((prev) => ({ ...prev, ...state })), + [setState] + ); useEffect(() => { function onGroupCallStateChanged() { updateState({ state: groupCall.state, - calls: [...groupCall.calls], localCallFeed: groupCall.localCallFeed, - activeSpeaker: groupCall.activeSpeaker, + activeSpeaker: groupCall.activeSpeaker ?? null, userMediaFeeds: [...groupCall.userMediaFeeds], microphoneMuted: groupCall.isMicrophoneMuted(), localVideoMuted: groupCall.isLocalVideoMuted(), isScreensharing: groupCall.isScreensharing(), localDesktopCapturerSourceId: groupCall.localDesktopCapturerSourceId, screenshareFeeds: [...groupCall.screenshareFeeds], - participants: [...groupCall.participants], }); } function onUserMediaFeedsChanged(userMediaFeeds: CallFeed[]): void { updateState({ userMediaFeeds: [...userMediaFeeds], + participants: getParticipants(groupCall), }); } @@ -152,9 +201,9 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { }); } - function onActiveSpeakerChanged(activeSpeaker: string): void { + function onActiveSpeakerChanged(activeSpeaker: CallFeed | undefined): void { updateState({ - activeSpeaker: activeSpeaker, + activeSpeaker: activeSpeaker ?? null, }); } @@ -179,15 +228,31 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { }); } - function onCallsChanged(calls: MatrixCall[]): void { - updateState({ - calls: [...calls], - }); + const prevCalls = new Set(); + + function onCallState(): void { + updateState({ participants: getParticipants(groupCall) }); } - function onParticipantsChanged(participants: RoomMember[]): void { + function onCallsChanged( + calls: Map> + ): void { + for (const call of prevCalls) call.off(CallEvent.State, onCallState); + prevCalls.clear(); + + for (const deviceMap of calls.values()) { + for (const call of deviceMap.values()) { + call.on(CallEvent.State, onCallState); + prevCalls.add(call); + } + } + + updateState({ participants: getParticipants(groupCall) }); + } + + function onParticipantsChanged(): void { updateState({ - participants: [...participants], + participants: getParticipants(groupCall), hasLocalParticipant: groupCall.hasLocalParticipant(), }); } @@ -218,16 +283,15 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { updateState({ error: null, state: groupCall.state, - calls: [...groupCall.calls], localCallFeed: groupCall.localCallFeed, - activeSpeaker: groupCall.activeSpeaker, + activeSpeaker: groupCall.activeSpeaker ?? null, userMediaFeeds: [...groupCall.userMediaFeeds], microphoneMuted: groupCall.isMicrophoneMuted(), localVideoMuted: groupCall.isLocalVideoMuted(), isScreensharing: groupCall.isScreensharing(), localDesktopCapturerSourceId: groupCall.localDesktopCapturerSourceId, screenshareFeeds: [...groupCall.screenshareFeeds], - participants: [...groupCall.participants], + participants: getParticipants(groupCall), hasLocalParticipant: groupCall.hasLocalParticipant(), }); @@ -264,7 +328,7 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { groupCall.removeListener(GroupCallEvent.Error, onError); groupCall.leave(); }; - }, [groupCall]); + }, [groupCall, updateState]); usePageUnload(() => { groupCall.leave(); @@ -290,7 +354,7 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { console.error(error); updateState({ error }); }); - }, [groupCall]); + }, [groupCall, updateState]); const leave = useCallback(() => groupCall.leave(), [groupCall]); @@ -341,7 +405,7 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { // toggling off groupCall.setScreensharingEnabled(false); } - }, [groupCall]); + }, [groupCall, updateState]); const onScreenshareStart = useCallback( async (ev: CustomEvent) => { @@ -355,7 +419,7 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { }); await widget.api.transport.reply(ev.detail, {}); }, - [groupCall] + [groupCall, updateState] ); const onScreenshareStop = useCallback( @@ -364,7 +428,7 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { await groupCall.setScreensharingEnabled(false); await widget.api.transport.reply(ev.detail, {}); }, - [groupCall] + [groupCall, updateState] ); useEffect(() => { @@ -402,7 +466,7 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { console.error(error); updateState({ error }); } - }, [t]); + }, [t, updateState]); const [spacebarHeld, setSpacebarHeld] = useState(false); @@ -468,7 +532,6 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { return { state, - calls, localCallFeed, activeSpeaker, userMediaFeeds, diff --git a/src/video-grid/VideoGrid.stories.tsx b/src/video-grid/VideoGrid.stories.tsx index fc3317c..e73ff89 100644 --- a/src/video-grid/VideoGrid.stories.tsx +++ b/src/video-grid/VideoGrid.stories.tsx @@ -21,7 +21,8 @@ import { RoomMember } from "matrix-js-sdk"; import { VideoGrid, useVideoGridLayout } from "./VideoGrid"; import { VideoTile } from "./VideoTile"; import { Button } from "../button"; -import { ConnectionState, TileDescriptor } from "../room/InCallView"; +import { TileDescriptor } from "../room/InCallView"; +import { ConnectionState } from "../room/useGroupCall"; export default { title: "VideoGrid", diff --git a/src/video-grid/VideoTile.tsx b/src/video-grid/VideoTile.tsx index 365d88e..a7792c6 100644 --- a/src/video-grid/VideoTile.tsx +++ b/src/video-grid/VideoTile.tsx @@ -23,7 +23,7 @@ import styles from "./VideoTile.module.css"; import { ReactComponent as MicMutedIcon } from "../icons/MicMuted.svg"; import { ReactComponent as VideoMutedIcon } from "../icons/VideoMuted.svg"; import { AudioButton, FullscreenButton } from "../button/Button"; -import { ConnectionState } from "../room/InCallView"; +import { ConnectionState } from "../room/useGroupCall"; interface Props { name: string; diff --git a/yarn.lock b/yarn.lock index 271686b..22b0d55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10196,9 +10196,9 @@ matrix-events-sdk@0.0.1: resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz#c8c38911e2cb29023b0bbac8d6f32e0de2c957dd" integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA== -"matrix-js-sdk@github:matrix-org/matrix-js-sdk#3f1c3392d45b0fc054c3788cc6c043cd5b4fb730": +"matrix-js-sdk@github:matrix-org/matrix-js-sdk#f46ecf970c658ae34b8d5fc3e73369c31ac79e90": version "21.1.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/3f1c3392d45b0fc054c3788cc6c043cd5b4fb730" + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/f46ecf970c658ae34b8d5fc3e73369c31ac79e90" dependencies: "@babel/runtime" "^7.12.5" "@types/sdp-transform" "^2.4.5"