Prepare for Element Call integration (#9224)

* Improve accessibility and testability of Tooltip

Adding a role to Tooltip was motivated by React Testing Library's
reliance on accessibility-related attributes to locate elements.

* Make the ReadyWatchingStore constructor safer

The ReadyWatchingStore constructor previously had a chance to
immediately call onReady, which was dangerous because it was potentially
calling the derived class's onReady at a point when the derived class
hadn't even finished construction yet. In normal usage, I guess this
never was a problem, but it was causing some of the tests I was writing
to crash. This is solved by separating out the onReady call into a start
method.

* Rename 1:1 call components to 'LegacyCall'

to reflect the fact that they're slated for removal, and to not clash
with the new Call code.

* Refactor VideoChannelStore into Call and CallStore

Call is an abstract class that currently only has a Jitsi
implementation, but this will make it easy to later add an Element Call
implementation.

* Remove WidgetReady, ClientReady, and ForceHangupCall hacks

These are no longer used by the new Jitsi call implementation, and can
be removed.

* yarn i18n

* Delete call map entries instead of inserting nulls

* Allow multiple active calls and consolidate call listeners

* Fix a race condition when creating a video room

* Un-hardcode the media device fallback labels

* Apply misc code review fixes

* yarn i18n

* Disconnect from calls more politely on logout

* Fix some strict mode errors

* Fix another updateRoom race condition
This commit is contained in:
Robin 2022-08-30 15:13:39 -04:00 committed by GitHub
parent 50f6986f6c
commit 0d6a550c33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
107 changed files with 2573 additions and 2157 deletions

View file

@ -21,14 +21,14 @@ import React from 'react';
import { CallType, MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
import classNames from 'classnames';
import CallHandler, { CallHandlerEvent } from '../CallHandler';
import LegacyCallHandler, { LegacyCallHandlerEvent } from '../LegacyCallHandler';
import { MatrixClientPeg } from '../MatrixClientPeg';
import { _t } from '../languageHandler';
import RoomAvatar from '../components/views/avatars/RoomAvatar';
import AccessibleTooltipButton from '../components/views/elements/AccessibleTooltipButton';
import AccessibleButton from '../components/views/elements/AccessibleButton';
export const getIncomingCallToastKey = (callId: string) => `call_${callId}`;
export const getIncomingLegacyCallToastKey = (callId: string) => `call_${callId}`;
interface IProps {
call: MatrixCall;
@ -38,83 +38,87 @@ interface IState {
silenced: boolean;
}
export default class IncomingCallToast extends React.Component<IProps, IState> {
export default class IncomingLegacyCallToast extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
silenced: CallHandler.instance.isCallSilenced(this.props.call.callId),
silenced: LegacyCallHandler.instance.isCallSilenced(this.props.call.callId),
};
}
public componentDidMount = (): void => {
CallHandler.instance.addListener(CallHandlerEvent.SilencedCallsChanged, this.onSilencedCallsChanged);
LegacyCallHandler.instance.addListener(
LegacyCallHandlerEvent.SilencedCallsChanged, this.onSilencedCallsChanged,
);
};
public componentWillUnmount(): void {
CallHandler.instance.removeListener(CallHandlerEvent.SilencedCallsChanged, this.onSilencedCallsChanged);
LegacyCallHandler.instance.removeListener(
LegacyCallHandlerEvent.SilencedCallsChanged, this.onSilencedCallsChanged,
);
}
private onSilencedCallsChanged = (): void => {
this.setState({ silenced: CallHandler.instance.isCallSilenced(this.props.call.callId) });
this.setState({ silenced: LegacyCallHandler.instance.isCallSilenced(this.props.call.callId) });
};
private onAnswerClick = (e: React.MouseEvent): void => {
e.stopPropagation();
CallHandler.instance.answerCall(CallHandler.instance.roomIdForCall(this.props.call));
LegacyCallHandler.instance.answerCall(LegacyCallHandler.instance.roomIdForCall(this.props.call));
};
private onRejectClick= (e: React.MouseEvent): void => {
e.stopPropagation();
CallHandler.instance.hangupOrReject(CallHandler.instance.roomIdForCall(this.props.call), true);
LegacyCallHandler.instance.hangupOrReject(LegacyCallHandler.instance.roomIdForCall(this.props.call), true);
};
private onSilenceClick = (e: React.MouseEvent): void => {
e.stopPropagation();
const callId = this.props.call.callId;
this.state.silenced ?
CallHandler.instance.unSilenceCall(callId) :
CallHandler.instance.silenceCall(callId);
LegacyCallHandler.instance.unSilenceCall(callId) :
LegacyCallHandler.instance.silenceCall(callId);
};
public render() {
const call = this.props.call;
const room = MatrixClientPeg.get().getRoom(CallHandler.instance.roomIdForCall(call));
const room = MatrixClientPeg.get().getRoom(LegacyCallHandler.instance.roomIdForCall(call));
const isVoice = call.type === CallType.Voice;
const contentClass = classNames("mx_IncomingCallToast_content", {
"mx_IncomingCallToast_content_voice": isVoice,
"mx_IncomingCallToast_content_video": !isVoice,
const contentClass = classNames("mx_IncomingLegacyCallToast_content", {
"mx_IncomingLegacyCallToast_content_voice": isVoice,
"mx_IncomingLegacyCallToast_content_video": !isVoice,
});
const silenceClass = classNames("mx_IncomingCallToast_iconButton", {
"mx_IncomingCallToast_unSilence": this.state.silenced,
"mx_IncomingCallToast_silence": !this.state.silenced,
const silenceClass = classNames("mx_IncomingLegacyCallToast_iconButton", {
"mx_IncomingLegacyCallToast_unSilence": this.state.silenced,
"mx_IncomingLegacyCallToast_silence": !this.state.silenced,
});
return <React.Fragment>
<RoomAvatar
room={room}
room={room ?? undefined}
height={32}
width={32}
/>
<div className={contentClass}>
<span className="mx_CallEvent_caller">
<span className="mx_LegacyCallEvent_caller">
{ room ? room.name : _t("Unknown caller") }
</span>
<div className="mx_CallEvent_type">
<div className="mx_CallEvent_type_icon" />
<div className="mx_LegacyCallEvent_type">
<div className="mx_LegacyCallEvent_type_icon" />
{ isVoice ? _t("Voice call") : _t("Video call") }
</div>
<div className="mx_IncomingCallToast_buttons">
<div className="mx_IncomingLegacyCallToast_buttons">
<AccessibleButton
className="mx_IncomingCallToast_button mx_IncomingCallToast_button_decline"
className="mx_IncomingLegacyCallToast_button mx_IncomingLegacyCallToast_button_decline"
onClick={this.onRejectClick}
kind="danger"
>
<span> { _t("Decline") } </span>
</AccessibleButton>
<AccessibleButton
className="mx_IncomingCallToast_button mx_IncomingCallToast_button_accept"
className="mx_IncomingLegacyCallToast_button mx_IncomingLegacyCallToast_button_accept"
onClick={this.onAnswerClick}
kind="primary"
>