Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 2c612d5

Browse files
authored
Use native js-sdk group call support (#9625)
* Use native js-sdk group call support Now that the js-sdk supports group calls natively, our group call implementation can be simplified a bit. Switching to the js-sdk implementation also brings the react-sdk up to date with recent MSC3401 changes, and adds support for joining calls from multiple devices. (So, the previous logic which sent to-device messages to prevent multi-device sessions is no longer necessary.) * Fix strings * Fix strict type errors
1 parent 3c7781a commit 2c612d5

File tree

20 files changed

+375
-559
lines changed

20 files changed

+375
-559
lines changed

src/components/views/beacon/RoomCallBanner.tsx

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,34 @@ limitations under the License.
1515
*/
1616

1717
import React, { useCallback } from "react";
18-
import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
18+
import { EventType } from "matrix-js-sdk/src/@types/event";
19+
import { Room } from "matrix-js-sdk/src/models/room";
20+
import { logger } from "matrix-js-sdk/src/logger";
1921

2022
import { _t } from "../../../languageHandler";
2123
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
2224
import dispatcher, { defaultDispatcher } from "../../../dispatcher/dispatcher";
2325
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
2426
import { Action } from "../../../dispatcher/actions";
25-
import { Call, ConnectionState, ElementCall } from "../../../models/Call";
27+
import { ConnectionState, ElementCall } from "../../../models/Call";
2628
import { useCall } from "../../../hooks/useCall";
2729
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
2830
import {
2931
OwnBeaconStore,
3032
OwnBeaconStoreEvent,
3133
} from "../../../stores/OwnBeaconStore";
32-
import { CallDurationFromEvent } from "../voip/CallDuration";
34+
import { GroupCallDuration } from "../voip/CallDuration";
3335
import { SdkContextClass } from "../../../contexts/SDKContext";
3436

3537
interface RoomCallBannerProps {
3638
roomId: Room["roomId"];
37-
call: Call;
39+
call: ElementCall;
3840
}
3941

4042
const RoomCallBannerInner: React.FC<RoomCallBannerProps> = ({
4143
roomId,
4244
call,
4345
}) => {
44-
const callEvent: MatrixEvent | null = (call as ElementCall)?.groupCall;
45-
4646
const connect = useCallback(
4747
(ev: ButtonEvent) => {
4848
ev.preventDefault();
@@ -57,15 +57,23 @@ const RoomCallBannerInner: React.FC<RoomCallBannerProps> = ({
5757
);
5858

5959
const onClick = useCallback(() => {
60+
const event = call.groupCall.room.currentState.getStateEvents(
61+
EventType.GroupCallPrefix, call.groupCall.groupCallId,
62+
);
63+
if (event === null) {
64+
logger.error("Couldn't find a group call event to jump to");
65+
return;
66+
}
67+
6068
dispatcher.dispatch<ViewRoomPayload>({
6169
action: Action.ViewRoom,
6270
room_id: roomId,
6371
metricsTrigger: undefined,
64-
event_id: callEvent.getId(),
72+
event_id: event.getId(),
6573
scroll_into_view: true,
6674
highlighted: true,
6775
});
68-
}, [callEvent, roomId]);
76+
}, [call, roomId]);
6977

7078
return (
7179
<div
@@ -74,7 +82,7 @@ const RoomCallBannerInner: React.FC<RoomCallBannerProps> = ({
7482
>
7583
<div className="mx_RoomCallBanner_text">
7684
<span className="mx_RoomCallBanner_label">{ _t("Video call") }</span>
77-
<CallDurationFromEvent mxEvent={callEvent} />
85+
<GroupCallDuration groupCall={call.groupCall} />
7886
</div>
7987

8088
<AccessibleButton
@@ -119,12 +127,11 @@ const RoomCallBanner: React.FC<Props> = ({ roomId }) => {
119127
}
120128

121129
// Split into outer/inner to avoid watching various parts if there is no call
122-
if (call) {
123-
// No banner if the call is connected (or connecting/disconnecting)
124-
if (call.connectionState !== ConnectionState.Disconnected) return null;
125-
126-
return <RoomCallBannerInner call={call} roomId={roomId} />;
130+
// No banner if the call is connected (or connecting/disconnecting)
131+
if (call !== null && call.connectionState === ConnectionState.Disconnected) {
132+
return <RoomCallBannerInner call={call as ElementCall} roomId={roomId} />;
127133
}
134+
128135
return null;
129136
};
130137

src/components/views/messages/CallEvent.tsx

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@ import React, { forwardRef, useCallback, useContext, useMemo } from "react";
1818

1919
import type { MatrixEvent } from "matrix-js-sdk/src/models/event";
2020
import type { RoomMember } from "matrix-js-sdk/src/models/room-member";
21-
import { Call, ConnectionState } from "../../../models/Call";
21+
import { ConnectionState, ElementCall } from "../../../models/Call";
2222
import { _t } from "../../../languageHandler";
2323
import {
2424
useCall,
2525
useConnectionState,
26-
useJoinCallButtonDisabled,
27-
useJoinCallButtonTooltip,
28-
useParticipants,
26+
useJoinCallButtonDisabledTooltip,
27+
useParticipatingMembers,
2928
} from "../../../hooks/useCall";
3029
import defaultDispatcher from "../../../dispatcher/dispatcher";
3130
import type { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
@@ -35,38 +34,38 @@ import MemberAvatar from "../avatars/MemberAvatar";
3534
import { LiveContentSummary, LiveContentType } from "../rooms/LiveContentSummary";
3635
import FacePile from "../elements/FacePile";
3736
import MatrixClientContext from "../../../contexts/MatrixClientContext";
38-
import { CallDuration, CallDurationFromEvent } from "../voip/CallDuration";
37+
import { CallDuration, GroupCallDuration } from "../voip/CallDuration";
3938
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
4039

4140
const MAX_FACES = 8;
4241

4342
interface ActiveCallEventProps {
4443
mxEvent: MatrixEvent;
45-
participants: Set<RoomMember>;
44+
call: ElementCall | null;
45+
participatingMembers: RoomMember[];
4646
buttonText: string;
4747
buttonKind: string;
48-
buttonTooltip?: string;
49-
buttonDisabled?: boolean;
48+
buttonDisabledTooltip?: string;
5049
onButtonClick: ((ev: ButtonEvent) => void) | null;
5150
}
5251

5352
const ActiveCallEvent = forwardRef<any, ActiveCallEventProps>(
5453
(
5554
{
5655
mxEvent,
57-
participants,
56+
call,
57+
participatingMembers,
5858
buttonText,
5959
buttonKind,
60-
buttonDisabled,
61-
buttonTooltip,
60+
buttonDisabledTooltip,
6261
onButtonClick,
6362
},
6463
ref,
6564
) => {
6665
const senderName = useMemo(() => mxEvent.sender?.name ?? mxEvent.getSender(), [mxEvent]);
6766

68-
const facePileMembers = useMemo(() => [...participants].slice(0, MAX_FACES), [participants]);
69-
const facePileOverflow = participants.size > facePileMembers.length;
67+
const facePileMembers = useMemo(() => participatingMembers.slice(0, MAX_FACES), [participatingMembers]);
68+
const facePileOverflow = participatingMembers.length > facePileMembers.length;
7069

7170
return <div className="mx_CallEvent_wrapper" ref={ref}>
7271
<div className="mx_CallEvent mx_CallEvent_active">
@@ -85,17 +84,17 @@ const ActiveCallEvent = forwardRef<any, ActiveCallEventProps>(
8584
type={LiveContentType.Video}
8685
text={_t("Video call")}
8786
active={false}
88-
participantCount={participants.size}
87+
participantCount={participatingMembers.length}
8988
/>
9089
<FacePile members={facePileMembers} faceSize={24} overflow={facePileOverflow} />
9190
</div>
92-
<CallDurationFromEvent mxEvent={mxEvent} />
91+
{ call && <GroupCallDuration groupCall={call.groupCall} /> }
9392
<AccessibleTooltipButton
9493
className="mx_CallEvent_button"
9594
kind={buttonKind}
96-
disabled={onButtonClick === null || buttonDisabled}
95+
disabled={onButtonClick === null || buttonDisabledTooltip !== undefined}
9796
onClick={onButtonClick}
98-
tooltip={buttonTooltip}
97+
tooltip={buttonDisabledTooltip}
9998
>
10099
{ buttonText }
101100
</AccessibleTooltipButton>
@@ -106,14 +105,13 @@ const ActiveCallEvent = forwardRef<any, ActiveCallEventProps>(
106105

107106
interface ActiveLoadedCallEventProps {
108107
mxEvent: MatrixEvent;
109-
call: Call;
108+
call: ElementCall;
110109
}
111110

112111
const ActiveLoadedCallEvent = forwardRef<any, ActiveLoadedCallEventProps>(({ mxEvent, call }, ref) => {
113112
const connectionState = useConnectionState(call);
114-
const participants = useParticipants(call);
115-
const joinCallButtonTooltip = useJoinCallButtonTooltip(call);
116-
const joinCallButtonDisabled = useJoinCallButtonDisabled(call);
113+
const participatingMembers = useParticipatingMembers(call);
114+
const joinCallButtonDisabledTooltip = useJoinCallButtonDisabledTooltip(call);
117115

118116
const connect = useCallback((ev: ButtonEvent) => {
119117
ev.preventDefault();
@@ -142,11 +140,11 @@ const ActiveLoadedCallEvent = forwardRef<any, ActiveLoadedCallEventProps>(({ mxE
142140
return <ActiveCallEvent
143141
ref={ref}
144142
mxEvent={mxEvent}
145-
participants={participants}
143+
call={call}
144+
participatingMembers={participatingMembers}
146145
buttonText={buttonText}
147146
buttonKind={buttonKind}
148-
buttonDisabled={joinCallButtonDisabled}
149-
buttonTooltip={joinCallButtonTooltip}
147+
buttonDisabledTooltip={joinCallButtonDisabledTooltip ?? undefined}
150148
onButtonClick={onButtonClick}
151149
/>;
152150
});
@@ -159,7 +157,6 @@ interface CallEventProps {
159157
* An event tile representing an active or historical Element call.
160158
*/
161159
export const CallEvent = forwardRef<any, CallEventProps>(({ mxEvent }, ref) => {
162-
const noParticipants = useMemo(() => new Set<RoomMember>(), []);
163160
const client = useContext(MatrixClientContext);
164161
const call = useCall(mxEvent.getRoomId()!);
165162
const latestEvent = client.getRoom(mxEvent.getRoomId())!.currentState
@@ -180,12 +177,13 @@ export const CallEvent = forwardRef<any, CallEventProps>(({ mxEvent }, ref) => {
180177
return <ActiveCallEvent
181178
ref={ref}
182179
mxEvent={mxEvent}
183-
participants={noParticipants}
180+
call={null}
181+
participatingMembers={[]}
184182
buttonText={_t("Join")}
185183
buttonKind="primary"
186184
onButtonClick={null}
187185
/>;
188186
}
189187

190-
return <ActiveLoadedCallEvent mxEvent={mxEvent} call={call} ref={ref} />;
188+
return <ActiveLoadedCallEvent mxEvent={mxEvent} call={call as ElementCall} ref={ref} />;
191189
});

src/components/views/rooms/LiveContentSummary.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import classNames from "classnames";
1919

2020
import { _t } from "../../../languageHandler";
2121
import { Call } from "../../../models/Call";
22-
import { useParticipants } from "../../../hooks/useCall";
22+
import { useParticipantCount } from "../../../hooks/useCall";
2323

2424
export enum LiveContentType {
2525
Video,
@@ -62,13 +62,10 @@ interface LiveContentSummaryWithCallProps {
6262
call: Call;
6363
}
6464

65-
export function LiveContentSummaryWithCall({ call }: LiveContentSummaryWithCallProps) {
66-
const participants = useParticipants(call);
67-
68-
return <LiveContentSummary
65+
export const LiveContentSummaryWithCall: FC<LiveContentSummaryWithCallProps> = ({ call }) =>
66+
<LiveContentSummary
6967
type={LiveContentType.Video}
7068
text={_t("Video")}
7169
active={false}
72-
participantCount={participants.size}
70+
participantCount={useParticipantCount(call)}
7371
/>;
74-
}

src/components/views/rooms/RoomHeader.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ import IconizedContextMenu, {
6666
IconizedContextMenuRadio,
6767
} from "../context_menus/IconizedContextMenu";
6868
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
69-
import { CallDurationFromEvent } from "../voip/CallDuration";
69+
import { GroupCallDuration } from "../voip/CallDuration";
7070
import { Alignment } from "../elements/Tooltip";
7171
import RoomCallBanner from '../beacon/RoomCallBanner';
7272

@@ -512,7 +512,7 @@ export default class RoomHeader extends React.Component<IProps, IState> {
512512
}
513513

514514
if (this.props.viewingCall && this.props.activeCall instanceof ElementCall) {
515-
startButtons.push(<CallLayoutSelector call={this.props.activeCall} />);
515+
startButtons.push(<CallLayoutSelector key="layout" call={this.props.activeCall} />);
516516
}
517517

518518
if (!this.props.viewingCall && this.props.onForgetClick) {
@@ -685,7 +685,7 @@ export default class RoomHeader extends React.Component<IProps, IState> {
685685
{ _t("Video call") }
686686
</div>
687687
{ this.props.activeCall instanceof ElementCall && (
688-
<CallDurationFromEvent mxEvent={this.props.activeCall.groupCall} />
688+
<GroupCallDuration groupCall={this.props.activeCall.groupCall} />
689689
) }
690690
{ /* Empty topic element to fill out space */ }
691691
<div className="mx_RoomHeader_topic" />

src/components/views/rooms/RoomTileCallSummary.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import React, { FC } from "react";
1818

1919
import type { Call } from "../../../models/Call";
2020
import { _t } from "../../../languageHandler";
21-
import { useConnectionState, useParticipants } from "../../../hooks/useCall";
21+
import { useConnectionState, useParticipantCount } from "../../../hooks/useCall";
2222
import { ConnectionState } from "../../../models/Call";
2323
import { LiveContentSummary, LiveContentType } from "./LiveContentSummary";
2424

@@ -27,13 +27,10 @@ interface Props {
2727
}
2828

2929
export const RoomTileCallSummary: FC<Props> = ({ call }) => {
30-
const connectionState = useConnectionState(call);
31-
const participants = useParticipants(call);
32-
3330
let text: string;
3431
let active: boolean;
3532

36-
switch (connectionState) {
33+
switch (useConnectionState(call)) {
3734
case ConnectionState.Disconnected:
3835
text = _t("Video");
3936
active = false;
@@ -53,6 +50,6 @@ export const RoomTileCallSummary: FC<Props> = ({ call }) => {
5350
type={LiveContentType.Video}
5451
text={text}
5552
active={active}
56-
participantCount={participants.size}
53+
participantCount={useParticipantCount(call)}
5754
/>;
5855
};

src/components/views/voip/CallDuration.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import React, { FC, useState, useEffect } from "react";
17+
import React, { FC, useState, useEffect, memo } from "react";
18+
import { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
1819

19-
import type { MatrixEvent } from "matrix-js-sdk/src/models/event";
2020
import { formatCallTime } from "../../../DateUtils";
2121

2222
interface CallDurationProps {
@@ -26,26 +26,28 @@ interface CallDurationProps {
2626
/**
2727
* A call duration counter.
2828
*/
29-
export const CallDuration: FC<CallDurationProps> = ({ delta }) => {
29+
export const CallDuration: FC<CallDurationProps> = memo(({ delta }) => {
3030
// Clock desync could lead to a negative duration, so just hide it if that happens
3131
if (delta <= 0) return null;
3232
return <div className="mx_CallDuration">{ formatCallTime(new Date(delta)) }</div>;
33-
};
33+
});
3434

35-
interface CallDurationFromEventProps {
36-
mxEvent: MatrixEvent;
35+
interface GroupCallDurationProps {
36+
groupCall: GroupCall;
3737
}
3838

3939
/**
40-
* A call duration counter that automatically counts up, given the event that
41-
* started the call.
40+
* A call duration counter that automatically counts up, given a live GroupCall
41+
* object.
4242
*/
43-
export const CallDurationFromEvent: FC<CallDurationFromEventProps> = ({ mxEvent }) => {
43+
export const GroupCallDuration: FC<GroupCallDurationProps> = ({ groupCall }) => {
4444
const [now, setNow] = useState(() => Date.now());
4545
useEffect(() => {
4646
const timer = setInterval(() => setNow(Date.now()), 1000);
4747
return () => clearInterval(timer);
4848
}, []);
4949

50-
return <CallDuration delta={now - mxEvent.getTs()} />;
50+
return groupCall.creationTs === null
51+
? null
52+
: <CallDuration delta={now - groupCall.creationTs} />;
5153
};

0 commit comments

Comments
 (0)