You've already forked matrix-react-sdk
mirror of
https://github.com/matrix-org/matrix-react-sdk.git
synced 2025-07-28 15:22:05 +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:
@ -17,63 +17,96 @@ limitations under the License.
|
||||
import { ensureDMExists, findDMForUser } from './createRoom';
|
||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
||||
import DMRoomMap from "./utils/DMRoomMap";
|
||||
import SdkConfig from "./SdkConfig";
|
||||
import CallHandler, { VIRTUAL_ROOM_EVENT_TYPE } from './CallHandler';
|
||||
import RoomListStore from './stores/room-list/RoomListStore';
|
||||
import { Room } from 'matrix-js-sdk/src/models/room';
|
||||
|
||||
// Functions for mapping users & rooms for the voip_mxid_translate_pattern
|
||||
// config option
|
||||
// Functions for mapping virtual users & rooms. Currently the only lookup
|
||||
// is sip virtual: there could be others in the future.
|
||||
|
||||
export function voipUserMapperEnabled(): boolean {
|
||||
return SdkConfig.get()['voip_mxid_translate_pattern'] !== undefined;
|
||||
}
|
||||
export default class VoipUserMapper {
|
||||
private virtualRoomIdCache = new Set<string>();
|
||||
|
||||
// only exported for tests
|
||||
export function userToVirtualUser(userId: string, templateString?: string): string {
|
||||
if (templateString === undefined) templateString = SdkConfig.get()['voip_mxid_translate_pattern'];
|
||||
if (!templateString) return null;
|
||||
return templateString.replace('${mxid}', encodeURIComponent(userId).replace(/%/g, '=').toLowerCase());
|
||||
}
|
||||
public static sharedInstance(): VoipUserMapper {
|
||||
if (window.mxVoipUserMapper === undefined) window.mxVoipUserMapper = new VoipUserMapper();
|
||||
return window.mxVoipUserMapper;
|
||||
}
|
||||
|
||||
// only exported for tests
|
||||
export function virtualUserToUser(userId: string, templateString?: string): string {
|
||||
if (templateString === undefined) templateString = SdkConfig.get()['voip_mxid_translate_pattern'];
|
||||
if (!templateString) return null;
|
||||
private async userToVirtualUser(userId: string): Promise<string> {
|
||||
const results = await CallHandler.sharedInstance().sipVirtualLookup(userId);
|
||||
if (results.length === 0) return null;
|
||||
return results[0].userid;
|
||||
}
|
||||
|
||||
const regexString = templateString.replace('${mxid}', '(.+)');
|
||||
public async getOrCreateVirtualRoomForRoom(roomId: string):Promise<string> {
|
||||
const userId = DMRoomMap.shared().getUserIdForRoomId(roomId);
|
||||
if (!userId) return null;
|
||||
|
||||
const match = userId.match('^' + regexString + '$');
|
||||
if (!match) return null;
|
||||
const virtualUser = await this.userToVirtualUser(userId);
|
||||
if (!virtualUser) return null;
|
||||
|
||||
return decodeURIComponent(match[1].replace(/=/g, '%'));
|
||||
}
|
||||
// There's quite a bit of acromatics here to prevent the virtual room being shown
|
||||
// while it's being created: forstly, we have to stop the RoomListStore from showing
|
||||
// new rooms for a bit, because we can't set the room account data to say it's a virtual
|
||||
// room until we have the room ID. Secondly, once we have the new room ID, we have to
|
||||
// temporarily cache the fact it's a virtual room because there's no local echo on
|
||||
// room account data so it won't show up in the room model until it comes down the
|
||||
// sync stream again. Ick.
|
||||
RoomListStore.instance.startHoldingNewRooms();
|
||||
try {
|
||||
const virtualRoomId = await ensureDMExists(MatrixClientPeg.get(), virtualUser);
|
||||
MatrixClientPeg.get().setRoomAccountData(virtualRoomId, VIRTUAL_ROOM_EVENT_TYPE, {
|
||||
native_room: roomId,
|
||||
});
|
||||
this.virtualRoomIdCache.add(virtualRoomId);
|
||||
|
||||
async function getOrCreateVirtualRoomForUser(userId: string):Promise<string> {
|
||||
const virtualUser = userToVirtualUser(userId);
|
||||
if (!virtualUser) return null;
|
||||
return virtualRoomId;
|
||||
} finally {
|
||||
RoomListStore.instance.stopHoldingNewRooms();
|
||||
}
|
||||
}
|
||||
|
||||
return await ensureDMExists(MatrixClientPeg.get(), virtualUser);
|
||||
}
|
||||
public nativeRoomForVirtualRoom(roomId: string):string {
|
||||
const virtualRoom = MatrixClientPeg.get().getRoom(roomId);
|
||||
if (!virtualRoom) return null;
|
||||
const virtualRoomEvent = virtualRoom.getAccountData(VIRTUAL_ROOM_EVENT_TYPE);
|
||||
if (!virtualRoomEvent || !virtualRoomEvent.getContent()) return null;
|
||||
return virtualRoomEvent.getContent()['native_room'] || null;
|
||||
}
|
||||
|
||||
export async function getOrCreateVirtualRoomForRoom(roomId: string):Promise<string> {
|
||||
const user = DMRoomMap.shared().getUserIdForRoomId(roomId);
|
||||
if (!user) return null;
|
||||
return getOrCreateVirtualRoomForUser(user);
|
||||
}
|
||||
public isVirtualRoom(roomId: string):boolean {
|
||||
if (this.nativeRoomForVirtualRoom(roomId)) return true;
|
||||
|
||||
export function roomForVirtualRoom(roomId: string):string {
|
||||
const virtualUser = DMRoomMap.shared().getUserIdForRoomId(roomId);
|
||||
if (!virtualUser) return null;
|
||||
const realUser = virtualUserToUser(virtualUser);
|
||||
const room = findDMForUser(MatrixClientPeg.get(), realUser);
|
||||
if (room) {
|
||||
return room.roomId;
|
||||
} else {
|
||||
return null;
|
||||
return this.virtualRoomIdCache.has(roomId);
|
||||
}
|
||||
|
||||
public async onNewInvitedRoom(invitedRoom: Room) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (result[0].fields.is_virtual) {
|
||||
const nativeUser = result[0].userid;
|
||||
const nativeRoom = findDMForUser(MatrixClientPeg.get(), nativeUser);
|
||||
if (nativeRoom) {
|
||||
// It's a virtual room with a matching native room, so set the room account data. This
|
||||
// will make sure we know where how to map calls and also allow us know not to display
|
||||
// it in the future.
|
||||
MatrixClientPeg.get().setRoomAccountData(invitedRoom.roomId, VIRTUAL_ROOM_EVENT_TYPE, {
|
||||
native_room: nativeRoom.roomId,
|
||||
});
|
||||
// also auto-join the virtual room if we have a matching native room
|
||||
// (possibly we should only join if we've also joined the native room, then we'd also have
|
||||
// to make sure we joined virtual rooms on joining a native one)
|
||||
MatrixClientPeg.get().joinRoom(invitedRoom.roomId);
|
||||
}
|
||||
|
||||
// also put this room in the virtual room ID cache so isVirtualRoom return the right answer
|
||||
// in however long it takes for the echo of setAccountData to come down the sync
|
||||
this.virtualRoomIdCache.add(invitedRoom.roomId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isVirtualRoom(roomId: string):boolean {
|
||||
const virtualUser = DMRoomMap.shared().getUserIdForRoomId(roomId);
|
||||
if (!virtualUser) return null;
|
||||
const realUser = virtualUserToUser(virtualUser);
|
||||
return Boolean(realUser);
|
||||
}
|
||||
|
Reference in New Issue
Block a user