1
0
mirror of https://github.com/matrix-org/matrix-react-sdk.git synced 2025-11-05 23:10:41 +03:00

VoIP virtual rooms, mk II

Does a thirdparty protocol lookup to the homeserver to get the
corresponding native/virtual user for a matrix ID. Stores the
mappings in room account data. Involves some slightly nasty workarounds
for that fact that room account data has no local echo.
This commit is contained in:
David Baker
2021-02-12 20:55:54 +00:00
parent 59069f95e9
commit 196507a730
9 changed files with 208 additions and 101 deletions

View File

@@ -84,10 +84,17 @@ import { CallError } from "matrix-js-sdk/src/webrtc/call";
import { logger } from 'matrix-js-sdk/src/logger';
import DesktopCapturerSourcePicker from "./components/views/elements/DesktopCapturerSourcePicker"
import { Action } from './dispatcher/actions';
import { roomForVirtualRoom, getOrCreateVirtualRoomForRoom } from './VoipUserMapper';
import VoipUserMapper from './VoipUserMapper';
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
const CHECK_PSTN_SUPPORT_ATTEMPTS = 3;
export const PROTOCOL_PSTN = 'm.protocol.pstn';
export const PROTOCOL_PSTN_PREFIXED = 'im.vector.protocol.pstn';
export const PROTOCOL_SIP_NATIVE = 'im.vector.protocol.sip_native';
export const PROTOCOL_SIP_VIRTUAL = 'im.vector.protocol.sip_virtual';
const CHECK_PROTOCOLS_ATTEMPTS = 3;
// Event type for room account data used to mark rooms as virtual rooms (and store the ID of their native room)
export const VIRTUAL_ROOM_EVENT_TYPE = 'im.vector.is_virtual_room';
enum AudioID {
Ring = 'ringAudio',
@@ -96,6 +103,12 @@ enum AudioID {
Busy = 'busyAudio',
}
interface ThirpartyLookupResponse {
userid: string,
protocol: string,
fields: {[key: string]: any},
}
// Unlike 'CallType' in js-sdk, this one includes screen sharing
// (because a screen sharing call is only a screen sharing call to the caller,
// to the callee it's just a video call, at least as far as the current impl
@@ -126,7 +139,12 @@ export default class CallHandler {
private audioPromises = new Map<AudioID, Promise<void>>();
private dispatcherRef: string = null;
private supportsPstnProtocol = null;
private pstnSupportPrefixed = null; // True if the server only support the prefixed pstn protocol
private supportsSipNativeVirtual = null; // im.vector.protocol.sip_virtual and im.vector.protocol.sip_native
private pstnSupportCheckTimer: NodeJS.Timeout; // number actually because we're in the browser
// For rooms we've been invited to, true if they're from virtual user, false if we've checked and they aren't.
private invitedRoomsAreVirtual = new Map<string, boolean>();
private invitedRoomCheckInProgress = false;
static sharedInstance() {
if (!window.mxCallHandler) {
@@ -140,9 +158,9 @@ export default class CallHandler {
* Gets the user-facing room associated with a call (call.roomId may be the call "virtual room"
* if a voip_mxid_translate_pattern is set in the config)
*/
public static roomIdForCall(call: MatrixCall) {
public static roomIdForCall(call: MatrixCall): string {
if (!call) return null;
return roomForVirtualRoom(call.roomId) || call.roomId;
return VoipUserMapper.sharedInstance().nativeRoomForVirtualRoom(call.roomId) || call.roomId;
}
start() {
@@ -163,7 +181,7 @@ export default class CallHandler {
MatrixClientPeg.get().on('Call.incoming', this.onCallIncoming);
}
this.checkForPstnSupport(CHECK_PSTN_SUPPORT_ATTEMPTS);
this.checkProtocols(CHECK_PROTOCOLS_ATTEMPTS);
}
stop() {
@@ -177,33 +195,73 @@ export default class CallHandler {
}
}
private async checkForPstnSupport(maxTries) {
private async checkProtocols(maxTries) {
try {
const protocols = await MatrixClientPeg.get().getThirdpartyProtocols();
if (protocols['im.vector.protocol.pstn'] !== undefined) {
this.supportsPstnProtocol = protocols['im.vector.protocol.pstn'];
} else if (protocols['m.protocol.pstn'] !== undefined) {
this.supportsPstnProtocol = protocols['m.protocol.pstn'];
if (protocols[PROTOCOL_PSTN] !== undefined) {
this.supportsPstnProtocol = Boolean(protocols[PROTOCOL_PSTN]);
if (this.supportsPstnProtocol) this.pstnSupportPrefixed = false;
} else if (protocols[PROTOCOL_PSTN_PREFIXED] !== undefined) {
this.supportsPstnProtocol = Boolean(protocols[PROTOCOL_PSTN_PREFIXED]);
if (this.supportsPstnProtocol) this.pstnSupportPrefixed = true;
} else {
this.supportsPstnProtocol = null;
}
dis.dispatch({action: Action.PstnSupportUpdated});
if (protocols[PROTOCOL_SIP_NATIVE] !== undefined && protocols[PROTOCOL_SIP_VIRTUAL] !== undefined) {
this.supportsSipNativeVirtual = Boolean(
protocols[PROTOCOL_SIP_NATIVE] && protocols[PROTOCOL_SIP_VIRTUAL],
);
}
dis.dispatch({action: Action.VirtualRoomSupportUpdated});
} catch (e) {
if (maxTries === 1) {
console.log("Failed to check for pstn protocol support and no retries remain: assuming no support", e);
console.log("Failed to check for protocol support and no retries remain: assuming no support", e);
} else {
console.log("Failed to check for pstn protocol support: will retry", e);
console.log("Failed to check for protocol support: will retry", e);
this.pstnSupportCheckTimer = setTimeout(() => {
this.checkForPstnSupport(maxTries - 1);
this.checkProtocols(maxTries - 1);
}, 10000);
}
}
}
getSupportsPstnProtocol() {
public getSupportsPstnProtocol() {
return this.supportsPstnProtocol;
}
public getSupportsVirtualRooms() {
return this.supportsPstnProtocol;
}
public pstnLookup(phoneNumber: string): Promise<ThirpartyLookupResponse[]> {
return MatrixClientPeg.get().getThirdpartyUser(
this.pstnSupportPrefixed ? PROTOCOL_PSTN_PREFIXED : PROTOCOL_PSTN, {
'm.id.phone': phoneNumber,
},
);
}
public sipVirtualLookup(nativeMxid: string): Promise<ThirpartyLookupResponse[]> {
return MatrixClientPeg.get().getThirdpartyUser(
PROTOCOL_SIP_VIRTUAL, {
'native_mxid': nativeMxid,
},
);
}
public sipNativeLookup(virtualMxid: string): Promise<ThirpartyLookupResponse[]> {
return MatrixClientPeg.get().getThirdpartyUser(
PROTOCOL_SIP_NATIVE, {
'virtual_mxid': virtualMxid,
},
);
}
private onCallIncoming = (call) => {
// we dispatch this synchronously to make sure that the event
// handlers on the call are set up immediately (so that if
@@ -543,7 +601,7 @@ export default class CallHandler {
Analytics.trackEvent('voip', 'placeCall', 'type', type);
CountlyAnalytics.instance.trackStartCall(roomId, type === PlaceCallType.Video, false);
const mappedRoomId = (await getOrCreateVirtualRoomForRoom(roomId)) || roomId;
const mappedRoomId = (await VoipUserMapper.sharedInstance().getOrCreateVirtualRoomForRoom(roomId)) || roomId;
logger.debug("Mapped real room " + roomId + " to room ID " + mappedRoomId);
const call = createNewMatrixCall(MatrixClientPeg.get(), mappedRoomId);