From 411fc47f28af494ba9e5f0488b1976daf84ad7eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 28 Aug 2021 09:27:47 +0200 Subject: [PATCH] Improve TypeScript in MatrixCall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/webrtc/call.ts | 254 ++++++++++++++++----------------- src/webrtc/callEventHandler.ts | 7 +- 2 files changed, 129 insertions(+), 132 deletions(-) diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index cfc19b2fe..4b6c7f013 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -31,14 +31,21 @@ import { randomString } from '../randomstring'; import { MCallReplacesEvent, MCallAnswer, - MCallOfferNegotiate, + MCallInviteNegotiate, CallCapabilities, SDPStreamMetadataPurpose, SDPStreamMetadata, SDPStreamMetadataKey, MCallSDPStreamMetadataChanged, + MCallSelectAnswer, + MCAllAssertedIdentity, + MCallCandidates, + MCallBase, + MCallHangupReject, } from './callEventTypes'; import { CallFeed } from './callFeed'; +import { MatrixClient } from "../client"; +import { ISendEventResponse } from "../@types/requests"; // events: hangup, error(err), replaced(call), state(state, oldState) @@ -261,30 +268,33 @@ function genCallID(): string { * @param {MatrixClient} opts.client The Matrix Client instance to send events to. */ export class MatrixCall extends EventEmitter { - roomId: string; - type: CallType; - callId: string; - state: CallState; - hangupParty: CallParty; - hangupReason: string; - direction: CallDirection; - ourPartyId: string; + public roomId: string; + public type: CallType = null; + public callId: string; + public state = CallState.Fledgling; + public hangupParty: CallParty; + public hangupReason: string; + public direction: CallDirection; + public ourPartyId: string; - private client: any; // Fix when client is TSified + private client: MatrixClient; private forceTURN: boolean; private turnServers: Array; - private candidateSendQueue: Array; - private candidateSendTries: number; - private sentEndOfCandidates: boolean; + // A queue for candidates waiting to go out. + // We try to amalgamate candidates into a single candidate message where + // possible + private candidateSendQueue: Array = []; + private candidateSendTries = 0; + private sentEndOfCandidates = false; private peerConn: RTCPeerConnection; - private feeds: Array; - private usermediaSenders: Array; - private screensharingSenders: Array; - private inviteOrAnswerSent: boolean; + private feeds: Array = []; + private usermediaSenders: Array = []; + private screensharingSenders: Array = []; + private inviteOrAnswerSent = false; private waitForLocalAVStream: boolean; private successor: MatrixCall; private opponentMember: RoomMember; - private opponentVersion: number; + private opponentVersion: number | string; // The party ID of the other side: undefined if we haven't chosen a partner // yet, null if we have but they didn't send a party ID. private opponentPartyId: string; @@ -293,7 +303,7 @@ export class MatrixCall extends EventEmitter { // The logic of when & if a call is on hold is nontrivial and explained in is*OnHold // This flag represents whether we want the other party to be on hold - private remoteOnHold; + private remoteOnHold = false; // the stats for the call at the point it ended. We can't get these after we // tear the call down, so we just grab a snapshot before we stop the call. @@ -301,7 +311,7 @@ export class MatrixCall extends EventEmitter { private callStatsAtEnd: any[]; // Perfect negotiation state: https://www.w3.org/TR/webrtc/#perfect-negotiation-example - private makingOffer: boolean; + private makingOffer = false; private ignoreOffer: boolean; // If candidates arrive before we've picked an opponent (which, in particular, @@ -317,7 +327,6 @@ export class MatrixCall extends EventEmitter { super(); this.roomId = opts.roomId; this.client = opts.client; - this.type = null; this.forceTURN = opts.forceTURN; this.ourPartyId = this.client.deviceId; // Array of Objects with urls, username, credential keys @@ -330,33 +339,14 @@ export class MatrixCall extends EventEmitter { for (const server of this.turnServers) { utils.checkObjectHasKeys(server, ["urls"]); } - this.callId = genCallID(); - this.state = CallState.Fledgling; - - // A queue for candidates waiting to go out. - // We try to amalgamate candidates into a single candidate message where - // possible - this.candidateSendQueue = []; - this.candidateSendTries = 0; - - this.sentEndOfCandidates = false; - this.inviteOrAnswerSent = false; - this.makingOffer = false; - - this.remoteOnHold = false; - - this.feeds = []; - - this.usermediaSenders = []; - this.screensharingSenders = []; } /** * Place a voice call to this room. * @throws If you have not specified a listener for 'error' events. */ - async placeVoiceCall() { + async placeVoiceCall(): Promise { logger.debug("placeVoiceCall"); this.checkForErrorListener(); const constraints = getUserMediaContraints(ConstraintsType.Audio); @@ -368,7 +358,7 @@ export class MatrixCall extends EventEmitter { * Place a video call to this room. * @throws If you have not specified a listener for 'error' events. */ - async placeVideoCall() { + async placeVideoCall(): Promise { logger.debug("placeVideoCall"); this.checkForErrorListener(); const constraints = getUserMediaContraints(ConstraintsType.Video); @@ -376,11 +366,11 @@ export class MatrixCall extends EventEmitter { await this.placeCallWithConstraints(constraints); } - public getOpponentMember() { + public getOpponentMember(): RoomMember { return this.opponentMember; } - public opponentCanBeTransferred() { + public opponentCanBeTransferred(): boolean { return Boolean(this.opponentCaps && this.opponentCaps["m.call.transferee"]); } @@ -462,7 +452,7 @@ export class MatrixCall extends EventEmitter { return !this.feeds.some((feed) => !feed.isLocal()); } - private pushRemoteFeed(stream: MediaStream) { + private pushRemoteFeed(stream: MediaStream): void { // Fallback to old behavior if the other side doesn't support SDPStreamMetadata if (!this.opponentSupportsSDPStreamMetadata()) { this.pushRemoteFeedWithoutMetadata(stream); @@ -495,7 +485,7 @@ export class MatrixCall extends EventEmitter { /** * This method is used ONLY if the other client doesn't support sending SDPStreamMetadata */ - private pushRemoteFeedWithoutMetadata(stream: MediaStream) { + private pushRemoteFeedWithoutMetadata(stream: MediaStream): void { const userId = this.getOpponentMember().userId; // We can guess the purpose here since the other client can only send one stream const purpose = SDPStreamMetadataPurpose.Usermedia; @@ -523,7 +513,7 @@ export class MatrixCall extends EventEmitter { logger.info(`Pushed remote stream (id="${stream.id}", active="${stream.active}")`); } - private pushLocalFeed(stream: MediaStream, purpose: SDPStreamMetadataPurpose, addToPeerConnection = true) { + private pushLocalFeed(stream: MediaStream, purpose: SDPStreamMetadataPurpose, addToPeerConnection = true): void { const userId = this.client.getUserId(); // We try to replace an existing feed if there already is one with the same purpose @@ -562,12 +552,12 @@ export class MatrixCall extends EventEmitter { logger.info(`Pushed local stream (id="${stream.id}", active="${stream.active}", purpose="${purpose}")`); } - private deleteAllFeeds() { + private deleteAllFeeds(): void { this.feeds = []; this.emit(CallEvent.FeedsChanged, this.feeds); } - private deleteFeedByStream(stream: MediaStream) { + private deleteFeedByStream(stream: MediaStream): void { logger.debug(`Removing feed with stream id ${stream.id}`); const feed = this.getFeedByStreamId(stream.id); @@ -607,8 +597,8 @@ export class MatrixCall extends EventEmitter { * Configure this call from an invite event. Used by MatrixClient. * @param {MatrixEvent} event The m.call.invite event */ - async initWithInvite(event: MatrixEvent) { - const invite = event.getContent(); + public async initWithInvite(event: MatrixEvent): Promise { + const invite = event.getContent(); this.direction = CallDirection.Inbound; // make sure we have valid turn creds. Unless something's gone wrong, it should @@ -674,7 +664,7 @@ export class MatrixCall extends EventEmitter { * Configure this call from a hangup or reject event. Used by MatrixClient. * @param {MatrixEvent} event The m.call.hangup event */ - initWithHangup(event: MatrixEvent) { + public initWithHangup(event: MatrixEvent): void { // perverse as it may seem, sometimes we want to instantiate a call with a // hangup message (because when getting the state of the room on load, events // come in reverse order and we want to remember that a call has been hung up) @@ -684,7 +674,7 @@ export class MatrixCall extends EventEmitter { /** * Answer a call. */ - async answer() { + public async answer(): Promise { if (this.inviteOrAnswerSent) { return; } @@ -719,7 +709,7 @@ export class MatrixCall extends EventEmitter { * MatrixClient. * @param {MatrixCall} newCall The new call. */ - replacedBy(newCall: MatrixCall) { + public replacedBy(newCall: MatrixCall): void { if (this.state === CallState.WaitLocalMedia) { logger.debug("Telling new call to wait for local media"); newCall.waitForLocalAVStream = true; @@ -737,7 +727,7 @@ export class MatrixCall extends EventEmitter { * @param {string} reason The reason why the call is being hung up. * @param {boolean} suppressEvent True to suppress emitting an event. */ - hangup(reason: CallErrorCode, suppressEvent: boolean) { + public hangup(reason: CallErrorCode, suppressEvent: boolean): void { if (this.callHasEnded()) return; logger.debug("Ending call " + this.callId); @@ -757,7 +747,7 @@ export class MatrixCall extends EventEmitter { * This used to be done by calling hangup, but is a separate method and protocol * event as of MSC2746. */ - reject() { + public reject(): void { if (this.state !== CallState.Ringing) { throw Error("Call must be in 'ringing' state to reject!"); } @@ -800,7 +790,7 @@ export class MatrixCall extends EventEmitter { public async setScreensharingEnabled( enabled: boolean, selectDesktopCapturerSource?: () => Promise, - ) { + ): Promise { // Skip if there is nothing to do if (enabled && this.isScreensharing()) { logger.warn(`There is already a screensharing stream - there is nothing to do!`); @@ -850,7 +840,7 @@ export class MatrixCall extends EventEmitter { private async setScreensharingEnabledWithoutMetadataSupport( enabled: boolean, selectDesktopCapturerSource?: () => Promise, - ) { + ): Promise { logger.debug(`Set screensharing enabled? ${enabled} using replaceTrack()`); if (enabled) { try { @@ -896,7 +886,7 @@ export class MatrixCall extends EventEmitter { * Set whether our outbound video should be muted or not. * @param {boolean} muted True to mute the outbound video. */ - setLocalVideoMuted(muted: boolean) { + public setLocalVideoMuted(muted: boolean): void { this.localUsermediaFeed?.setVideoMuted(muted); this.updateMuteStatus(); } @@ -910,7 +900,7 @@ export class MatrixCall extends EventEmitter { * @return {Boolean} True if the local preview video is muted, else false * (including if the call is not set up yet). */ - isLocalVideoMuted(): boolean { + public isLocalVideoMuted(): boolean { return this.localUsermediaFeed?.isVideoMuted(); } @@ -918,7 +908,7 @@ export class MatrixCall extends EventEmitter { * Set whether the microphone should be muted or not. * @param {boolean} muted True to mute the mic. */ - setMicrophoneMuted(muted: boolean) { + public setMicrophoneMuted(muted: boolean): void { this.localUsermediaFeed?.setAudioMuted(muted); this.updateMuteStatus(); } @@ -932,7 +922,7 @@ export class MatrixCall extends EventEmitter { * @return {Boolean} True if the mic is muted, else false (including if the call * is not set up yet). */ - isMicrophoneMuted(): boolean { + public isMicrophoneMuted(): boolean { return this.localUsermediaFeed?.isAudioMuted(); } @@ -940,11 +930,11 @@ export class MatrixCall extends EventEmitter { * @returns true if we have put the party on the other side of the call on hold * (that is, we are signalling to them that we are not listening) */ - isRemoteOnHold(): boolean { + public isRemoteOnHold(): boolean { return this.remoteOnHold; } - setRemoteOnHold(onHold: boolean) { + public setRemoteOnHold(onHold: boolean): void { if (this.isRemoteOnHold() === onHold) return; this.remoteOnHold = onHold; @@ -964,7 +954,7 @@ export class MatrixCall extends EventEmitter { * they cannot hear us). * @returns true if the other party has put us on hold */ - isLocalOnHold(): boolean { + public isLocalOnHold(): boolean { if (this.state !== CallState.Connected) return false; let callOnHold = true; @@ -984,7 +974,7 @@ export class MatrixCall extends EventEmitter { * Sends a DTMF digit to the other party * @param digit The digit (nb. string - '#' and '*' are dtmf too) */ - sendDtmfDigit(digit: string) { + public sendDtmfDigit(digit: string): void { for (const sender of this.peerConn.getSenders()) { if (sender.track.kind === 'audio' && sender.dtmf) { sender.dtmf.insertDTMF(digit); @@ -995,7 +985,7 @@ export class MatrixCall extends EventEmitter { throw new Error("Unable to find a track to send DTMF on"); } - private updateMuteStatus() { + private updateMuteStatus(): void { this.sendVoipEvent(EventType.CallSDPStreamMetadataChangedPrefix, { [SDPStreamMetadataKey]: this.getLocalSDPStreamMetadata(), }); @@ -1011,7 +1001,7 @@ export class MatrixCall extends EventEmitter { * Internal * @param {Object} stream */ - private gotUserMediaForInvite = async (stream: MediaStream) => { + private gotUserMediaForInvite = async (stream: MediaStream): Promise => { if (this.successor) { this.successor.gotUserMediaForAnswer(stream); return; @@ -1028,7 +1018,7 @@ export class MatrixCall extends EventEmitter { // Now we wait for the negotiationneeded event }; - private async sendAnswer() { + private async sendAnswer(): Promise { const answerContent = { answer: { sdp: this.peerConn.localDescription.sdp, @@ -1075,7 +1065,7 @@ export class MatrixCall extends EventEmitter { this.sendCandidateQueue(); } - private gotUserMediaForAnswer = async (stream: MediaStream) => { + private gotUserMediaForAnswer = async (stream: MediaStream): Promise => { if (this.callHasEnded()) { return; } @@ -1114,7 +1104,7 @@ export class MatrixCall extends EventEmitter { * Internal * @param {Object} event */ - private gotLocalIceCandidate = (event: RTCPeerConnectionIceEvent) => { + private gotLocalIceCandidate = (event: RTCPeerConnectionIceEvent): Promise => { if (event.candidate) { logger.debug( "Call " + this.callId + " got local ICE " + event.candidate.sdpMid + " candidate: " + @@ -1133,7 +1123,7 @@ export class MatrixCall extends EventEmitter { } }; - private onIceGatheringStateChange = (event: Event) => { + private onIceGatheringStateChange = (event: Event): void => { logger.debug("ice gathering state changed to " + this.peerConn.iceGatheringState); if (this.peerConn.iceGatheringState === 'complete' && !this.sentEndOfCandidates) { // If we didn't get an empty-string candidate to signal the end of candidates, @@ -1151,19 +1141,20 @@ export class MatrixCall extends EventEmitter { } }; - async onRemoteIceCandidatesReceived(ev: MatrixEvent) { + public async onRemoteIceCandidatesReceived(ev: MatrixEvent): Promise { if (this.callHasEnded()) { //debuglog("Ignoring remote ICE candidate because call has ended"); return; } - const candidates = ev.getContent().candidates; + const content = ev.getContent(); + const candidates = content.candidates; if (!candidates) { logger.info("Ignoring candidates event with no candidates!"); return; } - const fromPartyId = ev.getContent().version === 0 ? null : ev.getContent().party_id || null; + const fromPartyId = content.version === 0 ? null : content.party_id || null; if (this.opponentPartyId === undefined) { // we haven't picked an opponent yet so save the candidates @@ -1174,9 +1165,9 @@ export class MatrixCall extends EventEmitter { return; } - if (!this.partyIdMatches(ev.getContent())) { + if (!this.partyIdMatches(content)) { logger.info( - `Ignoring candidates from party ID ${ev.getContent().party_id}: ` + + `Ignoring candidates from party ID ${content.party_id}: ` + `we have chosen party ID ${this.opponentPartyId}`, ); @@ -1190,8 +1181,9 @@ export class MatrixCall extends EventEmitter { * Used by MatrixClient. * @param {Object} msg */ - async onAnswerReceived(event: MatrixEvent) { - logger.debug(`Got answer for call ID ${this.callId} from party ID ${event.getContent().party_id}`); + public async onAnswerReceived(event: MatrixEvent): Promise { + const content = event.getContent(); + logger.debug(`Got answer for call ID ${this.callId} from party ID ${content.party_id}`); if (this.callHasEnded()) { logger.debug(`Ignoring answer because call ID ${this.callId} has ended`); @@ -1200,7 +1192,7 @@ export class MatrixCall extends EventEmitter { if (this.opponentPartyId !== undefined) { logger.info( - `Ignoring answer from party ID ${event.getContent().party_id}: ` + + `Ignoring answer from party ID ${content.party_id}: ` + `we already have an answer/reject from ${this.opponentPartyId}`, ); return; @@ -1211,7 +1203,7 @@ export class MatrixCall extends EventEmitter { this.setState(CallState.Connecting); - const sdpStreamMetadata = event.getContent()[SDPStreamMetadataKey]; + const sdpStreamMetadata = content[SDPStreamMetadataKey]; if (sdpStreamMetadata) { this.updateRemoteSDPStreamMetadata(sdpStreamMetadata); } else { @@ -1219,7 +1211,7 @@ export class MatrixCall extends EventEmitter { } try { - await this.peerConn.setRemoteDescription(event.getContent().answer); + await this.peerConn.setRemoteDescription(content.answer); } catch (e) { logger.debug("Failed to set remote description", e); this.terminate(CallParty.Local, CallErrorCode.SetRemoteDescription, false); @@ -1242,13 +1234,13 @@ export class MatrixCall extends EventEmitter { } } - async onSelectAnswerReceived(event: MatrixEvent) { + public async onSelectAnswerReceived(event: MatrixEvent): Promise { if (this.direction !== CallDirection.Inbound) { logger.warn("Got select_answer for an outbound call: ignoring"); return; } - const selectedPartyId = event.getContent().selected_party_id; + const selectedPartyId = event.getContent().selected_party_id; if (selectedPartyId === undefined || selectedPartyId === null) { logger.warn("Got nonsensical select_answer with null/undefined selected_party_id: ignoring"); @@ -1262,8 +1254,9 @@ export class MatrixCall extends EventEmitter { } } - async onNegotiateReceived(event: MatrixEvent) { - const description = event.getContent().description; + public async onNegotiateReceived(event: MatrixEvent): Promise { + const content = event.getContent(); + const description = content.description; if (!description || !description.sdp || !description.type) { logger.info("Ignoring invalid m.call.negotiate event"); return; @@ -1288,7 +1281,7 @@ export class MatrixCall extends EventEmitter { const prevLocalOnHold = this.isLocalOnHold(); - const sdpStreamMetadata = event.getContent()[SDPStreamMetadataKey]; + const sdpStreamMetadata = content[SDPStreamMetadataKey]; if (sdpStreamMetadata) { this.updateRemoteSDPStreamMetadata(sdpStreamMetadata); } else { @@ -1336,12 +1329,13 @@ export class MatrixCall extends EventEmitter { this.updateRemoteSDPStreamMetadata(metadata); } - async onAssertedIdentityReceived(event: MatrixEvent) { - if (!event.getContent().asserted_identity) return; + public async onAssertedIdentityReceived(event: MatrixEvent): Promise { + const content = event.getContent(); + if (!content.asserted_identity) return; this.remoteAssertedIdentity = { - id: event.getContent().asserted_identity.id, - displayName: event.getContent().asserted_identity.display_name, + id: content.asserted_identity.id, + displayName: content.asserted_identity.display_name, }; this.emit(CallEvent.AssertedIdentityChanged); } @@ -1353,7 +1347,7 @@ export class MatrixCall extends EventEmitter { return this.state === CallState.Ended; } - private gotLocalOffer = async (description: RTCSessionDescriptionInit) => { + private gotLocalOffer = async (description: RTCSessionDescriptionInit): Promise => { logger.debug("Created offer: ", description); if (this.callHasEnded()) { @@ -1383,7 +1377,7 @@ export class MatrixCall extends EventEmitter { const content = { lifetime: CALL_TIMEOUT_MS, - } as MCallOfferNegotiate; + } as MCallInviteNegotiate; // clunky because TypeScript can't follow the types through if we use an expression as the key if (this.state === CallState.CreateOffer) { @@ -1442,7 +1436,7 @@ export class MatrixCall extends EventEmitter { } }; - private getLocalOfferFailed = (err: Error) => { + private getLocalOfferFailed = (err: Error): void => { logger.error("Failed to get local offer", err); this.emit( @@ -1455,7 +1449,7 @@ export class MatrixCall extends EventEmitter { this.terminate(CallParty.Local, CallErrorCode.LocalOfferFailed, false); }; - private getUserMediaFailed = (err: Error) => { + private getUserMediaFailed = (err: Error): void => { if (this.successor) { this.successor.getUserMediaFailed(err); return; @@ -1474,7 +1468,7 @@ export class MatrixCall extends EventEmitter { this.terminate(CallParty.Local, CallErrorCode.NoUserMedia, false); }; - onIceConnectionStateChanged = () => { + private onIceConnectionStateChanged = (): void => { if (this.callHasEnded()) { return; // because ICE can still complete as we're ending the call } @@ -1490,14 +1484,14 @@ export class MatrixCall extends EventEmitter { } }; - private onSignallingStateChanged = () => { + private onSignallingStateChanged = (): void => { logger.debug( "call " + this.callId + ": Signalling state changed to: " + this.peerConn.signalingState, ); }; - private onTrack = (ev: RTCTrackEvent) => { + private onTrack = (ev: RTCTrackEvent): void => { if (ev.streams.length === 0) { logger.warn(`Streamless ${ev.track.kind} found: ignoring.`); return; @@ -1521,7 +1515,7 @@ export class MatrixCall extends EventEmitter { * [96685:23:0518/162603.933377:ERROR:webrtc_video_engine.cc(1171)] GetChangedRecvParameters called without any video codecs. * [96685:23:0518/162603.933430:ERROR:sdp_offer_answer.cc(4302)] Failed to set local video description recv parameters for m-section with mid='2'. (INVALID_PARAMETER) */ - private getRidOfRTXCodecs() { + private getRidOfRTXCodecs(): void { // RTCRtpReceiver.getCapabilities and RTCRtpSender.getCapabilities don't seem to be supported on FF if (!RTCRtpReceiver.getCapabilities || !RTCRtpSender.getCapabilities) return; @@ -1549,7 +1543,7 @@ export class MatrixCall extends EventEmitter { } } - onNegotiationNeeded = async () => { + private onNegotiationNeeded = async (): Promise => { logger.info("Negotiation is needed!"); if (this.state !== CallState.CreateOffer && this.opponentVersion === 0) { @@ -1570,7 +1564,7 @@ export class MatrixCall extends EventEmitter { } }; - onHangupReceived = (msg) => { + public onHangupReceived = (msg: MCallHangupReject): void => { logger.debug("Hangup received for call ID " + this.callId); // party ID must match (our chosen partner hanging up the call) or be undefined (we haven't chosen @@ -1583,7 +1577,7 @@ export class MatrixCall extends EventEmitter { } }; - onRejectReceived = (msg) => { + public onRejectReceived = (msg: MCallHangupReject): void => { logger.debug("Reject received for call ID " + this.callId); // No need to check party_id for reject because if we'd received either @@ -1605,12 +1599,12 @@ export class MatrixCall extends EventEmitter { } }; - onAnsweredElsewhere = (msg) => { + public onAnsweredElsewhere = (msg: MCallAnswer): void => { logger.debug("Call ID " + this.callId + " answered elsewhere"); this.terminate(CallParty.Remote, CallErrorCode.AnsweredElsewhere, true); }; - setState(state: CallState) { + private setState(state: CallState): void { const oldState = this.state; this.state = state; this.emit(CallEvent.State, state, oldState); @@ -1622,7 +1616,7 @@ export class MatrixCall extends EventEmitter { * @param {Object} content * @return {Promise} */ - private sendVoipEvent(eventType: string, content: object) { + private sendVoipEvent(eventType: string, content: object): Promise { return this.client.sendEvent(this.roomId, eventType, Object.assign({}, content, { version: VOIP_PROTO_VERSION, call_id: this.callId, @@ -1630,7 +1624,7 @@ export class MatrixCall extends EventEmitter { })); } - queueCandidate(content: RTCIceCandidate) { + private queueCandidate(content: RTCIceCandidate): void { // We partially de-trickle candidates by waiting for `delay` before sending them // amalgamated, in order to avoid sending too many m.call.candidates events and hitting // rate limits in Matrix. @@ -1664,7 +1658,7 @@ export class MatrixCall extends EventEmitter { /* * Transfers this call to another user */ - async transfer(targetUserId: string) { + public async transfer(targetUserId: string): Promise { // Fetch the target user's global profile info: their room avatar / displayname // could be different in whatever room we share with them. const profileInfo = await this.client.getProfileInfo(targetUserId); @@ -1675,7 +1669,7 @@ export class MatrixCall extends EventEmitter { replacement_id: genCallID(), target_user: { id: targetUserId, - display_name: profileInfo.display_name, + display_name: profileInfo.displayname, avatar_url: profileInfo.avatar_url, }, create_call: replacementId, @@ -1690,7 +1684,7 @@ export class MatrixCall extends EventEmitter { * Transfers this call to the target call, effectively 'joining' the * two calls (so the remote parties on each call are connected together). */ - async transferToCall(transferTargetCall?: MatrixCall) { + public async transferToCall(transferTargetCall?: MatrixCall): Promise { const targetProfileInfo = await this.client.getProfileInfo(transferTargetCall.getOpponentMember().userId); const transfereeProfileInfo = await this.client.getProfileInfo(this.getOpponentMember().userId); @@ -1702,7 +1696,7 @@ export class MatrixCall extends EventEmitter { replacement_id: genCallID(), target_user: { id: this.getOpponentMember().userId, - display_name: transfereeProfileInfo.display_name, + display_name: transfereeProfileInfo.displayname, avatar_url: transfereeProfileInfo.avatar_url, }, await_call: newCallId, @@ -1714,7 +1708,7 @@ export class MatrixCall extends EventEmitter { replacement_id: genCallID(), target_user: { id: transferTargetCall.getOpponentMember().userId, - display_name: targetProfileInfo.display_name, + display_name: targetProfileInfo.displayname, avatar_url: targetProfileInfo.avatar_url, }, create_call: newCallId, @@ -1726,7 +1720,7 @@ export class MatrixCall extends EventEmitter { await transferTargetCall.terminate(CallParty.Local, CallErrorCode.Transfered, true); } - private async terminate(hangupParty: CallParty, hangupReason: CallErrorCode, shouldEmit: boolean) { + private async terminate(hangupParty: CallParty, hangupReason: CallErrorCode, shouldEmit: boolean): Promise { if (this.callHasEnded()) return; this.callStatsAtEnd = await this.collectCallStats(); @@ -1752,7 +1746,7 @@ export class MatrixCall extends EventEmitter { } } - private stopAllMedia() { + private stopAllMedia(): void { logger.debug(`stopAllMedia (stream=${this.localUsermediaStream})`); for (const feed of this.feeds) { @@ -1762,7 +1756,7 @@ export class MatrixCall extends EventEmitter { } } - private checkForErrorListener() { + private checkForErrorListener(): void { if (this.listeners("error").length === 0) { throw new Error( "You MUST attach an error listener using call.on('error', function() {})", @@ -1770,7 +1764,7 @@ export class MatrixCall extends EventEmitter { } } - private async sendCandidateQueue() { + private async sendCandidateQueue(): Promise { if (this.candidateSendQueue.length === 0) { return; } @@ -1819,7 +1813,7 @@ export class MatrixCall extends EventEmitter { } } - private async placeCallWithConstraints(constraints: MediaStreamConstraints) { + private async placeCallWithConstraints(constraints: MediaStreamConstraints): Promise { logger.log("Getting user media with constraints", constraints); // XXX Find a better way to do this this.client.callEventHandler.calls.set(this.callId, this); @@ -1864,7 +1858,7 @@ export class MatrixCall extends EventEmitter { return pc; } - private partyIdMatches(msg): boolean { + private partyIdMatches(msg: MCallBase): boolean { // They must either match or both be absent (in which case opponentPartyId will be null) // Also we ignore party IDs on the invite/offer if the version is 0, so we must do the same // here and use null if the version is 0 (woe betide any opponent sending messages in the @@ -1875,9 +1869,9 @@ export class MatrixCall extends EventEmitter { // Commits to an opponent for the call // ev: An invite or answer event - private chooseOpponent(ev: MatrixEvent) { + private chooseOpponent(ev: MatrixEvent): void { // I choo-choo-choose you - const msg = ev.getContent(); + const msg = ev.getContent(); logger.debug(`Choosing party ID ${msg.party_id} for call ID ${this.callId}`); @@ -1892,11 +1886,11 @@ export class MatrixCall extends EventEmitter { // party ID this.opponentPartyId = msg.party_id || null; } - this.opponentCaps = msg.capabilities || {}; + this.opponentCaps = msg.capabilities || {} as CallCapabilities; this.opponentMember = ev.sender; } - private async addBufferedIceCandidates() { + private async addBufferedIceCandidates(): Promise { const bufferedCandidates = this.remoteCandidateBuffer.get(this.opponentPartyId); if (bufferedCandidates) { logger.info(`Adding ${bufferedCandidates.length} buffered candidates for opponent ${this.opponentPartyId}`); @@ -1905,7 +1899,7 @@ export class MatrixCall extends EventEmitter { this.remoteCandidateBuffer = null; } - private async addIceCandidates(candidates: RTCIceCandidate[]) { + private async addIceCandidates(candidates: RTCIceCandidate[]): Promise { for (const candidate of candidates) { if ( (candidate.sdpMid === null || candidate.sdpMid === undefined) && @@ -1927,7 +1921,7 @@ export class MatrixCall extends EventEmitter { } } - public get hasPeerConnection() { + public get hasPeerConnection(): boolean { return Boolean(this.peerConn); } } @@ -1949,13 +1943,13 @@ async function getScreensharingStream( } } -function setTracksEnabled(tracks: Array, enabled: boolean) { +function setTracksEnabled(tracks: Array, enabled: boolean): void { for (let i = 0; i < tracks.length; i++) { tracks[i].enabled = enabled; } } -function getUserMediaContraints(type: ConstraintsType) { +function getUserMediaContraints(type: ConstraintsType): MediaStreamConstraints { const isWebkit = !!navigator.webkitGetUserMedia; switch (type) { @@ -1986,7 +1980,9 @@ function getUserMediaContraints(type: ConstraintsType) { } } -async function getScreenshareContraints(selectDesktopCapturerSource?: () => Promise) { +async function getScreenshareContraints( + selectDesktopCapturerSource?: () => Promise, +): Promise { if (window.electron?.getDesktopCapturerSources && selectDesktopCapturerSource) { // We have access to getDesktopCapturerSources() logger.debug("Electron getDesktopCapturerSources() is available..."); @@ -2020,14 +2016,14 @@ let videoInput: string; * @param {string=} deviceId the identifier for the device * undefined treated as unset */ -export function setAudioInput(deviceId: string) { audioInput = deviceId; } +export function setAudioInput(deviceId: string): void { audioInput = deviceId; } /** * Set a video input device to use for MatrixCalls * @function * @param {string=} deviceId the identifier for the device * undefined treated as unset */ -export function setVideoInput(deviceId: string) { videoInput = deviceId; } +export function setVideoInput(deviceId: string): void { videoInput = deviceId; } /** * DEPRECATED @@ -2042,7 +2038,7 @@ export function setVideoInput(deviceId: string) { videoInput = deviceId; } * since it's only possible to set this option on outbound calls. * @return {MatrixCall} the call or null if the browser doesn't support calling. */ -export function createNewMatrixCall(client: any, roomId: string, options?: CallOpts) { +export function createNewMatrixCall(client: any, roomId: string, options?: CallOpts): MatrixCall { // typeof prevents Node from erroring on an undefined reference if (typeof(window) === 'undefined' || typeof(document) === 'undefined') { // NB. We don't log here as apps try to create a call object as a test for diff --git a/src/webrtc/callEventHandler.ts b/src/webrtc/callEventHandler.ts index 0ce247263..7b7ca0f71 100644 --- a/src/webrtc/callEventHandler.ts +++ b/src/webrtc/callEventHandler.ts @@ -19,6 +19,7 @@ import { logger } from '../logger'; import { createNewMatrixCall, MatrixCall, CallErrorCode, CallState, CallDirection } from './call'; import { EventType } from '../@types/event'; import { MatrixClient } from '../client'; +import { MCallAnswer, MCallHangupReject } from "./callEventTypes"; // Don't ring unless we'd be ringing for at least 3 seconds: the user needs some // time to press the 'accept' button @@ -252,9 +253,9 @@ export class CallEventHandler { } else { if (call.state !== CallState.Ended) { if (type === EventType.CallHangup) { - call.onHangupReceived(content); + call.onHangupReceived(content as MCallHangupReject); } else { - call.onRejectReceived(content); + call.onRejectReceived(content as MCallHangupReject); } this.calls.delete(content.call_id); } @@ -274,7 +275,7 @@ export class CallEventHandler { case EventType.CallAnswer: if (weSentTheEvent) { if (call.state === CallState.Ringing) { - call.onAnsweredElsewhere(content); + call.onAnsweredElsewhere(content as MCallAnswer); } } else { call.onAnswerReceived(event);