diff --git a/res/css/views/messages/_CallEvent.scss b/res/css/views/messages/_CallEvent.scss index 1804462d4f..1bf62af22e 100644 --- a/res/css/views/messages/_CallEvent.scss +++ b/res/css/views/messages/_CallEvent.scss @@ -94,5 +94,29 @@ limitations under the License. .mx_CallEvent_content_tooltip { margin-right: 5px; } + + .mx_CallEvent_iconButton { + display: inline-flex; + margin-right: 16px; + + &::before { + content: ''; + + height: 16px; + width: 16px; + background-color: $icon-button-color; + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; + } + } + + .mx_CallEvent_silence::before { + mask-image: url('$(res)/img/voip/silence.svg'); + } + + .mx_CallEvent_unSilence::before { + mask-image: url('$(res)/img/voip/un-silence.svg'); + } } } diff --git a/src/components/structures/CallEventGrouper.ts b/src/components/structures/CallEventGrouper.ts index ab1444d4fa..c71d1a032a 100644 --- a/src/components/structures/CallEventGrouper.ts +++ b/src/components/structures/CallEventGrouper.ts @@ -24,6 +24,7 @@ import defaultDispatcher from "../../dispatcher/dispatcher"; export enum CallEventGrouperEvent { StateChanged = "state_changed", + SilencedChanged = "silenced_changed", } const SUPPORTED_STATES = [ @@ -44,7 +45,8 @@ export default class CallEventGrouper extends EventEmitter { constructor() { super(); - CallHandler.sharedInstance().addListener(CallHandlerEvent.CallsChanged, this.setCall) + CallHandler.sharedInstance().addListener(CallHandlerEvent.CallsChanged, this.setCall); + CallHandler.sharedInstance().addListener(CallHandlerEvent.SilencedCallsChanged, this.onSilencedCallsChanged); } private get invite(): MatrixEvent { @@ -79,6 +81,15 @@ export default class CallEventGrouper extends EventEmitter { return ![...this.events].some((event) => event.sender?.userId === MatrixClientPeg.get().getUserId()); } + private get callId(): string { + return [...this.events][0].getContent().call_id; + } + + private onSilencedCallsChanged = () => { + const newState = CallHandler.sharedInstance().isCallSilenced(this.callId); + this.emit(CallEventGrouperEvent.SilencedChanged, newState) + } + public answerCall = () => { this.call?.answer(); } @@ -95,6 +106,12 @@ export default class CallEventGrouper extends EventEmitter { }); } + public toggleSilenced = () => { + const silenced = CallHandler.sharedInstance().isCallSilenced(this.callId); + silenced ? + CallHandler.sharedInstance().unSilenceCall(this.callId) : + CallHandler.sharedInstance().silenceCall(this.callId); + } private setCallListeners() { if (!this.call) return; @@ -116,8 +133,7 @@ export default class CallEventGrouper extends EventEmitter { private setCall = () => { if (this.call) return; - const callId = [...this.events][0].getContent().call_id; - this.call = CallHandler.sharedInstance().getCallById(callId); + this.call = CallHandler.sharedInstance().getCallById(this.callId); this.setCallListeners(); this.setState(); } diff --git a/src/components/views/messages/CallEvent.tsx b/src/components/views/messages/CallEvent.tsx index 00b62e4482..4710391050 100644 --- a/src/components/views/messages/CallEvent.tsx +++ b/src/components/views/messages/CallEvent.tsx @@ -24,6 +24,7 @@ import FormButton from '../elements/FormButton'; import { CallErrorCode, CallState } from 'matrix-js-sdk/src/webrtc/call'; import InfoTooltip, { InfoTooltipKind } from '../elements/InfoTooltip'; import classNames from 'classnames'; +import AccessibleTooltipButton from '../elements/AccessibleTooltipButton'; interface IProps { mxEvent: MatrixEvent; @@ -32,6 +33,7 @@ interface IProps { interface IState { callState: CallState | CustomCallState; + silenced: boolean; } const TEXTUAL_STATES: Map = new Map([ @@ -45,25 +47,43 @@ export default class CallEvent extends React.Component { this.state = { callState: this.props.callEventGrouper.state, + silenced: false, } } componentDidMount() { this.props.callEventGrouper.addListener(CallEventGrouperEvent.StateChanged, this.onStateChanged); + this.props.callEventGrouper.addListener(CallEventGrouperEvent.SilencedChanged, this.onSilencedChanged); } componentWillUnmount() { this.props.callEventGrouper.removeListener(CallEventGrouperEvent.StateChanged, this.onStateChanged); + this.props.callEventGrouper.removeListener(CallEventGrouperEvent.SilencedChanged, this.onSilencedChanged); } + private onSilencedChanged = (newState) => { + this.setState({ silenced: newState }); + }; + private onStateChanged = (newState: CallState) => { this.setState({callState: newState}); - } + }; private renderContent(state: CallState | CustomCallState): JSX.Element { if (state === CallState.Ringing) { + const silenceClass = classNames({ + "mx_CallEvent_iconButton": true, + "mx_CallEvent_unSilence": this.state.silenced, + "mx_CallEvent_silence": !this.state.silenced, + }); + return (
+