Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Map phone number lookup results to their native rooms #6136

Merged
merged 4 commits into from
Jun 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions src/CallHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ export default class CallHandler extends EventEmitter {
}

public getSupportsVirtualRooms() {
return this.supportsPstnProtocol;
return this.supportsSipNativeVirtual;
}

public pstnLookup(phoneNumber: string): Promise<ThirdpartyLookupResponse[]> {
Expand Down Expand Up @@ -521,7 +521,9 @@ export default class CallHandler extends EventEmitter {
let newNativeAssertedIdentity = newAssertedIdentity;
if (newAssertedIdentity) {
const response = await this.sipNativeLookup(newAssertedIdentity);
if (response.length) newNativeAssertedIdentity = response[0].userid;
if (response.length && response[0].fields.lookup_success) {
newNativeAssertedIdentity = response[0].userid;
}
}
console.log(`Asserted identity ${newAssertedIdentity} mapped to ${newNativeAssertedIdentity}`);

Expand Down Expand Up @@ -862,7 +864,41 @@ export default class CallHandler extends EventEmitter {
});
break;
}
case Action.DialNumber:
this.dialNumber(payload.number);
break;
}
}

private async dialNumber(number: string) {
const results = await this.pstnLookup(number);
if (!results || results.length === 0 || !results[0].userid) {
Modal.createTrackedDialog('', '', ErrorDialog, {
title: _t("Unable to look up phone number"),
description: _t("There was an error looking up the phone number"),
});
return;
}
const userId = results[0].userid;

// Now check to see if this is a virtual user, in which case we should find the
// native user
let nativeUserId;
if (this.getSupportsVirtualRooms()) {
const nativeLookupResults = await this.sipNativeLookup(userId);
const lookupSuccess = nativeLookupResults.length > 0 && nativeLookupResults[0].fields.lookup_success;
nativeUserId = lookupSuccess ? nativeLookupResults[0].userid : userId;
console.log("Looked up " + number + " to " + userId + " and mapped to native user " + nativeUserId);
} else {
nativeUserId = userId;
}

const roomId = await ensureDMExists(MatrixClientPeg.get(), nativeUserId);

dis.dispatch({
action: 'view_room',
room_id: roomId,
});
}

setActiveCallRoomId(activeCallRoomId: string) {
Expand Down
6 changes: 3 additions & 3 deletions src/VoipUserMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default class VoipUserMapper {

private async userToVirtualUser(userId: string): Promise<string> {
const results = await CallHandler.sharedInstance().sipVirtualLookup(userId);
if (results.length === 0) return null;
if (results.length === 0 || !results[0].fields.lookup_success) return null;
return results[0].userid;
}

Expand Down Expand Up @@ -82,14 +82,14 @@ export default class VoipUserMapper {
return Boolean(claimedNativeRoomId);
}

public async onNewInvitedRoom(invitedRoom: Room) {
public async onNewInvitedRoom(invitedRoom: Room): Promise<void> {
if (!CallHandler.sharedInstance().getSupportsVirtualRooms()) return;

const inviterId = invitedRoom.getDMInviter();
console.log(`Checking virtual-ness of room ID ${invitedRoom.roomId}, invited by ${inviterId}`);
const result = await CallHandler.sharedInstance().sipNativeLookup(inviterId);
if (result.length === 0) {
return true;
return;
}

if (result[0].fields.is_virtual) {
Expand Down
27 changes: 7 additions & 20 deletions src/components/views/voip/DialPadModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,14 @@ limitations under the License.
*/

import * as React from "react";
import { ensureDMExists } from "../../../createRoom";
import { _t } from "../../../languageHandler";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import AccessibleButton from "../elements/AccessibleButton";
import Field from "../elements/Field";
import DialPad from './DialPad';
import dis from '../../../dispatcher/dispatcher';
import Modal from "../../../Modal";
import ErrorDialog from "../../views/dialogs/ErrorDialog";
import CallHandler from "../../../CallHandler";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { DialNumberPayload } from "../../../dispatcher/payloads/DialNumberPayload";
import { Action } from "../../../dispatcher/actions";

interface IProps {
onFinished: (boolean) => void;
Expand Down Expand Up @@ -67,21 +64,11 @@ export default class DialpadModal extends React.PureComponent<IProps, IState> {
}

onDialPress = async () => {
const results = await CallHandler.sharedInstance().pstnLookup(this.state.value);
if (!results || results.length === 0 || !results[0].userid) {
Modal.createTrackedDialog('', '', ErrorDialog, {
title: _t("Unable to look up phone number"),
description: _t("There was an error looking up the phone number"),
});
}
const userId = results[0].userid;

const roomId = await ensureDMExists(MatrixClientPeg.get(), userId);

dis.dispatch({
action: 'view_room',
room_id: roomId,
});
const payload: DialNumberPayload = {
action: Action.DialNumber,
number: this.state.value,
};
dis.dispatch(payload);

this.props.onFinished(true);
}
Expand Down
6 changes: 6 additions & 0 deletions src/dispatcher/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ export enum Action {
*/
OpenDialPad = "open_dial_pad",

/**
* Dial the phone number in the payload
* payload: DialNumberPayload
*/
DialNumber = "dial_number",

/**
* Fired when CallHandler has checked for PSTN protocol support
* payload: none
Expand Down
23 changes: 23 additions & 0 deletions src/dispatcher/payloads/DialNumberPayload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { ActionPayload } from "../payloads";
import { Action } from "../actions";

export interface DialNumberPayload extends ActionPayload {
action: Action.DialNumber;
number: string;
}
4 changes: 2 additions & 2 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
"Already in call": "Already in call",
"You're already in a call with this person.": "You're already in a call with this person.",
"You cannot place a call with yourself.": "You cannot place a call with yourself.",
"Unable to look up phone number": "Unable to look up phone number",
"There was an error looking up the phone number": "There was an error looking up the phone number",
"Call in Progress": "Call in Progress",
"A call is currently being placed!": "A call is currently being placed!",
"Permission Required": "Permission Required",
Expand Down Expand Up @@ -898,8 +900,6 @@
"Fill Screen": "Fill Screen",
"Return to call": "Return to call",
"%(name)s on hold": "%(name)s on hold",
"Unable to look up phone number": "Unable to look up phone number",
"There was an error looking up the phone number": "There was an error looking up the phone number",
"Dial pad": "Dial pad",
"Unknown caller": "Unknown caller",
"Incoming voice call": "Incoming voice call",
Expand Down
73 changes: 49 additions & 24 deletions test/CallHandler-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import dis from '../src/dispatcher/dispatcher';
import { CallEvent, CallState } from 'matrix-js-sdk/src/webrtc/call';
import DMRoomMap from '../src/utils/DMRoomMap';
import EventEmitter from 'events';
import { Action } from '../src/dispatcher/actions';
import SdkConfig from '../src/SdkConfig';
import { ActionPayload } from '../src/dispatcher/payloads';
import { Actions } from '../src/notifications/types';
import { Action } from '../src/dispatcher/actions';

const REAL_ROOM_ID = '$room1:example.org';
const MAPPED_ROOM_ID = '$room2:example.org';
Expand Down Expand Up @@ -75,6 +77,18 @@ class FakeCall extends EventEmitter {
}
}

function untilDispatch(waitForAction: string): Promise<ActionPayload> {
let dispatchHandle;
return new Promise<ActionPayload>(resolve => {
dispatchHandle = dis.register(payload => {
if (payload.action === waitForAction) {
dis.unregister(dispatchHandle);
resolve(payload);
}
});
});
}

describe('CallHandler', () => {
let dmRoomMap;
let callHandler;
Expand All @@ -94,6 +108,21 @@ describe('CallHandler', () => {
callHandler = new CallHandler();
callHandler.start();

const realRoom = mkStubDM(REAL_ROOM_ID, '@user1:example.org');
const mappedRoom = mkStubDM(MAPPED_ROOM_ID, '@user2:example.org');
const mappedRoom2 = mkStubDM(MAPPED_ROOM_ID_2, '@user3:example.org');

MatrixClientPeg.get().getRoom = roomId => {
switch (roomId) {
case REAL_ROOM_ID:
return realRoom;
case MAPPED_ROOM_ID:
return mappedRoom;
case MAPPED_ROOM_ID_2:
return mappedRoom2;
}
};

dmRoomMap = {
getUserIdForRoomId: roomId => {
if (roomId === REAL_ROOM_ID) {
Expand Down Expand Up @@ -134,38 +163,34 @@ describe('CallHandler', () => {
SdkConfig.unset();
});

it('should move calls between rooms when remote asserted identity changes', async () => {
const realRoom = mkStubDM(REAL_ROOM_ID, '@user1:example.org');
const mappedRoom = mkStubDM(MAPPED_ROOM_ID, '@user2:example.org');
const mappedRoom2 = mkStubDM(MAPPED_ROOM_ID_2, '@user3:example.org');
it('should look up the correct user and open the room when a phone number is dialled', async () => {
MatrixClientPeg.get().getThirdpartyUser = jest.fn().mockResolvedValue([{
userid: '@user2:example.org',
protocol: "im.vector.protocol.sip_native",
fields: {
is_native: true,
lookup_success: true,
},
}]);

MatrixClientPeg.get().getRoom = roomId => {
switch (roomId) {
case REAL_ROOM_ID:
return realRoom;
case MAPPED_ROOM_ID:
return mappedRoom;
case MAPPED_ROOM_ID_2:
return mappedRoom2;
}
};
dis.dispatch({
action: Action.DialNumber,
number: '01818118181',
}, true);

const viewRoomPayload = await untilDispatch('view_room');
expect(viewRoomPayload.room_id).toEqual(MAPPED_ROOM_ID);
});

it('should move calls between rooms when remote asserted identity changes', async () => {
dis.dispatch({
action: 'place_call',
type: PlaceCallType.Voice,
room_id: REAL_ROOM_ID,
}, true);

let dispatchHandle;
// wait for the call to be set up
await new Promise<void>(resolve => {
dispatchHandle = dis.register(payload => {
if (payload.action === 'call_state') {
resolve();
}
});
});
dis.unregister(dispatchHandle);
await untilDispatch('call_state');

// should start off in the actual room ID it's in at the protocol level
expect(callHandler.getCallForRoom(REAL_ROOM_ID)).toBe(fakeCall);
Expand Down