Merge pull request #5541 from matrix-org/dbkr/dmroommap_ts

Convert DMRoomMap to typescript
This commit is contained in:
David Baker 2021-01-13 16:23:30 +00:00 committed by GitHub
commit 604b9378ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 40 deletions

View file

@ -1,6 +1,5 @@
/* /*
Copyright 2016 OpenMarket Ltd Copyright 2016, 2019, 2021 The Matrix.org Foundation C.I.C.
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,7 +16,9 @@ limitations under the License.
import {MatrixClientPeg} from '../MatrixClientPeg'; import {MatrixClientPeg} from '../MatrixClientPeg';
import {uniq} from "lodash"; import {uniq} from "lodash";
import {Room} from "matrix-js-sdk/src/matrix"; import {Room} from "matrix-js-sdk/src/models/room";
import {Event} from "matrix-js-sdk/src/models/event";
import {MatrixClient} from "matrix-js-sdk/src/client";
/** /**
* Class that takes a Matrix Client and flips the m.direct map * Class that takes a Matrix Client and flips the m.direct map
@ -27,29 +28,31 @@ import {Room} from "matrix-js-sdk/src/matrix";
* With 'start', this can also keep itself up to date over time. * With 'start', this can also keep itself up to date over time.
*/ */
export default class DMRoomMap { export default class DMRoomMap {
private static sharedInstance: DMRoomMap;
private matrixClient: MatrixClient;
// TODO: convert these to maps
private roomToUser: {[key: string]: string} = null;
private userToRooms: {[key: string]: string[]} = null;
private hasSentOutPatchDirectAccountDataPatch: boolean;
private mDirectEvent: Event;
constructor(matrixClient) { constructor(matrixClient) {
this.matrixClient = matrixClient; this.matrixClient = matrixClient;
this.roomToUser = null; // see onAccountData
// see _onAccountData this.hasSentOutPatchDirectAccountDataPatch = false;
this._hasSentOutPatchDirectAccountDataPatch = false;
// XXX: Force-bind the event handler method because it
// doesn't call it with our object as the 'this'
// (use a static property arrow function for this when we can)
this._onAccountData = this._onAccountData.bind(this);
const mDirectEvent = matrixClient.getAccountData('m.direct'); const mDirectEvent = matrixClient.getAccountData('m.direct');
this.mDirectEvent = mDirectEvent ? mDirectEvent.getContent() : {}; this.mDirectEvent = mDirectEvent ? mDirectEvent.getContent() : {};
this.userToRooms = null;
} }
/** /**
* Makes and returns a new shared instance that can then be accessed * Makes and returns a new shared instance that can then be accessed
* with shared(). This returned instance is not automatically started. * with shared(). This returned instance is not automatically started.
*/ */
static makeShared() { public static makeShared(): DMRoomMap {
DMRoomMap._sharedInstance = new DMRoomMap(MatrixClientPeg.get()); DMRoomMap.sharedInstance = new DMRoomMap(MatrixClientPeg.get());
return DMRoomMap._sharedInstance; return DMRoomMap.sharedInstance;
} }
/** /**
@ -57,32 +60,33 @@ export default class DMRoomMap {
* that uses the singleton matrix client * that uses the singleton matrix client
* The shared instance must be started before use. * The shared instance must be started before use.
*/ */
static shared() { public static shared(): DMRoomMap {
return DMRoomMap._sharedInstance; return DMRoomMap.sharedInstance;
} }
start() { public start() {
this._populateRoomToUser(); this.populateRoomToUser();
this.matrixClient.on("accountData", this._onAccountData); this.matrixClient.on("accountData", this.onAccountData);
} }
stop() { public stop() {
this.matrixClient.removeListener("accountData", this._onAccountData); this.matrixClient.removeListener("accountData", this.onAccountData);
} }
_onAccountData(ev) { private onAccountData = (ev) => {
if (ev.getType() == 'm.direct') { if (ev.getType() == 'm.direct') {
this.mDirectEvent = this.matrixClient.getAccountData('m.direct').getContent() || {}; this.mDirectEvent = this.matrixClient.getAccountData('m.direct').getContent() || {};
this.userToRooms = null; this.userToRooms = null;
this.roomToUser = null; this.roomToUser = null;
} }
} }
/** /**
* some client bug somewhere is causing some DMs to be marked * some client bug somewhere is causing some DMs to be marked
* with ourself, not the other user. Fix it by guessing the other user and * with ourself, not the other user. Fix it by guessing the other user and
* modifying userToRooms * modifying userToRooms
*/ */
_patchUpSelfDMs(userToRooms) { private patchUpSelfDMs(userToRooms) {
const myUserId = this.matrixClient.getUserId(); const myUserId = this.matrixClient.getUserId();
const selfRoomIds = userToRooms[myUserId]; const selfRoomIds = userToRooms[myUserId];
if (selfRoomIds) { if (selfRoomIds) {
@ -118,10 +122,10 @@ export default class DMRoomMap {
} }
} }
getDMRoomsForUserId(userId) { public getDMRoomsForUserId(userId): string[] {
// Here, we return the empty list if there are no rooms, // Here, we return the empty list if there are no rooms,
// since the number of conversations you have with this user is zero. // since the number of conversations you have with this user is zero.
return this._getUserToRooms()[userId] || []; return this.getUserToRooms()[userId] || [];
} }
/** /**
@ -129,7 +133,7 @@ export default class DMRoomMap {
* @param {string[]} ids The identifiers (user IDs and email addresses) to look for. * @param {string[]} ids The identifiers (user IDs and email addresses) to look for.
* @returns {Room} The DM room which all IDs given share, or falsey if no common room. * @returns {Room} The DM room which all IDs given share, or falsey if no common room.
*/ */
getDMRoomForIdentifiers(ids) { public getDMRoomForIdentifiers(ids: string[]): Room {
// TODO: [Canonical DMs] Handle lookups for email addresses. // TODO: [Canonical DMs] Handle lookups for email addresses.
// For now we'll pretend we only get user IDs and end up returning nothing for email addresses // For now we'll pretend we only get user IDs and end up returning nothing for email addresses
@ -145,7 +149,7 @@ export default class DMRoomMap {
return joinedRooms[0]; return joinedRooms[0];
} }
getUserIdForRoomId(roomId) { public getUserIdForRoomId(roomId: string) {
if (this.roomToUser == null) { if (this.roomToUser == null) {
// we lazily populate roomToUser so you can use // we lazily populate roomToUser so you can use
// this class just to call getDMRoomsForUserId // this class just to call getDMRoomsForUserId
@ -153,7 +157,7 @@ export default class DMRoomMap {
// convenient wrapper and there's no point // convenient wrapper and there's no point
// iterating through the map if getUserIdForRoomId() // iterating through the map if getUserIdForRoomId()
// is never called. // is never called.
this._populateRoomToUser(); this.populateRoomToUser();
} }
// Here, we return undefined if the room is not in the map: // Here, we return undefined if the room is not in the map:
// the room ID you gave is not a DM room for any user. // the room ID you gave is not a DM room for any user.
@ -167,7 +171,7 @@ export default class DMRoomMap {
return this.roomToUser[roomId]; return this.roomToUser[roomId];
} }
getUniqueRoomsWithIndividuals(): {[userId: string]: Room} { public getUniqueRoomsWithIndividuals(): {[userId: string]: Room} {
if (!this.roomToUser) return {}; // No rooms means no map. if (!this.roomToUser) return {}; // No rooms means no map.
return Object.keys(this.roomToUser) return Object.keys(this.roomToUser)
.map(r => ({userId: this.getUserIdForRoomId(r), room: this.matrixClient.getRoom(r)})) .map(r => ({userId: this.getUserIdForRoomId(r), room: this.matrixClient.getRoom(r)}))
@ -175,20 +179,20 @@ export default class DMRoomMap {
.reduce((obj, r) => (obj[r.userId] = r.room) && obj, {}); .reduce((obj, r) => (obj[r.userId] = r.room) && obj, {});
} }
_getUserToRooms() { private getUserToRooms(): {[key: string]: string[]} {
if (!this.userToRooms) { if (!this.userToRooms) {
const userToRooms = this.mDirectEvent; const userToRooms = this.mDirectEvent as {[key: string]: string[]};
const myUserId = this.matrixClient.getUserId(); const myUserId = this.matrixClient.getUserId();
const selfDMs = userToRooms[myUserId]; const selfDMs = userToRooms[myUserId];
if (selfDMs && selfDMs.length) { if (selfDMs && selfDMs.length) {
const neededPatching = this._patchUpSelfDMs(userToRooms); const neededPatching = this.patchUpSelfDMs(userToRooms);
// to avoid multiple devices fighting to correct // to avoid multiple devices fighting to correct
// the account data, only try to send the corrected // the account data, only try to send the corrected
// version once. // version once.
console.warn(`Invalid m.direct account data detected ` + console.warn(`Invalid m.direct account data detected ` +
`(self-chats that shouldn't be), patching it up.`); `(self-chats that shouldn't be), patching it up.`);
if (neededPatching && !this._hasSentOutPatchDirectAccountDataPatch) { if (neededPatching && !this.hasSentOutPatchDirectAccountDataPatch) {
this._hasSentOutPatchDirectAccountDataPatch = true; this.hasSentOutPatchDirectAccountDataPatch = true;
this.matrixClient.setAccountData('m.direct', userToRooms); this.matrixClient.setAccountData('m.direct', userToRooms);
} }
} }
@ -197,9 +201,9 @@ export default class DMRoomMap {
return this.userToRooms; return this.userToRooms;
} }
_populateRoomToUser() { private populateRoomToUser() {
this.roomToUser = {}; this.roomToUser = {};
for (const user of Object.keys(this._getUserToRooms())) { for (const user of Object.keys(this.getUserToRooms())) {
for (const roomId of this.userToRooms[user]) { for (const roomId of this.userToRooms[user]) {
this.roomToUser[roomId] = user; this.roomToUser[roomId] = user;
} }

View file

@ -9,8 +9,8 @@ import sdk from '../../../skinned-sdk';
import { DragDropContext } from 'react-beautiful-dnd'; import { DragDropContext } from 'react-beautiful-dnd';
import dis from '../../../../src/dispatcher/dispatcher'; import dis from '../../../../src/dispatcher/dispatcher';
import DMRoomMap from '../../../../src/utils/DMRoomMap.js'; import DMRoomMap from '../../../../src/utils/DMRoomMap';
import GroupStore from '../../../../src/stores/GroupStore.js'; import GroupStore from '../../../../src/stores/GroupStore';
import { MatrixClient, Room, RoomMember } from 'matrix-js-sdk'; import { MatrixClient, Room, RoomMember } from 'matrix-js-sdk';
import {DefaultTagID} from "../../../../src/stores/room-list/models"; import {DefaultTagID} from "../../../../src/stores/room-list/models";

View file

@ -42,7 +42,7 @@ describe("mkClient self-test", function() {
describe("shieldStatusForMembership self-trust behaviour", function() { describe("shieldStatusForMembership self-trust behaviour", function() {
beforeAll(() => { beforeAll(() => {
DMRoomMap._sharedInstance = { DMRoomMap.sharedInstance = {
getUserIdForRoomId: (roomId) => roomId === "DM" ? "@any:h" : null, getUserIdForRoomId: (roomId) => roomId === "DM" ? "@any:h" : null,
}; };
}); });