Merge pull request #6627 from SimonBrandner/fix/voip
Properly fix VoIP issues
This commit is contained in:
commit
90a6f251c7
3 changed files with 102 additions and 88 deletions
|
@ -464,85 +464,7 @@ export default class CallHandler extends EventEmitter {
|
||||||
this.removeCallForRoom(mappedRoomId);
|
this.removeCallForRoom(mappedRoomId);
|
||||||
});
|
});
|
||||||
call.on(CallEvent.State, (newState: CallState, oldState: CallState) => {
|
call.on(CallEvent.State, (newState: CallState, oldState: CallState) => {
|
||||||
if (!this.matchesCallForThisRoom(call)) return;
|
this.onCallStateChanged(newState, oldState, call);
|
||||||
|
|
||||||
this.setCallState(call, newState);
|
|
||||||
|
|
||||||
switch (oldState) {
|
|
||||||
case CallState.Ringing:
|
|
||||||
this.pause(AudioID.Ring);
|
|
||||||
break;
|
|
||||||
case CallState.InviteSent:
|
|
||||||
this.pause(AudioID.Ringback);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newState !== CallState.Ringing) {
|
|
||||||
this.silencedCalls.delete(call.callId);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (newState) {
|
|
||||||
case CallState.Ringing: {
|
|
||||||
const incomingCallPushRule = (
|
|
||||||
new PushProcessor(MatrixClientPeg.get()).getPushRuleById(RuleId.IncomingCall)
|
|
||||||
);
|
|
||||||
const pushRuleEnabled = incomingCallPushRule?.enabled;
|
|
||||||
const tweakSetToRing = incomingCallPushRule?.actions.some((action: Tweaks) => (
|
|
||||||
action.set_tweak === TweakName.Sound &&
|
|
||||||
action.value === "ring"
|
|
||||||
));
|
|
||||||
|
|
||||||
if (pushRuleEnabled && tweakSetToRing) {
|
|
||||||
this.play(AudioID.Ring);
|
|
||||||
} else {
|
|
||||||
this.silenceCall(call.callId);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CallState.InviteSent: {
|
|
||||||
this.play(AudioID.Ringback);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CallState.Ended: {
|
|
||||||
const hangupReason = call.hangupReason;
|
|
||||||
Analytics.trackEvent('voip', 'callEnded', 'hangupReason', hangupReason);
|
|
||||||
this.removeCallForRoom(mappedRoomId);
|
|
||||||
if (oldState === CallState.InviteSent && call.hangupParty === CallParty.Remote) {
|
|
||||||
this.play(AudioID.Busy);
|
|
||||||
|
|
||||||
// Don't show a modal when we got rejected/the call was hung up
|
|
||||||
if (!hangupReason || [CallErrorCode.UserHangup, "user hangup"].includes(hangupReason)) break;
|
|
||||||
|
|
||||||
let title;
|
|
||||||
let description;
|
|
||||||
// TODO: We should either do away with these or figure out a copy for each code (expect user_hangup...)
|
|
||||||
if (call.hangupReason === CallErrorCode.UserBusy) {
|
|
||||||
title = _t("User Busy");
|
|
||||||
description = _t("The user you called is busy.");
|
|
||||||
} else {
|
|
||||||
title = _t("Call Failed");
|
|
||||||
description = _t("The call could not be established");
|
|
||||||
}
|
|
||||||
|
|
||||||
Modal.createTrackedDialog('Call Handler', 'Call Failed', ErrorDialog, {
|
|
||||||
title, description,
|
|
||||||
});
|
|
||||||
} else if (
|
|
||||||
hangupReason === CallErrorCode.AnsweredElsewhere && oldState === CallState.Connecting
|
|
||||||
) {
|
|
||||||
Modal.createTrackedDialog('Call Handler', 'Call Failed', ErrorDialog, {
|
|
||||||
title: _t("Answered Elsewhere"),
|
|
||||||
description: _t("The call was answered on another device."),
|
|
||||||
});
|
|
||||||
} else if (oldState !== CallState.Fledgling && oldState !== CallState.Ringing) {
|
|
||||||
// don't play the end-call sound for calls that never got off the ground
|
|
||||||
this.play(AudioID.CallEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logCallStats(call, mappedRoomId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
call.on(CallEvent.Replaced, (newCall: MatrixCall) => {
|
call.on(CallEvent.Replaced, (newCall: MatrixCall) => {
|
||||||
if (!this.matchesCallForThisRoom(call)) return;
|
if (!this.matchesCallForThisRoom(call)) return;
|
||||||
|
@ -598,6 +520,89 @@ export default class CallHandler extends EventEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onCallStateChanged = (newState: CallState, oldState: CallState, call: MatrixCall): void => {
|
||||||
|
if (!this.matchesCallForThisRoom(call)) return;
|
||||||
|
|
||||||
|
const mappedRoomId = this.roomIdForCall(call);
|
||||||
|
this.setCallState(call, newState);
|
||||||
|
|
||||||
|
switch (oldState) {
|
||||||
|
case CallState.Ringing:
|
||||||
|
this.pause(AudioID.Ring);
|
||||||
|
break;
|
||||||
|
case CallState.InviteSent:
|
||||||
|
this.pause(AudioID.Ringback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newState !== CallState.Ringing) {
|
||||||
|
this.silencedCalls.delete(call.callId);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (newState) {
|
||||||
|
case CallState.Ringing: {
|
||||||
|
const incomingCallPushRule = (
|
||||||
|
new PushProcessor(MatrixClientPeg.get()).getPushRuleById(RuleId.IncomingCall)
|
||||||
|
);
|
||||||
|
const pushRuleEnabled = incomingCallPushRule?.enabled;
|
||||||
|
const tweakSetToRing = incomingCallPushRule?.actions.some((action: Tweaks) => (
|
||||||
|
action.set_tweak === TweakName.Sound &&
|
||||||
|
action.value === "ring"
|
||||||
|
));
|
||||||
|
|
||||||
|
if (pushRuleEnabled && tweakSetToRing) {
|
||||||
|
this.play(AudioID.Ring);
|
||||||
|
} else {
|
||||||
|
this.silenceCall(call.callId);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CallState.InviteSent: {
|
||||||
|
this.play(AudioID.Ringback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CallState.Ended: {
|
||||||
|
const hangupReason = call.hangupReason;
|
||||||
|
Analytics.trackEvent('voip', 'callEnded', 'hangupReason', hangupReason);
|
||||||
|
this.removeCallForRoom(mappedRoomId);
|
||||||
|
if (oldState === CallState.InviteSent && call.hangupParty === CallParty.Remote) {
|
||||||
|
this.play(AudioID.Busy);
|
||||||
|
|
||||||
|
// Don't show a modal when we got rejected/the call was hung up
|
||||||
|
if (!hangupReason || [CallErrorCode.UserHangup, "user hangup"].includes(hangupReason)) break;
|
||||||
|
|
||||||
|
let title;
|
||||||
|
let description;
|
||||||
|
// TODO: We should either do away with these or figure out a copy for each code (expect user_hangup...)
|
||||||
|
if (call.hangupReason === CallErrorCode.UserBusy) {
|
||||||
|
title = _t("User Busy");
|
||||||
|
description = _t("The user you called is busy.");
|
||||||
|
} else {
|
||||||
|
title = _t("Call Failed");
|
||||||
|
description = _t("The call could not be established");
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.createTrackedDialog('Call Handler', 'Call Failed', ErrorDialog, {
|
||||||
|
title, description,
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
hangupReason === CallErrorCode.AnsweredElsewhere && oldState === CallState.Connecting
|
||||||
|
) {
|
||||||
|
Modal.createTrackedDialog('Call Handler', 'Call Failed', ErrorDialog, {
|
||||||
|
title: _t("Answered Elsewhere"),
|
||||||
|
description: _t("The call was answered on another device."),
|
||||||
|
});
|
||||||
|
} else if (oldState !== CallState.Fledgling && oldState !== CallState.Ringing) {
|
||||||
|
// don't play the end-call sound for calls that never got off the ground
|
||||||
|
this.play(AudioID.CallEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logCallStats(call, mappedRoomId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private async logCallStats(call: MatrixCall, mappedRoomId: string) {
|
private async logCallStats(call: MatrixCall, mappedRoomId: string) {
|
||||||
const stats = await call.getCurrentCallStats();
|
const stats = await call.getCurrentCallStats();
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
@ -861,6 +866,8 @@ export default class CallHandler extends EventEmitter {
|
||||||
this.calls.set(mappedRoomId, call);
|
this.calls.set(mappedRoomId, call);
|
||||||
this.emit(CallHandlerEvent.CallsChanged, this.calls);
|
this.emit(CallHandlerEvent.CallsChanged, this.calls);
|
||||||
this.setCallListeners(call);
|
this.setCallListeners(call);
|
||||||
|
// Explicitly handle first state change
|
||||||
|
this.onCallStateChanged(call.state, null, call);
|
||||||
|
|
||||||
// get ready to send encrypted events in the room, so if the user does answer
|
// get ready to send encrypted events in the room, so if the user does answer
|
||||||
// the call, we'll be ready to send. NB. This is the protocol-level room ID not
|
// the call, we'll be ready to send. NB. This is the protocol-level room ID not
|
||||||
|
|
|
@ -55,7 +55,7 @@ import { getKeyBindingsManager, NavigationAction, RoomAction } from '../../KeyBi
|
||||||
import { IOpts } from "../../createRoom";
|
import { IOpts } from "../../createRoom";
|
||||||
import SpacePanel from "../views/spaces/SpacePanel";
|
import SpacePanel from "../views/spaces/SpacePanel";
|
||||||
import { replaceableComponent } from "../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../utils/replaceableComponent";
|
||||||
import CallHandler, { CallHandlerEvent } from '../../CallHandler';
|
import CallHandler from '../../CallHandler';
|
||||||
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
||||||
import AudioFeedArrayForCall from '../views/voip/AudioFeedArrayForCall';
|
import AudioFeedArrayForCall from '../views/voip/AudioFeedArrayForCall';
|
||||||
import RoomView from './RoomView';
|
import RoomView from './RoomView';
|
||||||
|
@ -142,6 +142,7 @@ interface IState {
|
||||||
class LoggedInView extends React.Component<IProps, IState> {
|
class LoggedInView extends React.Component<IProps, IState> {
|
||||||
static displayName = 'LoggedInView';
|
static displayName = 'LoggedInView';
|
||||||
|
|
||||||
|
private dispatcherRef: string;
|
||||||
protected readonly _matrixClient: MatrixClient;
|
protected readonly _matrixClient: MatrixClient;
|
||||||
protected readonly _roomView: React.RefObject<any>;
|
protected readonly _roomView: React.RefObject<any>;
|
||||||
protected readonly _resizeContainer: React.RefObject<ResizeHandle>;
|
protected readonly _resizeContainer: React.RefObject<ResizeHandle>;
|
||||||
|
@ -156,7 +157,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
// use compact timeline view
|
// use compact timeline view
|
||||||
useCompactLayout: SettingsStore.getValue('useCompactLayout'),
|
useCompactLayout: SettingsStore.getValue('useCompactLayout'),
|
||||||
usageLimitDismissed: false,
|
usageLimitDismissed: false,
|
||||||
activeCalls: [],
|
activeCalls: CallHandler.sharedInstance().getAllActiveCalls(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// stash the MatrixClient in case we log out before we are unmounted
|
// stash the MatrixClient in case we log out before we are unmounted
|
||||||
|
@ -172,7 +173,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
document.addEventListener('keydown', this.onNativeKeyDown, false);
|
document.addEventListener('keydown', this.onNativeKeyDown, false);
|
||||||
CallHandler.sharedInstance().addListener(CallHandlerEvent.CallsChanged, this.onCallsChanged);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
|
|
||||||
this.updateServerNoticeEvents();
|
this.updateServerNoticeEvents();
|
||||||
|
|
||||||
|
@ -197,7 +198,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
document.removeEventListener('keydown', this.onNativeKeyDown, false);
|
document.removeEventListener('keydown', this.onNativeKeyDown, false);
|
||||||
CallHandler.sharedInstance().removeListener(CallHandlerEvent.CallsChanged, this.onCallsChanged);
|
dis.unregister(this.dispatcherRef);
|
||||||
this._matrixClient.removeListener("accountData", this.onAccountData);
|
this._matrixClient.removeListener("accountData", this.onAccountData);
|
||||||
this._matrixClient.removeListener("sync", this.onSync);
|
this._matrixClient.removeListener("sync", this.onSync);
|
||||||
this._matrixClient.removeListener("RoomState.events", this.onRoomStateEvents);
|
this._matrixClient.removeListener("RoomState.events", this.onRoomStateEvents);
|
||||||
|
@ -205,10 +206,16 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
this.resizer.detach();
|
this.resizer.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onCallsChanged = () => {
|
private onAction = (payload): void => {
|
||||||
this.setState({
|
switch (payload.action) {
|
||||||
activeCalls: CallHandler.sharedInstance().getAllActiveCalls(),
|
case 'call_state': {
|
||||||
});
|
const activeCalls = CallHandler.sharedInstance().getAllActiveCalls();
|
||||||
|
if (activeCalls !== this.state.activeCalls) {
|
||||||
|
this.setState({ activeCalls });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public canResetTimelineInRoom = (roomId: string) => {
|
public canResetTimelineInRoom = (roomId: string) => {
|
||||||
|
|
|
@ -32,7 +32,7 @@ export default class AudioFeedArrayForCall extends React.Component<IProps, IStat
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
feeds: [],
|
feeds: this.props.call.getRemoteFeeds(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue