diff --git a/spec/unit/webrtc/call.spec.ts b/spec/unit/webrtc/call.spec.ts index 0e7b67547..e5ef6de87 100644 --- a/spec/unit/webrtc/call.spec.ts +++ b/spec/unit/webrtc/call.spec.ts @@ -15,7 +15,7 @@ limitations under the License. */ import {TestClient} from '../../TestClient'; -import {MatrixCall, CallErrorCode} from '../../../src/webrtc/call'; +import {MatrixCall, CallErrorCode, CallEvent} from '../../../src/webrtc/call'; const DUMMY_SDP = ( "v=0\r\n" + @@ -254,4 +254,48 @@ describe('Call', function() { sdpMid: '', }); }); + + it('should map asserted identity messages to remoteAssertedIdentity', async function() { + const callPromise = call.placeVoiceCall(); + await client.httpBackend.flush(); + await callPromise; + await call.onAnswerReceived({ + getContent: () => { + return { + version: 1, + call_id: call.callId, + party_id: 'party_id', + answer: { + sdp: DUMMY_SDP, + }, + }; + }, + }); + + const identChangedCallback = jest.fn(); + call.on(CallEvent.AssertedIdentityChanged, identChangedCallback); + + await call.onAssertedIdentityReceived({ + getContent: () => { + return { + version: 1, + call_id: call.callId, + party_id: 'party_id', + asserted_identity: { + id: "@steve:example.com", + display_name: "Steve Gibbons", + }, + }; + }, + }); + + expect(identChangedCallback).toHaveBeenCalled(); + + const ident = call.getRemoteAssertedIdentity(); + expect(ident.id).toEqual("@steve:example.com"); + expect(ident.displayName).toEqual("Steve Gibbons"); + + // Hangup to stop timers + call.hangup(CallErrorCode.UserHangup, true); + }); }); diff --git a/src/@types/event.ts b/src/@types/event.ts index 92f90ea62..a908088d7 100644 --- a/src/@types/event.ts +++ b/src/@types/event.ts @@ -53,6 +53,8 @@ export enum EventType { CallSelectAnswer = "m.call.select_answer", CallNegotiate = "m.call.negotiate", CallReplaces = "m.call.replaces", + CallAssertedIdentity = "m.call.asserted_identity", + CallAssertedIdentityPrefix = "org.matrix.call.asserted_identity", KeyVerificationRequest = "m.key.verification.request", KeyVerificationStart = "m.key.verification.start", KeyVerificationCancel = "m.key.verification.cancel", diff --git a/src/client.js b/src/client.js index a57f84539..63514c2e5 100644 --- a/src/client.js +++ b/src/client.js @@ -729,6 +729,17 @@ MatrixClient.prototype.setSupportsCallTransfer = function(supportsCallTransfer) this._supportsCallTransfer = supportsCallTransfer; }; +/** + * Creates a new call. + * The place*Call methods on the returned call can be used to actually place a call + * + * @param {string} roomId The room the call is to be placed in. + * @return {MatrixCall} the call or null if the browser doesn't support calling. + */ +MatrixClient.prototype.createCall = function(roomId) { + return createNewMatrixCall(this, roomId); +}; + /** * Get the current sync state. * @return {?string} the sync state, which may be null. diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index 53680702a..233c6e1fa 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -62,6 +62,11 @@ interface TurnServer { ttl?: number, } +interface AssertedIdentity { + id: string, + displayName: string, +} + export enum CallState { Fledgling = 'fledgling', InviteSent = 'invite_sent', @@ -101,6 +106,8 @@ export enum CallEvent { RemoteHoldUnhold = 'remote_hold_unhold', // backwards compat alias for LocalHoldUnhold: remove in a major version bump HoldUnhold = 'hold_unhold', + + AssertedIdentityChanged = 'asserted_identity_changed', } export enum CallErrorCode { @@ -292,6 +299,8 @@ export class MatrixCall extends EventEmitter { // the call) we buffer them up here so we can then add the ones from the party we pick private remoteCandidateBuffer = new Map(); + private remoteAssertedIdentity: AssertedIdentity; + constructor(opts: CallOpts) { super(); this.roomId = opts.roomId; @@ -419,6 +428,10 @@ export class MatrixCall extends EventEmitter { return Boolean(this.opponentCaps && this.opponentCaps["m.call.transferee"]); } + public getRemoteAssertedIdentity(): AssertedIdentity { + return this.remoteAssertedIdentity; + } + /** * Retrieve the local <video> DOM element. * @return {Element} The dom element @@ -1199,6 +1212,16 @@ export class MatrixCall extends EventEmitter { } } + async onAssertedIdentityReceived(event: MatrixEvent) { + if (!event.getContent().asserted_identity) return; + + this.remoteAssertedIdentity = { + id: event.getContent().asserted_identity.id, + displayName: event.getContent().asserted_identity.display_name, + }; + this.emit(CallEvent.AssertedIdentityChanged); + } + private callHasEnded(): boolean { // This exists as workaround to typescript trying to be clever and erroring // when putting if (this.state === CallState.Ended) return; twice in the same @@ -1913,6 +1936,9 @@ export function setAudioInput(deviceId: string) { audioInput = deviceId; } export function setVideoInput(deviceId: string) { videoInput = deviceId; } /** + * DEPRECATED + * Use client.createCall() + * * Create a new Matrix call for the browser. * @param {MatrixClient} client The client instance to use. * @param {string} roomId The room the call is in. diff --git a/src/webrtc/callEventHandler.ts b/src/webrtc/callEventHandler.ts index c45bf62b3..9c4c38b26 100644 --- a/src/webrtc/callEventHandler.ts +++ b/src/webrtc/callEventHandler.ts @@ -87,7 +87,11 @@ export class CallEventHandler { private onEvent = (event: MatrixEvent) => { // any call events or ones that might be once they're decrypted - if (event.getType().indexOf("m.call.") === 0 || event.isBeingDecrypted()) { + if ( + event.getType().indexOf("m.call.") === 0 || + event.getType().indexOf("org.matrix.call.") === 0 + || event.isBeingDecrypted() + ) { // queue up for processing once all events from this sync have been // processed (see above). this.callEventBuffer.push(event); @@ -271,6 +275,18 @@ export class CallEventHandler { } call.onNegotiateReceived(event); + } else if ( + event.getType() === EventType.CallAssertedIdentity || + event.getType() === EventType.CallAssertedIdentityPrefix + ) { + if (!call) return; + + if (event.getContent().party_id === call.ourPartyId) { + // Ignore remote echo (not that we send asserted identity, but still...) + return; + } + + call.onAssertedIdentityReceived(event); } } }