Merge pull request #6136 from matrix-org/dbkr/map_phone_number_lookup_to_native
Map phone number lookup results to their native roomspull/21833/head
						commit
						7421efe8f9
					
				|  | @ -264,7 +264,7 @@ export default class CallHandler extends EventEmitter { | |||
|     } | ||||
| 
 | ||||
|     public getSupportsVirtualRooms() { | ||||
|         return this.supportsPstnProtocol; | ||||
|         return this.supportsSipNativeVirtual; | ||||
|     } | ||||
| 
 | ||||
|     public pstnLookup(phoneNumber: string): Promise<ThirdpartyLookupResponse[]> { | ||||
|  | @ -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}`); | ||||
| 
 | ||||
|  | @ -862,9 +864,43 @@ 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) { | ||||
|         logger.info("Setting call in room " + activeCallRoomId + " active"); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|     } | ||||
| 
 | ||||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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; | ||||
|  | @ -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); | ||||
|     } | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  | @ -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", | ||||
|  | @ -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", | ||||
|  |  | |||
|  | @ -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'; | ||||
|  | @ -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; | ||||
|  | @ -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) { | ||||
|  | @ -134,38 +163,34 @@ describe('CallHandler', () => { | |||
|         SdkConfig.unset(); | ||||
|     }); | ||||
| 
 | ||||
|     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, | ||||
|             }, | ||||
|         }]); | ||||
| 
 | ||||
|         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 () => { | ||||
|         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; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         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); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 David Baker
						David Baker