You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-25 05:23:13 +03:00
Merge branch 'master' into develop
This commit is contained in:
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,3 +1,20 @@
|
||||
Changes in [19.4.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v19.4.0) (2022-08-31)
|
||||
==================================================================================================
|
||||
|
||||
## ✨ Features
|
||||
* Re-emit room state events on rooms ([\#2607](https://github.com/matrix-org/matrix-js-sdk/pull/2607)).
|
||||
* Add ability to override built in room name generator for an i18n'able one ([\#2609](https://github.com/matrix-org/matrix-js-sdk/pull/2609)).
|
||||
* Add txn_id support to sliding sync ([\#2567](https://github.com/matrix-org/matrix-js-sdk/pull/2567)).
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Refactor Sync and fix `initialSyncLimit` ([\#2587](https://github.com/matrix-org/matrix-js-sdk/pull/2587)).
|
||||
* Use deep equality comparisons when searching for outgoing key requests by target ([\#2623](https://github.com/matrix-org/matrix-js-sdk/pull/2623)). Contributed by @duxovni.
|
||||
* Fix room membership race with PREPARED event ([\#2613](https://github.com/matrix-org/matrix-js-sdk/pull/2613)). Contributed by @jotto.
|
||||
* fixed a sliding sync bug which could cause the `roomIndexToRoomId` map to be incorrect when a new room is added in the middle of the list or when an existing room is deleted from the middle of the list. ([\#2610](https://github.com/matrix-org/matrix-js-sdk/pull/2610)).
|
||||
* Fix: Handle parsing of a beacon info event without asset ([\#2591](https://github.com/matrix-org/matrix-js-sdk/pull/2591)). Fixes vector-im/element-web#23078. Contributed by @kerryarchibald.
|
||||
* Fix finding event read up to if stable private read receipts is missing ([\#2585](https://github.com/matrix-org/matrix-js-sdk/pull/2585)). Fixes vector-im/element-web#23027.
|
||||
* fixed a sliding sync issue where history could be interpreted as live events. ([\#2583](https://github.com/matrix-org/matrix-js-sdk/pull/2583)).
|
||||
|
||||
Changes in [19.3.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v19.3.0) (2022-08-16)
|
||||
==================================================================================================
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "matrix-js-sdk",
|
||||
"version": "19.3.0",
|
||||
"version": "19.4.0",
|
||||
"description": "Matrix Client-Server SDK for Javascript",
|
||||
"engines": {
|
||||
"node": ">=12.9.0"
|
||||
@@ -32,7 +32,7 @@
|
||||
"keywords": [
|
||||
"matrix-org"
|
||||
],
|
||||
"main": "./src/index.ts",
|
||||
"main": "./lib/index.js",
|
||||
"browser": "./lib/browser-index.js",
|
||||
"matrix_src_main": "./src/index.ts",
|
||||
"matrix_src_browser": "./src/browser-index.js",
|
||||
@@ -125,5 +125,6 @@
|
||||
"jestSonar": {
|
||||
"reportPath": "coverage",
|
||||
"sonar56x": true
|
||||
}
|
||||
},
|
||||
"typings": "./lib/index.d.ts"
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ import { ClientEvent, MatrixClient, RoomMember } from '../../../../src';
|
||||
import { DeviceInfo, IDevice } from '../../../../src/crypto/deviceinfo';
|
||||
import { DeviceTrustLevel } from '../../../../src/crypto/CrossSigning';
|
||||
|
||||
const MegolmDecryption = algorithms.DECRYPTION_CLASSES['m.megolm.v1.aes-sha2'];
|
||||
const MegolmEncryption = algorithms.ENCRYPTION_CLASSES['m.megolm.v1.aes-sha2'];
|
||||
const MegolmDecryption = algorithms.DECRYPTION_CLASSES.get('m.megolm.v1.aes-sha2');
|
||||
const MegolmEncryption = algorithms.ENCRYPTION_CLASSES.get('m.megolm.v1.aes-sha2');
|
||||
|
||||
const ROOM_ID = '!ROOM:ID';
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import { IAbortablePromise, MatrixScheduler } from '../../../src';
|
||||
|
||||
const Olm = global.Olm;
|
||||
|
||||
const MegolmDecryption = algorithms.DECRYPTION_CLASSES['m.megolm.v1.aes-sha2'];
|
||||
const MegolmDecryption = algorithms.DECRYPTION_CLASSES.get('m.megolm.v1.aes-sha2');
|
||||
|
||||
const ROOM_ID = '!ROOM:ID';
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import { IRoomEncryption } from "../RoomList";
|
||||
*
|
||||
* @type {Object.<string, function(new: module:crypto/algorithms/base.EncryptionAlgorithm)>}
|
||||
*/
|
||||
export const ENCRYPTION_CLASSES: Record<string, new (params: IParams) => EncryptionAlgorithm> = {};
|
||||
export const ENCRYPTION_CLASSES = new Map<string, new (params: IParams) => EncryptionAlgorithm>();
|
||||
|
||||
type DecryptionClassParams = Omit<IParams, "deviceId" | "config">;
|
||||
|
||||
@@ -44,7 +44,7 @@ type DecryptionClassParams = Omit<IParams, "deviceId" | "config">;
|
||||
*
|
||||
* @type {Object.<string, function(new: module:crypto/algorithms/base.DecryptionAlgorithm)>}
|
||||
*/
|
||||
export const DECRYPTION_CLASSES: Record<string, new (params: DecryptionClassParams) => DecryptionAlgorithm> = {};
|
||||
export const DECRYPTION_CLASSES = new Map<string, new (params: DecryptionClassParams) => DecryptionAlgorithm>();
|
||||
|
||||
export interface IParams {
|
||||
userId: string;
|
||||
@@ -297,6 +297,6 @@ export function registerAlgorithm(
|
||||
encryptor: new (params: IParams) => EncryptionAlgorithm,
|
||||
decryptor: new (params: Omit<IParams, "deviceId">) => DecryptionAlgorithm,
|
||||
): void {
|
||||
ENCRYPTION_CLASSES[algorithm] = encryptor;
|
||||
DECRYPTION_CLASSES[algorithm] = decryptor;
|
||||
ENCRYPTION_CLASSES.set(algorithm, encryptor);
|
||||
DECRYPTION_CLASSES.set(algorithm, decryptor);
|
||||
}
|
||||
|
||||
@@ -1191,7 +1191,7 @@ class MegolmEncryption extends EncryptionAlgorithm {
|
||||
class MegolmDecryption extends DecryptionAlgorithm {
|
||||
// events which we couldn't decrypt due to unknown sessions / indexes: map from
|
||||
// senderKey|sessionId to Set of MatrixEvents
|
||||
private pendingEvents: Record<string, Map<string, Set<MatrixEvent>>> = {};
|
||||
private pendingEvents = new Map<string, Map<string, Set<MatrixEvent>>>();
|
||||
|
||||
// this gets stubbed out by the unit tests.
|
||||
private olmlib = olmlib;
|
||||
@@ -1343,10 +1343,10 @@ class MegolmDecryption extends DecryptionAlgorithm {
|
||||
const content = event.getWireContent();
|
||||
const senderKey = content.sender_key;
|
||||
const sessionId = content.session_id;
|
||||
if (!this.pendingEvents[senderKey]) {
|
||||
this.pendingEvents[senderKey] = new Map();
|
||||
if (!this.pendingEvents.has(senderKey)) {
|
||||
this.pendingEvents.set(senderKey, new Map<string, Set<MatrixEvent>>());
|
||||
}
|
||||
const senderPendingEvents = this.pendingEvents[senderKey];
|
||||
const senderPendingEvents = this.pendingEvents.get(senderKey);
|
||||
if (!senderPendingEvents.has(sessionId)) {
|
||||
senderPendingEvents.set(sessionId, new Set());
|
||||
}
|
||||
@@ -1364,7 +1364,7 @@ class MegolmDecryption extends DecryptionAlgorithm {
|
||||
const content = event.getWireContent();
|
||||
const senderKey = content.sender_key;
|
||||
const sessionId = content.session_id;
|
||||
const senderPendingEvents = this.pendingEvents[senderKey];
|
||||
const senderPendingEvents = this.pendingEvents.get(senderKey);
|
||||
const pendingEvents = senderPendingEvents?.get(sessionId);
|
||||
if (!pendingEvents) {
|
||||
return;
|
||||
@@ -1375,7 +1375,7 @@ class MegolmDecryption extends DecryptionAlgorithm {
|
||||
senderPendingEvents.delete(sessionId);
|
||||
}
|
||||
if (senderPendingEvents.size === 0) {
|
||||
delete this.pendingEvents[senderKey];
|
||||
this.pendingEvents.delete(senderKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1711,7 +1711,7 @@ class MegolmDecryption extends DecryptionAlgorithm {
|
||||
* @return {Boolean} whether all messages were successfully decrypted
|
||||
*/
|
||||
private async retryDecryption(senderKey: string, sessionId: string): Promise<boolean> {
|
||||
const senderPendingEvents = this.pendingEvents[senderKey];
|
||||
const senderPendingEvents = this.pendingEvents.get(senderKey);
|
||||
if (!senderPendingEvents) {
|
||||
return true;
|
||||
}
|
||||
@@ -1732,16 +1732,16 @@ class MegolmDecryption extends DecryptionAlgorithm {
|
||||
}));
|
||||
|
||||
// If decrypted successfully, they'll have been removed from pendingEvents
|
||||
return !this.pendingEvents[senderKey]?.has(sessionId);
|
||||
return !this.pendingEvents.get(senderKey)?.has(sessionId);
|
||||
}
|
||||
|
||||
public async retryDecryptionFromSender(senderKey: string): Promise<boolean> {
|
||||
const senderPendingEvents = this.pendingEvents[senderKey];
|
||||
const senderPendingEvents = this.pendingEvents.get(senderKey);
|
||||
if (!senderPendingEvents) {
|
||||
return true;
|
||||
}
|
||||
|
||||
delete this.pendingEvents[senderKey];
|
||||
this.pendingEvents.delete(senderKey);
|
||||
|
||||
await Promise.all([...senderPendingEvents].map(async ([_sessionId, pending]) => {
|
||||
await Promise.all([...pending].map(async (ev) => {
|
||||
@@ -1753,7 +1753,7 @@ class MegolmDecryption extends DecryptionAlgorithm {
|
||||
}));
|
||||
}));
|
||||
|
||||
return !this.pendingEvents[senderKey];
|
||||
return !this.pendingEvents.has(senderKey);
|
||||
}
|
||||
|
||||
public async sendSharedHistoryInboundSessions(devicesByUser: Record<string, DeviceInfo[]>): Promise<void> {
|
||||
|
||||
@@ -278,9 +278,9 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
private oneTimeKeyCheckInProgress = false;
|
||||
|
||||
// EncryptionAlgorithm instance for each room
|
||||
private roomEncryptors: Record<string, EncryptionAlgorithm> = {};
|
||||
private roomEncryptors = new Map<string, EncryptionAlgorithm>();
|
||||
// map from algorithm to DecryptionAlgorithm instance, for each room
|
||||
private roomDecryptors: Record<string, Record<string, DecryptionAlgorithm>> = {};
|
||||
private roomDecryptors = new Map<string, Map<string, DecryptionAlgorithm>>();
|
||||
|
||||
private deviceKeys: Record<string, string> = {}; // type: key
|
||||
|
||||
@@ -422,7 +422,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
this.deviceList.on(CryptoEvent.UserCrossSigningUpdated, this.onDeviceListUserCrossSigningUpdated);
|
||||
this.reEmitter.reEmit(this.deviceList, [CryptoEvent.DevicesUpdated, CryptoEvent.WillUpdateDevices]);
|
||||
|
||||
this.supportedAlgorithms = Object.keys(algorithms.DECRYPTION_CLASSES);
|
||||
this.supportedAlgorithms = Array.from(algorithms.DECRYPTION_CLASSES.keys());
|
||||
|
||||
this.outgoingRoomKeyRequestManager = new OutgoingRoomKeyRequestManager(
|
||||
baseApis, this.deviceId, this.cryptoStore,
|
||||
@@ -2527,7 +2527,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
* This should not normally be necessary.
|
||||
*/
|
||||
public forceDiscardSession(roomId: string): void {
|
||||
const alg = this.roomEncryptors[roomId];
|
||||
const alg = this.roomEncryptors.get(roomId);
|
||||
if (alg === undefined) throw new Error("Room not encrypted");
|
||||
if (alg.forceDiscardSession === undefined) {
|
||||
throw new Error("Room encryption algorithm doesn't support session discarding");
|
||||
@@ -2580,7 +2580,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
// the encryption event would appear in both.
|
||||
// If it's called more than twice though,
|
||||
// it signals a bug on client or server.
|
||||
const existingAlg = this.roomEncryptors[roomId];
|
||||
const existingAlg = this.roomEncryptors.get(roomId);
|
||||
if (existingAlg) {
|
||||
return;
|
||||
}
|
||||
@@ -2594,7 +2594,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
storeConfigPromise = this.roomList.setRoomEncryption(roomId, config);
|
||||
}
|
||||
|
||||
const AlgClass = algorithms.ENCRYPTION_CLASSES[config.algorithm];
|
||||
const AlgClass = algorithms.ENCRYPTION_CLASSES.get(config.algorithm);
|
||||
if (!AlgClass) {
|
||||
throw new Error("Unable to encrypt with " + config.algorithm);
|
||||
}
|
||||
@@ -2608,7 +2608,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
roomId,
|
||||
config,
|
||||
});
|
||||
this.roomEncryptors[roomId] = alg;
|
||||
this.roomEncryptors.set(roomId, alg);
|
||||
|
||||
if (storeConfigPromise) {
|
||||
await storeConfigPromise;
|
||||
@@ -2640,7 +2640,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
public trackRoomDevices(roomId: string): Promise<void> {
|
||||
const trackMembers = async () => {
|
||||
// not an encrypted room
|
||||
if (!this.roomEncryptors[roomId]) {
|
||||
if (!this.roomEncryptors.has(roomId)) {
|
||||
return;
|
||||
}
|
||||
const room = this.clientStore.getRoom(roomId);
|
||||
@@ -2785,7 +2785,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
* @param {module:models/room} room the room the event is in
|
||||
*/
|
||||
public prepareToEncrypt(room: Room): void {
|
||||
const alg = this.roomEncryptors[room.roomId];
|
||||
const alg = this.roomEncryptors.get(room.roomId);
|
||||
if (alg) {
|
||||
alg.prepareToEncrypt(room);
|
||||
}
|
||||
@@ -2808,7 +2808,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
|
||||
const roomId = event.getRoomId();
|
||||
|
||||
const alg = this.roomEncryptors[roomId];
|
||||
const alg = this.roomEncryptors.get(roomId);
|
||||
if (!alg) {
|
||||
// MatrixClient has already checked that this room should be encrypted,
|
||||
// so this is an unexpected situation.
|
||||
@@ -3097,7 +3097,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
private getTrackedE2eRooms(): Room[] {
|
||||
return this.clientStore.getRooms().filter((room) => {
|
||||
// check for rooms with encryption enabled
|
||||
const alg = this.roomEncryptors[room.roomId];
|
||||
const alg = this.roomEncryptors.get(room.roomId);
|
||||
if (!alg) {
|
||||
return false;
|
||||
}
|
||||
@@ -3533,7 +3533,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
|
||||
const roomId = member.roomId;
|
||||
|
||||
const alg = this.roomEncryptors[roomId];
|
||||
const alg = this.roomEncryptors.get(roomId);
|
||||
if (!alg) {
|
||||
// not encrypting in this room
|
||||
return;
|
||||
@@ -3634,11 +3634,11 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
` for ${roomId} / ${body.session_id} (id ${req.requestId})`);
|
||||
|
||||
if (userId !== this.userId) {
|
||||
if (!this.roomEncryptors[roomId]) {
|
||||
if (!this.roomEncryptors.get(roomId)) {
|
||||
logger.debug(`room key request for unencrypted room ${roomId}`);
|
||||
return;
|
||||
}
|
||||
const encryptor = this.roomEncryptors[roomId];
|
||||
const encryptor = this.roomEncryptors.get(roomId);
|
||||
const device = this.deviceList.getStoredDevice(userId, deviceId);
|
||||
if (!device) {
|
||||
logger.debug(`Ignoring keyshare for unknown device ${userId}:${deviceId}`);
|
||||
@@ -3674,12 +3674,12 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
|
||||
// if we don't have a decryptor for this room/alg, we don't have
|
||||
// the keys for the requested events, and can drop the requests.
|
||||
if (!this.roomDecryptors[roomId]) {
|
||||
if (!this.roomDecryptors.has(roomId)) {
|
||||
logger.log(`room key request for unencrypted room ${roomId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const decryptor = this.roomDecryptors[roomId][alg];
|
||||
const decryptor = this.roomDecryptors.get(roomId).get(alg);
|
||||
if (!decryptor) {
|
||||
logger.log(`room key request for unknown alg ${alg} in room ${roomId}`);
|
||||
return;
|
||||
@@ -3745,23 +3745,24 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
* unknown
|
||||
*/
|
||||
public getRoomDecryptor(roomId: string, algorithm: string): DecryptionAlgorithm {
|
||||
let decryptors: Record<string, DecryptionAlgorithm>;
|
||||
let decryptors: Map<string, DecryptionAlgorithm>;
|
||||
let alg: DecryptionAlgorithm;
|
||||
|
||||
roomId = roomId || null;
|
||||
if (roomId) {
|
||||
decryptors = this.roomDecryptors[roomId];
|
||||
decryptors = this.roomDecryptors.get(roomId);
|
||||
if (!decryptors) {
|
||||
this.roomDecryptors[roomId] = decryptors = {};
|
||||
decryptors = new Map<string, DecryptionAlgorithm>();
|
||||
this.roomDecryptors.set(roomId, decryptors);
|
||||
}
|
||||
|
||||
alg = decryptors[algorithm];
|
||||
alg = decryptors.get(algorithm);
|
||||
if (alg) {
|
||||
return alg;
|
||||
}
|
||||
}
|
||||
|
||||
const AlgClass = algorithms.DECRYPTION_CLASSES[algorithm];
|
||||
const AlgClass = algorithms.DECRYPTION_CLASSES.get(algorithm);
|
||||
if (!AlgClass) {
|
||||
throw new algorithms.DecryptionError(
|
||||
'UNKNOWN_ENCRYPTION_ALGORITHM',
|
||||
@@ -3777,7 +3778,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
});
|
||||
|
||||
if (decryptors) {
|
||||
decryptors[algorithm] = alg;
|
||||
decryptors.set(algorithm, alg);
|
||||
}
|
||||
return alg;
|
||||
}
|
||||
@@ -3791,9 +3792,9 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
*/
|
||||
private getRoomDecryptors(algorithm: string): DecryptionAlgorithm[] {
|
||||
const decryptors = [];
|
||||
for (const d of Object.values(this.roomDecryptors)) {
|
||||
if (algorithm in d) {
|
||||
decryptors.push(d[algorithm]);
|
||||
for (const d of this.roomDecryptors.values()) {
|
||||
if (d.has(algorithm)) {
|
||||
decryptors.push(d.get(algorithm));
|
||||
}
|
||||
}
|
||||
return decryptors;
|
||||
|
||||
@@ -86,7 +86,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
private readonly displayPendingEvents: boolean;
|
||||
private liveTimeline: EventTimeline;
|
||||
private timelines: EventTimeline[];
|
||||
private _eventIdToTimeline: Record<string, EventTimeline>;
|
||||
private _eventIdToTimeline = new Map<string, EventTimeline>();
|
||||
private filter?: Filter;
|
||||
|
||||
/**
|
||||
@@ -138,7 +138,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
|
||||
// just a list - *not* ordered.
|
||||
this.timelines = [this.liveTimeline];
|
||||
this._eventIdToTimeline = {};
|
||||
this._eventIdToTimeline = new Map<string, EventTimeline>();
|
||||
|
||||
this.filter = opts.filter;
|
||||
|
||||
@@ -210,7 +210,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
* @return {module:models/event-timeline~EventTimeline} timeline
|
||||
*/
|
||||
public eventIdToTimeline(eventId: string): EventTimeline {
|
||||
return this._eventIdToTimeline[eventId];
|
||||
return this._eventIdToTimeline.get(eventId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,10 +220,10 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
* @param {String} newEventId event ID of the replacement event
|
||||
*/
|
||||
public replaceEventId(oldEventId: string, newEventId: string): void {
|
||||
const existingTimeline = this._eventIdToTimeline[oldEventId];
|
||||
const existingTimeline = this._eventIdToTimeline.get(oldEventId);
|
||||
if (existingTimeline) {
|
||||
delete this._eventIdToTimeline[oldEventId];
|
||||
this._eventIdToTimeline[newEventId] = existingTimeline;
|
||||
this._eventIdToTimeline.delete(oldEventId);
|
||||
this._eventIdToTimeline.set(newEventId, existingTimeline);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
|
||||
if (resetAllTimelines) {
|
||||
this.timelines = [newTimeline];
|
||||
this._eventIdToTimeline = {};
|
||||
this._eventIdToTimeline = new Map<string, EventTimeline>();
|
||||
} else {
|
||||
this.timelines.push(newTimeline);
|
||||
}
|
||||
@@ -288,7 +288,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
* the given event, or null if unknown
|
||||
*/
|
||||
public getTimelineForEvent(eventId: string): EventTimeline | null {
|
||||
const res = this._eventIdToTimeline[eventId];
|
||||
const res = this._eventIdToTimeline.get(eventId);
|
||||
return (res === undefined) ? null : res;
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
const event = events[i];
|
||||
const eventId = event.getId();
|
||||
|
||||
const existingTimeline = this._eventIdToTimeline[eventId];
|
||||
const existingTimeline = this._eventIdToTimeline.get(eventId);
|
||||
|
||||
if (!existingTimeline) {
|
||||
// we don't know about this event yet. Just add it to the timeline.
|
||||
@@ -601,7 +601,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
}
|
||||
}
|
||||
|
||||
const timeline = this._eventIdToTimeline[event.getId()];
|
||||
const timeline = this._eventIdToTimeline.get(event.getId());
|
||||
if (timeline) {
|
||||
if (duplicateStrategy === DuplicateStrategy.Replace) {
|
||||
debuglog("EventTimelineSet.addLiveEvent: replacing duplicate event " + event.getId());
|
||||
@@ -697,7 +697,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
roomState,
|
||||
timelineWasEmpty,
|
||||
});
|
||||
this._eventIdToTimeline[eventId] = timeline;
|
||||
this._eventIdToTimeline.set(eventId, timeline);
|
||||
|
||||
this.relations.aggregateParentEvent(event);
|
||||
this.relations.aggregateChildEvent(event, this);
|
||||
@@ -725,10 +725,10 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
newEventId: string,
|
||||
): void {
|
||||
// XXX: why don't we infer newEventId from localEvent?
|
||||
const existingTimeline = this._eventIdToTimeline[oldEventId];
|
||||
const existingTimeline = this._eventIdToTimeline.get(oldEventId);
|
||||
if (existingTimeline) {
|
||||
delete this._eventIdToTimeline[oldEventId];
|
||||
this._eventIdToTimeline[newEventId] = existingTimeline;
|
||||
this._eventIdToTimeline.delete(oldEventId);
|
||||
this._eventIdToTimeline.set(newEventId, existingTimeline);
|
||||
} else {
|
||||
if (this.filter) {
|
||||
if (this.filter.filterRoomTimeline([localEvent]).length) {
|
||||
@@ -753,14 +753,14 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
* in this room.
|
||||
*/
|
||||
public removeEvent(eventId: string): MatrixEvent | null {
|
||||
const timeline = this._eventIdToTimeline[eventId];
|
||||
const timeline = this._eventIdToTimeline.get(eventId);
|
||||
if (!timeline) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const removed = timeline.removeEvent(eventId);
|
||||
if (removed) {
|
||||
delete this._eventIdToTimeline[eventId];
|
||||
this._eventIdToTimeline.delete(eventId);
|
||||
const data = {
|
||||
timeline: timeline,
|
||||
};
|
||||
@@ -787,8 +787,8 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
return 0;
|
||||
}
|
||||
|
||||
const timeline1 = this._eventIdToTimeline[eventId1];
|
||||
const timeline2 = this._eventIdToTimeline[eventId2];
|
||||
const timeline1 = this._eventIdToTimeline.get(eventId1);
|
||||
const timeline2 = this._eventIdToTimeline.get(eventId2);
|
||||
|
||||
if (timeline1 === undefined) {
|
||||
return null;
|
||||
|
||||
@@ -26,7 +26,7 @@ import { logger } from '../logger';
|
||||
import { VerificationRequest } from "../crypto/verification/request/VerificationRequest";
|
||||
import { EVENT_VISIBILITY_CHANGE_TYPE, EventType, MsgType, RelationType } from "../@types/event";
|
||||
import { Crypto, IEventDecryptionResult } from "../crypto";
|
||||
import { deepSortedObjectEntries } from "../utils";
|
||||
import { deepSortedObjectEntries, internaliseString } from "../utils";
|
||||
import { RoomMember } from "./room-member";
|
||||
import { Thread, ThreadEvent, EventHandlerMap as ThreadEventHandlerMap, THREAD_RELATION_TYPE } from "./thread";
|
||||
import { IActionsObject } from '../pushprocessor';
|
||||
@@ -37,14 +37,6 @@ import { EventStatus } from "./event-status";
|
||||
|
||||
export { EventStatus } from "./event-status";
|
||||
|
||||
const interns: Record<string, string> = {};
|
||||
function intern(str: string): string {
|
||||
if (!interns[str]) {
|
||||
interns[str] = str;
|
||||
}
|
||||
return interns[str];
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
export interface IContent {
|
||||
[key: string]: any;
|
||||
@@ -326,17 +318,17 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
// of space if we don't intern it.
|
||||
["state_key", "type", "sender", "room_id", "membership"].forEach((prop) => {
|
||||
if (typeof event[prop] !== "string") return;
|
||||
event[prop] = intern(event[prop]);
|
||||
event[prop] = internaliseString(event[prop]);
|
||||
});
|
||||
|
||||
["membership", "avatar_url", "displayname"].forEach((prop) => {
|
||||
if (typeof event.content?.[prop] !== "string") return;
|
||||
event.content[prop] = intern(event.content[prop]);
|
||||
event.content[prop] = internaliseString(event.content[prop]);
|
||||
});
|
||||
|
||||
["rel_type"].forEach((prop) => {
|
||||
if (typeof event.content?.["m.relates_to"]?.[prop] !== "string") return;
|
||||
event.content["m.relates_to"][prop] = intern(event.content["m.relates_to"][prop]);
|
||||
event.content["m.relates_to"][prop] = internaliseString(event.content["m.relates_to"][prop]);
|
||||
});
|
||||
|
||||
this.txnId = event.txn_id || null;
|
||||
@@ -796,6 +788,8 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
// not a decryption error: log the whole exception as an error
|
||||
// (and don't bother with a retry)
|
||||
const re = options.isRetry ? 're' : '';
|
||||
// For find results: this can produce "Error decrypting event (id=$ev)" and
|
||||
// "Error redecrypting event (id=$ev)".
|
||||
logger.error(
|
||||
`Error ${re}decrypting event ` +
|
||||
`(id=${this.getId()}): ${e.stack || e}`,
|
||||
|
||||
@@ -23,14 +23,8 @@ import { Room } from "./room";
|
||||
|
||||
export class RelationsContainer {
|
||||
// A tree of objects to access a set of related children for an event, as in:
|
||||
// this.relations[parentEventId][relationType][relationEventType]
|
||||
private relations: {
|
||||
[parentEventId: string]: {
|
||||
[relationType: RelationType | string]: {
|
||||
[eventType: EventType | string]: Relations;
|
||||
};
|
||||
};
|
||||
} = {};
|
||||
// this.relations.get(parentEventId).get(relationType).get(relationEventType)
|
||||
private relations = new Map<string, Map<RelationType | string, Map<EventType | string, Relations>>>();
|
||||
|
||||
constructor(private readonly client: MatrixClient, private readonly room?: Room) {
|
||||
}
|
||||
@@ -57,14 +51,15 @@ export class RelationsContainer {
|
||||
relationType: RelationType | string,
|
||||
eventType: EventType | string,
|
||||
): Relations | undefined {
|
||||
return this.relations[eventId]?.[relationType]?.[eventType];
|
||||
return this.relations.get(eventId)?.get(relationType)?.get(eventType);
|
||||
}
|
||||
|
||||
public getAllChildEventsForEvent(parentEventId: string): MatrixEvent[] {
|
||||
const relationsForEvent = this.relations[parentEventId] ?? {};
|
||||
const relationsForEvent = this.relations.get(parentEventId)
|
||||
?? new Map<RelationType | string, Map<EventType | string, Relations>>();
|
||||
const events: MatrixEvent[] = [];
|
||||
for (const relationsRecord of Object.values(relationsForEvent)) {
|
||||
for (const relations of Object.values(relationsRecord)) {
|
||||
for (const relationsRecord of relationsForEvent.values()) {
|
||||
for (const relations of relationsRecord.values()) {
|
||||
events.push(...relations.getRelations());
|
||||
}
|
||||
}
|
||||
@@ -79,11 +74,11 @@ export class RelationsContainer {
|
||||
* @param {MatrixEvent} event The event to check as relation target.
|
||||
*/
|
||||
public aggregateParentEvent(event: MatrixEvent): void {
|
||||
const relationsForEvent = this.relations[event.getId()];
|
||||
const relationsForEvent = this.relations.get(event.getId());
|
||||
if (!relationsForEvent) return;
|
||||
|
||||
for (const relationsWithRelType of Object.values(relationsForEvent)) {
|
||||
for (const relationsWithEventType of Object.values(relationsWithRelType)) {
|
||||
for (const relationsWithRelType of relationsForEvent.values()) {
|
||||
for (const relationsWithEventType of relationsWithRelType.values()) {
|
||||
relationsWithEventType.setTargetEvent(event);
|
||||
}
|
||||
}
|
||||
@@ -123,23 +118,26 @@ export class RelationsContainer {
|
||||
const { event_id: relatesToEventId, rel_type: relationType } = relation;
|
||||
const eventType = event.getType();
|
||||
|
||||
let relationsForEvent = this.relations[relatesToEventId];
|
||||
let relationsForEvent = this.relations.get(relatesToEventId);
|
||||
if (!relationsForEvent) {
|
||||
relationsForEvent = this.relations[relatesToEventId] = {};
|
||||
relationsForEvent = new Map<RelationType | string, Map<EventType | string, Relations>>();
|
||||
this.relations.set(relatesToEventId, relationsForEvent);
|
||||
}
|
||||
|
||||
let relationsWithRelType = relationsForEvent[relationType];
|
||||
let relationsWithRelType = relationsForEvent.get(relationType);
|
||||
if (!relationsWithRelType) {
|
||||
relationsWithRelType = relationsForEvent[relationType] = {};
|
||||
relationsWithRelType = new Map<EventType | string, Relations>();
|
||||
relationsForEvent.set(relationType, relationsWithRelType);
|
||||
}
|
||||
|
||||
let relationsWithEventType = relationsWithRelType[eventType];
|
||||
let relationsWithEventType = relationsWithRelType.get(eventType);
|
||||
if (!relationsWithEventType) {
|
||||
relationsWithEventType = relationsWithRelType[eventType] = new Relations(
|
||||
relationsWithEventType = new Relations(
|
||||
relationType,
|
||||
eventType,
|
||||
this.client,
|
||||
);
|
||||
relationsWithRelType.set(eventType, relationsWithEventType);
|
||||
|
||||
const room = this.room ?? timelineSet?.room;
|
||||
const relatesToEvent = timelineSet?.findEventById(relatesToEventId)
|
||||
|
||||
@@ -79,7 +79,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
public readonly reEmitter = new TypedReEmitter<EmittedEvents, EventHandlerMap>(this);
|
||||
private sentinels: Record<string, RoomMember> = {}; // userId: RoomMember
|
||||
// stores fuzzy matches to a list of userIDs (applies utils.removeHiddenChars to keys)
|
||||
private displayNameToUserIds: Record<string, string[]> = {};
|
||||
private displayNameToUserIds = new Map<string, string[]>();
|
||||
private userIdsToDisplayNames: Record<string, string> = {};
|
||||
private tokenToInvite: Record<string, MatrixEvent> = {}; // 3pid invite state_key to m.room.member invite
|
||||
private joinedMemberCount: number = null; // cache of the number of joined members
|
||||
@@ -709,7 +709,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
* @return {string[]} An array of user IDs or an empty array.
|
||||
*/
|
||||
public getUserIdsWithDisplayName(displayName: string): string[] {
|
||||
return this.displayNameToUserIds[utils.removeHiddenChars(displayName)] || [];
|
||||
return this.displayNameToUserIds.get(utils.removeHiddenChars(displayName)) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -941,11 +941,11 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
// the lot.
|
||||
const strippedOldName = utils.removeHiddenChars(oldName);
|
||||
|
||||
const existingUserIds = this.displayNameToUserIds[strippedOldName];
|
||||
const existingUserIds = this.displayNameToUserIds.get(strippedOldName);
|
||||
if (existingUserIds) {
|
||||
// remove this user ID from this array
|
||||
const filteredUserIDs = existingUserIds.filter((id) => id !== userId);
|
||||
this.displayNameToUserIds[strippedOldName] = filteredUserIDs;
|
||||
this.displayNameToUserIds.set(strippedOldName, filteredUserIDs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -954,10 +954,9 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
const strippedDisplayname = displayName && utils.removeHiddenChars(displayName);
|
||||
// an empty stripped displayname (undefined/'') will be set to MXID in room-member.js
|
||||
if (strippedDisplayname) {
|
||||
if (!this.displayNameToUserIds[strippedDisplayname]) {
|
||||
this.displayNameToUserIds[strippedDisplayname] = [];
|
||||
}
|
||||
this.displayNameToUserIds[strippedDisplayname].push(userId);
|
||||
const arr = this.displayNameToUserIds.get(strippedDisplayname) ?? [];
|
||||
arr.push(userId);
|
||||
this.displayNameToUserIds.set(strippedDisplayname, arr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
24
src/utils.ts
24
src/utils.ts
@@ -28,6 +28,30 @@ import { MatrixClient, MatrixEvent } from ".";
|
||||
import { M_TIMESTAMP } from "./@types/location";
|
||||
import { ReceiptType } from "./@types/read_receipts";
|
||||
|
||||
const interns = new Map<string, string>();
|
||||
|
||||
/**
|
||||
* Internalises a string, reusing a known pointer or storing the pointer
|
||||
* if needed for future strings.
|
||||
* @param str The string to internalise.
|
||||
* @returns The internalised string.
|
||||
*/
|
||||
export function internaliseString(str: string): string {
|
||||
// Unwrap strings before entering the map, if we somehow got a wrapped
|
||||
// string as our input. This should only happen from tests.
|
||||
if ((str as unknown) instanceof String) {
|
||||
str = str.toString();
|
||||
}
|
||||
|
||||
// Check the map to see if we can store the value
|
||||
if (!interns.has(str)) {
|
||||
interns.set(str, str);
|
||||
}
|
||||
|
||||
// Return any cached string reference
|
||||
return interns.get(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a dictionary of query parameters.
|
||||
* Omits any undefined/null values.
|
||||
|
||||
Reference in New Issue
Block a user