You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-30 04:23:07 +03:00
Fix more typescript --strict violations (#2795)
* Stash tsc fixes * Iterate * Iterate * Iterate * Fix tests * Iterate * Iterate * Iterate * Iterate * Add tests
This commit is contained in:
committed by
GitHub
parent
4b3e6939d6
commit
9f2f08dfd3
@ -658,7 +658,7 @@ describe("MatrixClient", function() {
|
||||
|
||||
// The vote event has been copied into the thread
|
||||
const eventRefWithThreadId = withThreadId(
|
||||
eventPollResponseReference, eventPollStartThreadRoot.getId());
|
||||
eventPollResponseReference, eventPollStartThreadRoot.getId()!);
|
||||
expect(eventRefWithThreadId.threadRootId).toBeTruthy();
|
||||
|
||||
expect(threaded).toEqual([
|
||||
@ -695,7 +695,7 @@ describe("MatrixClient", function() {
|
||||
expect(threaded).toEqual([
|
||||
eventPollStartThreadRoot,
|
||||
eventMessageInThread,
|
||||
withThreadId(eventReaction, eventPollStartThreadRoot.getId()),
|
||||
withThreadId(eventReaction, eventPollStartThreadRoot.getId()!),
|
||||
]);
|
||||
});
|
||||
|
||||
@ -725,7 +725,7 @@ describe("MatrixClient", function() {
|
||||
|
||||
expect(threaded).toEqual([
|
||||
eventPollStartThreadRoot,
|
||||
withThreadId(eventPollResponseReference, eventPollStartThreadRoot.getId()),
|
||||
withThreadId(eventPollResponseReference, eventPollStartThreadRoot.getId()!),
|
||||
eventMessageInThread,
|
||||
]);
|
||||
});
|
||||
@ -757,7 +757,7 @@ describe("MatrixClient", function() {
|
||||
expect(threaded).toEqual([
|
||||
eventPollStartThreadRoot,
|
||||
eventMessageInThread,
|
||||
withThreadId(eventReaction, eventPollStartThreadRoot.getId()),
|
||||
withThreadId(eventReaction, eventPollStartThreadRoot.getId()!),
|
||||
]);
|
||||
});
|
||||
|
||||
@ -813,7 +813,7 @@ describe("MatrixClient", function() {
|
||||
// Thread should contain only stuff that happened in the thread - no room state events
|
||||
expect(threaded).toEqual([
|
||||
eventPollStartThreadRoot,
|
||||
withThreadId(eventPollResponseReference, eventPollStartThreadRoot.getId()),
|
||||
withThreadId(eventPollResponseReference, eventPollStartThreadRoot.getId()!),
|
||||
eventMessageInThread,
|
||||
]);
|
||||
});
|
||||
@ -1375,7 +1375,7 @@ const buildEventMessageInThread = (root: MatrixEvent) => new MatrixEvent({
|
||||
"m.relates_to": {
|
||||
"event_id": root.getId(),
|
||||
"m.in_reply_to": {
|
||||
"event_id": root.getId(),
|
||||
"event_id": root.getId()!,
|
||||
},
|
||||
"rel_type": "m.thread",
|
||||
},
|
||||
@ -1474,13 +1474,13 @@ const buildEventReply = (target: MatrixEvent) => new MatrixEvent({
|
||||
"device_id": "XISFUZSKHH",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": target.getId(),
|
||||
"event_id": target.getId()!,
|
||||
},
|
||||
},
|
||||
"sender_key": "i3N3CtG/CD2bGB8rA9fW6adLYSDvlUhf2iuU73L65Vg",
|
||||
"session_id": "Ja11R/KG6ua0wdk8zAzognrxjio1Gm/RK2Gn6lFL804",
|
||||
},
|
||||
"event_id": target.getId() + Math.random(),
|
||||
"event_id": target.getId()! + Math.random(),
|
||||
"origin_server_ts": 1643815466378,
|
||||
"room_id": "!STrMRsukXHtqQdSeHa:matrix.org",
|
||||
"sender": "@andybalaam-test1:matrix.org",
|
||||
|
@ -307,7 +307,7 @@ export function mkReplyMessage(
|
||||
"rel_type": "m.in_reply_to",
|
||||
"event_id": opts.replyToMessage.getId(),
|
||||
"m.in_reply_to": {
|
||||
"event_id": opts.replyToMessage.getId(),
|
||||
"event_id": opts.replyToMessage.getId()!,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -222,9 +222,9 @@ describe.each([
|
||||
["IndexedDBCryptoStore",
|
||||
() => new IndexedDBCryptoStore(global.indexedDB, "tests")],
|
||||
["LocalStorageCryptoStore",
|
||||
() => new IndexedDBCryptoStore(undefined, "tests")],
|
||||
() => new IndexedDBCryptoStore(undefined!, "tests")],
|
||||
["MemoryCryptoStore", () => {
|
||||
const store = new IndexedDBCryptoStore(undefined, "tests");
|
||||
const store = new IndexedDBCryptoStore(undefined!, "tests");
|
||||
// @ts-ignore set private properties
|
||||
store._backend = new MemoryCryptoStore();
|
||||
// @ts-ignore
|
||||
@ -255,6 +255,6 @@ describe.each([
|
||||
expect(nokey).toBeNull();
|
||||
|
||||
const key = await getCrossSigningKeyCache!("self_signing", "");
|
||||
expect(new Uint8Array(key)).toEqual(testKey);
|
||||
expect(new Uint8Array(key!)).toEqual(testKey);
|
||||
});
|
||||
});
|
||||
|
@ -536,6 +536,7 @@ describe("MegolmDecryption", function() {
|
||||
"@bob:example.com", BOB_DEVICES,
|
||||
);
|
||||
aliceClient.crypto!.deviceList.downloadKeys = async function(userIds) {
|
||||
// @ts-ignore short-circuiting private method
|
||||
return this.getDevicesFromStore(userIds);
|
||||
};
|
||||
|
||||
|
@ -437,6 +437,7 @@ describe("Secrets", function() {
|
||||
return [keyId, secretStorageKeys[keyId]];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -571,6 +572,7 @@ describe("Secrets", function() {
|
||||
return [keyId, secretStorageKeys[keyId]];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -131,7 +131,11 @@ function makeRemoteEcho(event) {
|
||||
}));
|
||||
}
|
||||
|
||||
async function distributeEvent(ownRequest, theirRequest, event) {
|
||||
async function distributeEvent(
|
||||
ownRequest: VerificationRequest,
|
||||
theirRequest: VerificationRequest,
|
||||
event: MatrixEvent,
|
||||
): Promise<void> {
|
||||
await ownRequest.channel.handleEvent(
|
||||
makeRemoteEcho(event),
|
||||
ownRequest,
|
||||
|
@ -45,7 +45,7 @@ describe('EventTimelineSet', () => {
|
||||
it('should return the related events', () => {
|
||||
eventTimelineSet.relations.aggregateChildEvent(messageEvent);
|
||||
const relations = eventTimelineSet.relations.getChildEventsForEvent(
|
||||
messageEvent.getId(),
|
||||
messageEvent.getId()!,
|
||||
"m.in_reply_to",
|
||||
EventType.RoomMessage,
|
||||
);
|
||||
@ -193,7 +193,7 @@ describe('EventTimelineSet', () => {
|
||||
it('should not return the related events', () => {
|
||||
eventTimelineSet.relations.aggregateChildEvent(messageEvent);
|
||||
const relations = eventTimelineSet.relations.getChildEventsForEvent(
|
||||
messageEvent.getId(),
|
||||
messageEvent.getId()!,
|
||||
"m.in_reply_to",
|
||||
EventType.RoomMessage,
|
||||
);
|
||||
@ -236,7 +236,7 @@ describe('EventTimelineSet', () => {
|
||||
"m.relates_to": {
|
||||
"event_id": root.getId(),
|
||||
"m.in_reply_to": {
|
||||
"event_id": root.getId(),
|
||||
"event_id": root.getId()!,
|
||||
},
|
||||
"rel_type": "m.thread",
|
||||
},
|
||||
@ -278,14 +278,14 @@ describe('EventTimelineSet', () => {
|
||||
});
|
||||
|
||||
it("should return true if the timeline set is for a thread and the event is its thread root", () => {
|
||||
const thread = new Thread(messageEvent.getId(), messageEvent, { room, client });
|
||||
const thread = new Thread(messageEvent.getId()!, messageEvent, { room, client });
|
||||
const eventTimelineSet = new EventTimelineSet(room, {}, client, thread);
|
||||
messageEvent.setThread(thread);
|
||||
expect(eventTimelineSet.canContain(messageEvent)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should return true if the timeline set is for a thread and the event is a response to it", () => {
|
||||
const thread = new Thread(messageEvent.getId(), messageEvent, { room, client });
|
||||
const thread = new Thread(messageEvent.getId()!, messageEvent, { room, client });
|
||||
const eventTimelineSet = new EventTimelineSet(room, {}, client, thread);
|
||||
messageEvent.setThread(thread);
|
||||
const event = mkThreadResponse(messageEvent);
|
||||
@ -310,7 +310,7 @@ describe('EventTimelineSet', () => {
|
||||
content: { body: "test" },
|
||||
event_id: "!test1:server",
|
||||
});
|
||||
eventTimelineSet.handleRemoteEcho(roomMessageEvent, "~!local-event-id:server", roomMessageEvent.getId());
|
||||
eventTimelineSet.handleRemoteEcho(roomMessageEvent, "~!local-event-id:server", roomMessageEvent.getId()!);
|
||||
expect(eventTimelineSet.getLiveTimeline().getEvents()).toContain(roomMessageEvent);
|
||||
|
||||
const roomFilteredEvent = new MatrixEvent({
|
||||
@ -318,7 +318,7 @@ describe('EventTimelineSet', () => {
|
||||
content: { body: "test" },
|
||||
event_id: "!test2:server",
|
||||
});
|
||||
eventTimelineSet.handleRemoteEcho(roomFilteredEvent, "~!local-event-id:server", roomFilteredEvent.getId());
|
||||
eventTimelineSet.handleRemoteEcho(roomFilteredEvent, "~!local-event-id:server", roomFilteredEvent.getId()!);
|
||||
expect(eventTimelineSet.getLiveTimeline().getEvents()).not.toContain(roomFilteredEvent);
|
||||
});
|
||||
});
|
||||
|
@ -341,11 +341,11 @@ describe("EventTimeline", function() {
|
||||
timeline.addEvent(events[1], { toStartOfTimeline: false });
|
||||
expect(timeline.getEvents().length).toEqual(2);
|
||||
|
||||
let ev = timeline.removeEvent(events[0].getId());
|
||||
let ev = timeline.removeEvent(events[0].getId()!);
|
||||
expect(ev).toBe(events[0]);
|
||||
expect(timeline.getEvents().length).toEqual(1);
|
||||
|
||||
ev = timeline.removeEvent(events[1].getId());
|
||||
ev = timeline.removeEvent(events[1].getId()!);
|
||||
expect(ev).toBe(events[1]);
|
||||
expect(timeline.getEvents().length).toEqual(0);
|
||||
});
|
||||
@ -357,11 +357,11 @@ describe("EventTimeline", function() {
|
||||
expect(timeline.getEvents().length).toEqual(3);
|
||||
expect(timeline.getBaseIndex()).toEqual(1);
|
||||
|
||||
timeline.removeEvent(events[2].getId());
|
||||
timeline.removeEvent(events[2].getId()!);
|
||||
expect(timeline.getEvents().length).toEqual(2);
|
||||
expect(timeline.getBaseIndex()).toEqual(1);
|
||||
|
||||
timeline.removeEvent(events[1].getId());
|
||||
timeline.removeEvent(events[1].getId()!);
|
||||
expect(timeline.getEvents().length).toEqual(1);
|
||||
expect(timeline.getBaseIndex()).toEqual(0);
|
||||
});
|
||||
@ -372,7 +372,7 @@ describe("EventTimeline", function() {
|
||||
it("should not make baseIndex assplode when removing the last event",
|
||||
function() {
|
||||
timeline.addEvent(events[0], { toStartOfTimeline: true });
|
||||
timeline.removeEvent(events[0].getId());
|
||||
timeline.removeEvent(events[0].getId()!);
|
||||
const initialIndex = timeline.getBaseIndex();
|
||||
timeline.addEvent(events[1], { toStartOfTimeline: false });
|
||||
timeline.addEvent(events[2], { toStartOfTimeline: false });
|
||||
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 New Vector Ltd
|
||||
Copyright 2019, 2022 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 { MatrixEvent } from "../../src/models/event";
|
||||
|
||||
describe("MatrixEvent", () => {
|
||||
describe(".attemptDecryption", () => {
|
||||
let encryptedEvent;
|
||||
const eventId = 'test_encrypted_event';
|
||||
|
||||
beforeEach(() => {
|
||||
encryptedEvent = new MatrixEvent({
|
||||
event_id: eventId,
|
||||
type: 'm.room.encrypted',
|
||||
content: {
|
||||
ciphertext: 'secrets',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should retry decryption if a retry is queued', async () => {
|
||||
const eventAttemptDecryptionSpy = jest.spyOn(encryptedEvent, 'attemptDecryption');
|
||||
|
||||
const crypto = {
|
||||
decryptEvent: jest.fn()
|
||||
.mockImplementationOnce(() => {
|
||||
// schedule a second decryption attempt while
|
||||
// the first one is still running.
|
||||
encryptedEvent.attemptDecryption(crypto);
|
||||
|
||||
const error = new Error("nope");
|
||||
error.name = 'DecryptionError';
|
||||
return Promise.reject(error);
|
||||
})
|
||||
.mockImplementationOnce(() => {
|
||||
return Promise.resolve({
|
||||
clearEvent: {
|
||||
type: 'm.room.message',
|
||||
},
|
||||
});
|
||||
}),
|
||||
};
|
||||
|
||||
await encryptedEvent.attemptDecryption(crypto);
|
||||
|
||||
expect(eventAttemptDecryptionSpy).toHaveBeenCalledTimes(2);
|
||||
expect(crypto.decryptEvent).toHaveBeenCalledTimes(2);
|
||||
expect(encryptedEvent.getType()).toEqual('m.room.message');
|
||||
});
|
||||
});
|
||||
});
|
@ -312,7 +312,7 @@ describe("MSC3089Branch", () => {
|
||||
} as MatrixEvent);
|
||||
|
||||
const events = [await branch.getFileEvent(), await branch2.getFileEvent(), {
|
||||
replacingEventId: (): string => null,
|
||||
replacingEventId: (): string | undefined => undefined,
|
||||
getId: () => "$unknown",
|
||||
}];
|
||||
staticRoom.getLiveTimeline = () => ({ getEvents: () => events }) as EventTimeline;
|
||||
|
@ -135,7 +135,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
// noinspection ExceptionCaughtLocallyJS
|
||||
throw new Error("Failed to fail");
|
||||
} catch (e) {
|
||||
expect(e.errcode).toEqual("M_FORBIDDEN");
|
||||
expect((<MatrixError>e).errcode).toEqual("M_FORBIDDEN");
|
||||
}
|
||||
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
@ -513,7 +513,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
function expectOrder(childRoomId: string, order: number) {
|
||||
const child = childTrees.find(c => c.roomId === childRoomId);
|
||||
expect(child).toBeDefined();
|
||||
expect(child.getOrder()).toEqual(order);
|
||||
expect(child!.getOrder()).toEqual(order);
|
||||
}
|
||||
|
||||
function makeMockChildRoom(roomId: string): Room {
|
||||
@ -608,7 +608,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
// noinspection ExceptionCaughtLocallyJS
|
||||
throw new Error("Failed to fail");
|
||||
} catch (e) {
|
||||
expect(e.message).toEqual("Cannot set order of top level spaces currently");
|
||||
expect((<Error>e).message).toEqual("Cannot set order of top level spaces currently");
|
||||
}
|
||||
});
|
||||
|
||||
@ -706,7 +706,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
|
||||
const treeA = childTrees.find(c => c.roomId === a);
|
||||
expect(treeA).toBeDefined();
|
||||
await treeA.setOrder(1);
|
||||
await treeA!.setOrder(1);
|
||||
|
||||
expect(clientSendStateFn).toHaveBeenCalledTimes(3);
|
||||
expect(clientSendStateFn).toHaveBeenCalledWith(tree.roomId, EventType.SpaceChild, expect.objectContaining({
|
||||
@ -743,7 +743,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
|
||||
const treeA = childTrees.find(c => c.roomId === a);
|
||||
expect(treeA).toBeDefined();
|
||||
await treeA.setOrder(1);
|
||||
await treeA!.setOrder(1);
|
||||
|
||||
expect(clientSendStateFn).toHaveBeenCalledTimes(1);
|
||||
expect(clientSendStateFn).toHaveBeenCalledWith(tree.roomId, EventType.SpaceChild, expect.objectContaining({
|
||||
@ -771,7 +771,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
|
||||
const treeA = childTrees.find(c => c.roomId === a);
|
||||
expect(treeA).toBeDefined();
|
||||
await treeA.setOrder(2);
|
||||
await treeA!.setOrder(2);
|
||||
|
||||
expect(clientSendStateFn).toHaveBeenCalledTimes(1);
|
||||
expect(clientSendStateFn).toHaveBeenCalledWith(tree.roomId, EventType.SpaceChild, expect.objectContaining({
|
||||
@ -800,7 +800,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
|
||||
const treeB = childTrees.find(c => c.roomId === b);
|
||||
expect(treeB).toBeDefined();
|
||||
await treeB.setOrder(2);
|
||||
await treeB!.setOrder(2);
|
||||
|
||||
expect(clientSendStateFn).toHaveBeenCalledTimes(1);
|
||||
expect(clientSendStateFn).toHaveBeenCalledWith(tree.roomId, EventType.SpaceChild, expect.objectContaining({
|
||||
@ -829,7 +829,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
|
||||
const treeC = childTrees.find(ch => ch.roomId === c);
|
||||
expect(treeC).toBeDefined();
|
||||
await treeC.setOrder(1);
|
||||
await treeC!.setOrder(1);
|
||||
|
||||
expect(clientSendStateFn).toHaveBeenCalledTimes(1);
|
||||
expect(clientSendStateFn).toHaveBeenCalledWith(tree.roomId, EventType.SpaceChild, expect.objectContaining({
|
||||
@ -858,7 +858,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
|
||||
const treeB = childTrees.find(ch => ch.roomId === b);
|
||||
expect(treeB).toBeDefined();
|
||||
await treeB.setOrder(2);
|
||||
await treeB!.setOrder(2);
|
||||
|
||||
expect(clientSendStateFn).toHaveBeenCalledTimes(2);
|
||||
expect(clientSendStateFn).toHaveBeenCalledWith(tree.roomId, EventType.SpaceChild, expect.objectContaining({
|
||||
@ -903,7 +903,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
url: mxc,
|
||||
file: fileInfo,
|
||||
metadata: true, // additional content from test
|
||||
[UNSTABLE_MSC3089_LEAF.unstable]: {}, // test to ensure we're definitely using unstable
|
||||
[UNSTABLE_MSC3089_LEAF.unstable!]: {}, // test to ensure we're definitely using unstable
|
||||
});
|
||||
|
||||
return Promise.resolve({ event_id: fileEventId }); // eslint-disable-line camelcase
|
||||
@ -965,7 +965,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
expect(contents).toMatchObject({
|
||||
...content,
|
||||
"m.new_content": content,
|
||||
[UNSTABLE_MSC3089_LEAF.unstable]: {}, // test to ensure we're definitely using unstable
|
||||
[UNSTABLE_MSC3089_LEAF.unstable!]: {}, // test to ensure we're definitely using unstable
|
||||
});
|
||||
|
||||
return Promise.resolve({ event_id: fileEventId }); // eslint-disable-line camelcase
|
||||
@ -1010,7 +1010,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
|
||||
const file = tree.getFile(fileEventId);
|
||||
expect(file).toBeDefined();
|
||||
expect(file.indexEvent).toBe(fileEvent);
|
||||
expect(file!.indexEvent).toBe(fileEvent);
|
||||
});
|
||||
|
||||
it('should return falsy for unknown files', () => {
|
||||
|
@ -263,7 +263,7 @@ describe('Beacon', () => {
|
||||
roomId,
|
||||
);
|
||||
// less than the original event
|
||||
oldUpdateEvent.event.origin_server_ts = liveBeaconEvent.event.origin_server_ts - 1000;
|
||||
oldUpdateEvent.event.origin_server_ts = liveBeaconEvent.event.origin_server_ts! - 1000;
|
||||
|
||||
beacon.update(oldUpdateEvent);
|
||||
// didnt update
|
||||
|
@ -115,8 +115,53 @@ describe('MatrixEvent', () => {
|
||||
});
|
||||
|
||||
const prom = emitPromise(ev, MatrixEventEvent.VisibilityChange);
|
||||
ev.applyVisibilityEvent({ visible: false, eventId: ev.getId(), reason: null });
|
||||
ev.applyVisibilityEvent({ visible: false, eventId: ev.getId()!, reason: null });
|
||||
await prom;
|
||||
});
|
||||
});
|
||||
|
||||
describe(".attemptDecryption", () => {
|
||||
let encryptedEvent;
|
||||
const eventId = 'test_encrypted_event';
|
||||
|
||||
beforeEach(() => {
|
||||
encryptedEvent = new MatrixEvent({
|
||||
event_id: eventId,
|
||||
type: 'm.room.encrypted',
|
||||
content: {
|
||||
ciphertext: 'secrets',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should retry decryption if a retry is queued', async () => {
|
||||
const eventAttemptDecryptionSpy = jest.spyOn(encryptedEvent, 'attemptDecryption');
|
||||
|
||||
const crypto = {
|
||||
decryptEvent: jest.fn()
|
||||
.mockImplementationOnce(() => {
|
||||
// schedule a second decryption attempt while
|
||||
// the first one is still running.
|
||||
encryptedEvent.attemptDecryption(crypto);
|
||||
|
||||
const error = new Error("nope");
|
||||
error.name = 'DecryptionError';
|
||||
return Promise.reject(error);
|
||||
})
|
||||
.mockImplementationOnce(() => {
|
||||
return Promise.resolve({
|
||||
clearEvent: {
|
||||
type: 'm.room.message',
|
||||
},
|
||||
});
|
||||
}),
|
||||
};
|
||||
|
||||
await encryptedEvent.attemptDecryption(crypto);
|
||||
|
||||
expect(eventAttemptDecryptionSpy).toHaveBeenCalledTimes(2);
|
||||
expect(crypto.decryptEvent).toHaveBeenCalledTimes(2);
|
||||
expect(encryptedEvent.getType()).toEqual('m.room.message');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -140,11 +140,11 @@ describe.each([
|
||||
],
|
||||
});
|
||||
await flushAndRunTimersUntil(() => httpBackend.requests.length > 0);
|
||||
expect(httpBackend.flushSync(null, 1)).toEqual(1);
|
||||
expect(httpBackend.flushSync(undefined, 1)).toEqual(1);
|
||||
|
||||
await flushAndRunTimersUntil(() => httpBackend.requests.length > 0);
|
||||
|
||||
expect(httpBackend.flushSync(null, 1)).toEqual(1);
|
||||
expect(httpBackend.flushSync(undefined, 1)).toEqual(1);
|
||||
|
||||
// flush, as per comment in first test
|
||||
await flushPromises();
|
||||
@ -164,7 +164,7 @@ describe.each([
|
||||
],
|
||||
});
|
||||
await flushAndRunTimersUntil(() => httpBackend.requests.length > 0);
|
||||
expect(httpBackend.flushSync(null, 1)).toEqual(1);
|
||||
expect(httpBackend.flushSync(undefined, 1)).toEqual(1);
|
||||
|
||||
// Asserting that another request is never made is obviously
|
||||
// a bit tricky - we just flush the queue what should hopefully
|
||||
@ -200,7 +200,7 @@ describe.each([
|
||||
],
|
||||
});
|
||||
await flushAndRunTimersUntil(() => httpBackend.requests.length > 0);
|
||||
expect(httpBackend.flushSync(null, 1)).toEqual(1);
|
||||
expect(httpBackend.flushSync(undefined, 1)).toEqual(1);
|
||||
await flushPromises();
|
||||
|
||||
logger.info("Advancing clock to just before expected retry time...");
|
||||
@ -215,7 +215,7 @@ describe.each([
|
||||
jest.advanceTimersByTime(2000);
|
||||
await flushPromises();
|
||||
|
||||
expect(httpBackend.flushSync(null, 1)).toEqual(1);
|
||||
expect(httpBackend.flushSync(undefined, 1)).toEqual(1);
|
||||
});
|
||||
|
||||
it("retries on retryImmediately()", async function() {
|
||||
@ -223,7 +223,7 @@ describe.each([
|
||||
versions: ["r0.0.1"],
|
||||
});
|
||||
|
||||
await Promise.all([client.startClient(), httpBackend.flush(null, 1, 20)]);
|
||||
await Promise.all([client.startClient(), httpBackend.flush(undefined, 1, 20)]);
|
||||
|
||||
httpBackend.when(
|
||||
"PUT", "/sendToDevice/org.example.foo/",
|
||||
@ -239,13 +239,13 @@ describe.each([
|
||||
FAKE_MSG,
|
||||
],
|
||||
});
|
||||
expect(await httpBackend.flush(null, 1, 1)).toEqual(1);
|
||||
expect(await httpBackend.flush(undefined, 1, 1)).toEqual(1);
|
||||
await flushPromises();
|
||||
|
||||
client.retryImmediately();
|
||||
|
||||
// longer timeout here to try & avoid flakiness
|
||||
expect(await httpBackend.flush(null, 1, 3000)).toEqual(1);
|
||||
expect(await httpBackend.flush(undefined, 1, 3000)).toEqual(1);
|
||||
});
|
||||
|
||||
it("retries on when client is started", async function() {
|
||||
@ -269,13 +269,13 @@ describe.each([
|
||||
FAKE_MSG,
|
||||
],
|
||||
});
|
||||
expect(await httpBackend.flush(null, 1, 1)).toEqual(1);
|
||||
expect(await httpBackend.flush(undefined, 1, 1)).toEqual(1);
|
||||
await flushPromises();
|
||||
|
||||
client.stopClient();
|
||||
await Promise.all([client.startClient(), httpBackend.flush("/_matrix/client/versions", 1, 20)]);
|
||||
|
||||
expect(await httpBackend.flush(null, 1, 20)).toEqual(1);
|
||||
expect(await httpBackend.flush(undefined, 1, 20)).toEqual(1);
|
||||
});
|
||||
|
||||
it("retries when a message is retried", async function() {
|
||||
@ -283,7 +283,7 @@ describe.each([
|
||||
versions: ["r0.0.1"],
|
||||
});
|
||||
|
||||
await Promise.all([client.startClient(), httpBackend.flush(null, 1, 20)]);
|
||||
await Promise.all([client.startClient(), httpBackend.flush(undefined, 1, 20)]);
|
||||
|
||||
httpBackend.when(
|
||||
"PUT", "/sendToDevice/org.example.foo/",
|
||||
@ -300,7 +300,7 @@ describe.each([
|
||||
],
|
||||
});
|
||||
|
||||
expect(await httpBackend.flush(null, 1, 1)).toEqual(1);
|
||||
expect(await httpBackend.flush(undefined, 1, 1)).toEqual(1);
|
||||
await flushPromises();
|
||||
|
||||
const dummyEvent = new MatrixEvent({
|
||||
@ -311,7 +311,7 @@ describe.each([
|
||||
} as unknown as Room;
|
||||
client.resendEvent(dummyEvent, mockRoom);
|
||||
|
||||
expect(await httpBackend.flush(null, 1, 20)).toEqual(1);
|
||||
expect(await httpBackend.flush(undefined, 1, 20)).toEqual(1);
|
||||
});
|
||||
|
||||
it("splits many messages into multiple HTTP requests", async function() {
|
||||
|
@ -97,7 +97,7 @@ describe("Read receipt", () => {
|
||||
"POST", encodeUri("/rooms/$roomId/receipt/$receiptType/$eventId", {
|
||||
$roomId: ROOM_ID,
|
||||
$receiptType: ReceiptType.Read,
|
||||
$eventId: threadEvent.getId(),
|
||||
$eventId: threadEvent.getId()!,
|
||||
}),
|
||||
).check((request) => {
|
||||
expect(request.data.thread_id).toEqual(THREAD_ID);
|
||||
@ -115,7 +115,7 @@ describe("Read receipt", () => {
|
||||
"POST", encodeUri("/rooms/$roomId/receipt/$receiptType/$eventId", {
|
||||
$roomId: ROOM_ID,
|
||||
$receiptType: ReceiptType.Read,
|
||||
$eventId: roomEvent.getId(),
|
||||
$eventId: roomEvent.getId()!,
|
||||
}),
|
||||
).check((request) => {
|
||||
expect(request.data.thread_id).toEqual(MAIN_ROOM_TIMELINE);
|
||||
@ -133,7 +133,7 @@ describe("Read receipt", () => {
|
||||
"POST", encodeUri("/rooms/$roomId/receipt/$receiptType/$eventId", {
|
||||
$roomId: ROOM_ID,
|
||||
$receiptType: ReceiptType.Read,
|
||||
$eventId: threadEvent.getId(),
|
||||
$eventId: threadEvent.getId()!,
|
||||
}),
|
||||
).check((request) => {
|
||||
expect(request.data.thread_id).toBeUndefined();
|
||||
@ -151,7 +151,7 @@ describe("Read receipt", () => {
|
||||
"POST", encodeUri("/rooms/$roomId/receipt/$receiptType/$eventId", {
|
||||
$roomId: ROOM_ID,
|
||||
$receiptType: ReceiptType.Read,
|
||||
$eventId: threadEvent.getId(),
|
||||
$eventId: threadEvent.getId()!,
|
||||
}),
|
||||
).check((request) => {
|
||||
expect(request.data).toEqual({});
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2022 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 * as callbacks from "../../src/realtime-callbacks";
|
||||
|
||||
let wallTime = 1234567890;
|
||||
@ -37,7 +53,7 @@ describe("realtime-callbacks", function() {
|
||||
|
||||
it("should set 'this' to the global object", function() {
|
||||
let passed = false;
|
||||
const callback = function() {
|
||||
const callback = function(this: typeof global) {
|
||||
expect(this).toBe(global); // eslint-disable-line @typescript-eslint/no-invalid-this
|
||||
expect(this.console).toBeTruthy(); // eslint-disable-line @typescript-eslint/no-invalid-this
|
||||
passed = true;
|
||||
|
@ -22,7 +22,7 @@ import { TestClient } from "../TestClient";
|
||||
|
||||
describe("Relations", function() {
|
||||
it("should deduplicate annotations", function() {
|
||||
const room = new Room("room123", null, null);
|
||||
const room = new Room("room123", null!, null!);
|
||||
const relations = new Relations("m.annotation", "m.reaction", room);
|
||||
|
||||
// Create an instance of an annotation
|
||||
@ -99,7 +99,7 @@ describe("Relations", function() {
|
||||
|
||||
// Add the target event first, then the relation event
|
||||
{
|
||||
const room = new Room("room123", null, null);
|
||||
const room = new Room("room123", null!, null!);
|
||||
const relationsCreated = new Promise(resolve => {
|
||||
targetEvent.once(MatrixEventEvent.RelationsCreated, resolve);
|
||||
});
|
||||
@ -113,7 +113,7 @@ describe("Relations", function() {
|
||||
|
||||
// Add the relation event first, then the target event
|
||||
{
|
||||
const room = new Room("room123", null, null);
|
||||
const room = new Room("room123", null!, null!);
|
||||
const relationsCreated = new Promise(resolve => {
|
||||
targetEvent.once(MatrixEventEvent.RelationsCreated, resolve);
|
||||
});
|
||||
@ -127,7 +127,7 @@ describe("Relations", function() {
|
||||
});
|
||||
|
||||
it("should re-use Relations between all timeline sets in a room", async () => {
|
||||
const room = new Room("room123", null, null);
|
||||
const room = new Room("room123", null!, null!);
|
||||
const timelineSet1 = new EventTimelineSet(room);
|
||||
const timelineSet2 = new EventTimelineSet(room);
|
||||
expect(room.relations).toBe(timelineSet1.relations);
|
||||
@ -136,7 +136,7 @@ describe("Relations", function() {
|
||||
|
||||
it("should ignore m.replace for state events", async () => {
|
||||
const userId = "@bob:example.com";
|
||||
const room = new Room("room123", null, userId);
|
||||
const room = new Room("room123", null!, userId);
|
||||
const relations = new Relations("m.replace", "m.room.topic", room);
|
||||
|
||||
// Create an instance of a state event with rel_type m.replace
|
||||
|
@ -172,7 +172,7 @@ describe("RoomState", function() {
|
||||
state.on(RoomStateEvent.Members, function(ev, st, mem) {
|
||||
expect(ev).toEqual(memberEvents[emitCount]);
|
||||
expect(st).toEqual(state);
|
||||
expect(mem).toEqual(state.getMember(ev.getSender()));
|
||||
expect(mem).toEqual(state.getMember(ev.getSender()!));
|
||||
emitCount += 1;
|
||||
});
|
||||
state.setStateEvents(memberEvents);
|
||||
|
@ -24,7 +24,7 @@ import {
|
||||
DuplicateStrategy,
|
||||
EventStatus,
|
||||
EventTimelineSet,
|
||||
EventType,
|
||||
EventType, IStateEventWithRoomId,
|
||||
JoinRule,
|
||||
MatrixEvent,
|
||||
MatrixEventEvent,
|
||||
@ -66,7 +66,7 @@ describe("Room", function() {
|
||||
"body": "Reply :: " + Math.random(),
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": target.getId(),
|
||||
"event_id": target.getId()!,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -84,7 +84,7 @@ describe("Room", function() {
|
||||
},
|
||||
"m.relates_to": {
|
||||
rel_type: RelationType.Replace,
|
||||
event_id: target.getId(),
|
||||
event_id: target.getId()!,
|
||||
},
|
||||
},
|
||||
}, room.client);
|
||||
@ -97,9 +97,9 @@ describe("Room", function() {
|
||||
content: {
|
||||
"body": "Thread response :: " + Math.random(),
|
||||
"m.relates_to": {
|
||||
"event_id": root.getId(),
|
||||
"event_id": root.getId()!,
|
||||
"m.in_reply_to": {
|
||||
"event_id": root.getId(),
|
||||
"event_id": root.getId()!,
|
||||
},
|
||||
"rel_type": "m.thread",
|
||||
},
|
||||
@ -114,7 +114,7 @@ describe("Room", function() {
|
||||
content: {
|
||||
"m.relates_to": {
|
||||
"rel_type": RelationType.Annotation,
|
||||
"event_id": target.getId(),
|
||||
"event_id": target.getId()!,
|
||||
"key": Math.random().toString(),
|
||||
},
|
||||
},
|
||||
@ -125,7 +125,7 @@ describe("Room", function() {
|
||||
type: EventType.RoomRedaction,
|
||||
user: userA,
|
||||
room: roomId,
|
||||
redacts: target.getId(),
|
||||
redacts: target.getId()!,
|
||||
content: {},
|
||||
}, room.client);
|
||||
|
||||
@ -722,13 +722,13 @@ describe("Room", function() {
|
||||
it("should handle events in the same timeline", function() {
|
||||
room.addLiveEvents(events);
|
||||
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[0].getId(),
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[0].getId()!,
|
||||
events[1].getId()))
|
||||
.toBeLessThan(0);
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[2].getId(),
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[2].getId()!,
|
||||
events[1].getId()))
|
||||
.toBeGreaterThan(0);
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[1].getId(),
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[1].getId()!,
|
||||
events[1].getId()))
|
||||
.toEqual(0);
|
||||
});
|
||||
@ -741,10 +741,10 @@ describe("Room", function() {
|
||||
room.addEventsToTimeline([events[0]], false, oldTimeline);
|
||||
room.addLiveEvents([events[1]]);
|
||||
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[0].getId(),
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[0].getId()!,
|
||||
events[1].getId()))
|
||||
.toBeLessThan(0);
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[1].getId(),
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[1].getId()!,
|
||||
events[0].getId()))
|
||||
.toBeGreaterThan(0);
|
||||
});
|
||||
@ -755,10 +755,10 @@ describe("Room", function() {
|
||||
room.addEventsToTimeline([events[0]], false, oldTimeline);
|
||||
room.addLiveEvents([events[1]]);
|
||||
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[0].getId(),
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[0].getId()!,
|
||||
events[1].getId()))
|
||||
.toBe(null);
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[1].getId(),
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[1].getId()!,
|
||||
events[0].getId()))
|
||||
.toBe(null);
|
||||
});
|
||||
@ -767,13 +767,13 @@ describe("Room", function() {
|
||||
room.addLiveEvents(events);
|
||||
|
||||
expect(room.getUnfilteredTimelineSet()
|
||||
.compareEventOrdering(events[0].getId(), "xxx"))
|
||||
.compareEventOrdering(events[0].getId()!, "xxx"))
|
||||
.toBe(null);
|
||||
expect(room.getUnfilteredTimelineSet()
|
||||
.compareEventOrdering("xxx", events[0].getId()))
|
||||
.toBe(null);
|
||||
expect(room.getUnfilteredTimelineSet()
|
||||
.compareEventOrdering(events[0].getId(), events[0].getId()))
|
||||
.compareEventOrdering(events[0].getId()!, events[0].getId()))
|
||||
.toBe(0);
|
||||
});
|
||||
});
|
||||
@ -1228,7 +1228,7 @@ describe("Room", function() {
|
||||
it("should store the receipt so it can be obtained via getReceiptsForEvent", function() {
|
||||
const ts = 13787898424;
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(eventToAck.getId(), "m.read", userB, ts),
|
||||
mkRecord(eventToAck.getId()!, "m.read", userB, ts),
|
||||
]));
|
||||
expect(room.getReceiptsForEvent(eventToAck)).toEqual([{
|
||||
type: "m.read",
|
||||
@ -1247,7 +1247,7 @@ describe("Room", function() {
|
||||
const ts = 13787898424;
|
||||
|
||||
const receiptEvent = mkReceipt(roomId, [
|
||||
mkRecord(eventToAck.getId(), "m.read", userB, ts),
|
||||
mkRecord(eventToAck.getId()!, "m.read", userB, ts),
|
||||
]);
|
||||
|
||||
room.addReceipt(receiptEvent);
|
||||
@ -1261,11 +1261,11 @@ describe("Room", function() {
|
||||
});
|
||||
const ts = 13787898424;
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(eventToAck.getId(), "m.read", userB, ts),
|
||||
mkRecord(eventToAck.getId()!, "m.read", userB, ts),
|
||||
]));
|
||||
const ts2 = 13787899999;
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(nextEventToAck.getId(), "m.read", userB, ts2),
|
||||
mkRecord(nextEventToAck.getId()!, "m.read", userB, ts2),
|
||||
]));
|
||||
expect(room.getReceiptsForEvent(eventToAck)).toEqual([]);
|
||||
expect(room.getReceiptsForEvent(nextEventToAck)).toEqual([{
|
||||
@ -1280,9 +1280,9 @@ describe("Room", function() {
|
||||
it("should persist multiple receipts for a single event ID", function() {
|
||||
const ts = 13787898424;
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(eventToAck.getId(), "m.read", userB, ts),
|
||||
mkRecord(eventToAck.getId(), "m.read", userC, ts),
|
||||
mkRecord(eventToAck.getId(), "m.read", userD, ts),
|
||||
mkRecord(eventToAck.getId()!, "m.read", userB, ts),
|
||||
mkRecord(eventToAck.getId()!, "m.read", userC, ts),
|
||||
mkRecord(eventToAck.getId()!, "m.read", userD, ts),
|
||||
]));
|
||||
expect(room.getUsersReadUpTo(eventToAck)).toEqual(
|
||||
[userB, userC, userD],
|
||||
@ -1300,9 +1300,9 @@ describe("Room", function() {
|
||||
});
|
||||
const ts = 13787898424;
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(eventToAck.getId(), "m.read", userB, ts),
|
||||
mkRecord(eventTwo.getId(), "m.read", userC, ts),
|
||||
mkRecord(eventThree.getId(), "m.read", userD, ts),
|
||||
mkRecord(eventToAck.getId()!, "m.read", userB, ts),
|
||||
mkRecord(eventTwo.getId()!, "m.read", userC, ts),
|
||||
mkRecord(eventThree.getId()!, "m.read", userD, ts),
|
||||
]));
|
||||
expect(room.getUsersReadUpTo(eventToAck)).toEqual([userB]);
|
||||
expect(room.getUsersReadUpTo(eventTwo)).toEqual([userC]);
|
||||
@ -1311,9 +1311,9 @@ describe("Room", function() {
|
||||
|
||||
it("should persist multiple receipts for a single user ID", function() {
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(eventToAck.getId(), "m.delivered", userB, 13787898424),
|
||||
mkRecord(eventToAck.getId(), "m.read", userB, 22222222),
|
||||
mkRecord(eventToAck.getId(), "m.seen", userB, 33333333),
|
||||
mkRecord(eventToAck.getId()!, "m.delivered", userB, 13787898424),
|
||||
mkRecord(eventToAck.getId()!, "m.read", userB, 22222222),
|
||||
mkRecord(eventToAck.getId()!, "m.seen", userB, 33333333),
|
||||
]));
|
||||
expect(room.getReceiptsForEvent(eventToAck)).toEqual([
|
||||
{
|
||||
@ -1361,19 +1361,19 @@ describe("Room", function() {
|
||||
|
||||
// check it initialises correctly
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(events[0].getId(), "m.read", userB, ts),
|
||||
mkRecord(events[0].getId()!, "m.read", userB, ts),
|
||||
]));
|
||||
expect(room.getEventReadUpTo(userB)).toEqual(events[0].getId());
|
||||
|
||||
// 2>0, so it should move forward
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(events[2].getId(), "m.read", userB, ts),
|
||||
mkRecord(events[2].getId()!, "m.read", userB, ts),
|
||||
]));
|
||||
expect(room.getEventReadUpTo(userB)).toEqual(events[2].getId());
|
||||
|
||||
// 1<2, so it should stay put
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(events[1].getId(), "m.read", userB, ts),
|
||||
mkRecord(events[1].getId()!, "m.read", userB, ts),
|
||||
]));
|
||||
expect(room.getEventReadUpTo(userB)).toEqual(events[2].getId());
|
||||
});
|
||||
@ -1399,13 +1399,13 @@ describe("Room", function() {
|
||||
|
||||
// check it initialises correctly
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(events[0].getId(), "m.read", userB, ts),
|
||||
mkRecord(events[0].getId()!, "m.read", userB, ts),
|
||||
]));
|
||||
expect(room.getEventReadUpTo(userB)).toEqual(events[0].getId());
|
||||
|
||||
// 2>0, so it should move forward
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(events[2].getId(), "m.read", userB, ts),
|
||||
mkRecord(events[2].getId()!, "m.read", userB, ts),
|
||||
]), true);
|
||||
expect(room.getEventReadUpTo(userB)).toEqual(events[2].getId());
|
||||
expect(room.getReceiptsForEvent(events[2])).toEqual([
|
||||
@ -1414,7 +1414,7 @@ describe("Room", function() {
|
||||
|
||||
// 1<2, so it should stay put
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(events[1].getId(), "m.read", userB, ts),
|
||||
mkRecord(events[1].getId()!, "m.read", userB, ts),
|
||||
]));
|
||||
expect(room.getEventReadUpTo(userB)).toEqual(events[2].getId());
|
||||
expect(room.getEventReadUpTo(userB, true)).toEqual(events[1].getId());
|
||||
@ -1428,7 +1428,7 @@ describe("Room", function() {
|
||||
it("should return user IDs read up to the given event", function() {
|
||||
const ts = 13787898424;
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(eventToAck.getId(), "m.read", userB, ts),
|
||||
mkRecord(eventToAck.getId()!, "m.read", userB, ts),
|
||||
]));
|
||||
expect(room.getUsersReadUpTo(eventToAck)).toEqual([userB]);
|
||||
});
|
||||
@ -1438,9 +1438,9 @@ describe("Room", function() {
|
||||
it("should acknowledge if an event has been read", function() {
|
||||
const ts = 13787898424;
|
||||
room.addReceipt(mkReceipt(roomId, [
|
||||
mkRecord(eventToAck.getId(), "m.read", userB, ts),
|
||||
mkRecord(eventToAck.getId()!, "m.read", userB, ts),
|
||||
]));
|
||||
expect(room.hasUserReadEvent(userB, eventToAck.getId())).toEqual(true);
|
||||
expect(room.hasUserReadEvent(userB, eventToAck.getId()!)).toEqual(true);
|
||||
});
|
||||
it("return false for an unknown event", function() {
|
||||
expect(room.hasUserReadEvent(userB, "unknown_event")).toEqual(false);
|
||||
@ -1556,7 +1556,7 @@ describe("Room", function() {
|
||||
user: userA,
|
||||
type: EventType.RoomRedaction,
|
||||
content: {},
|
||||
redacts: eventA.getId(),
|
||||
redacts: eventA.getId()!,
|
||||
event: true,
|
||||
});
|
||||
redactA.status = EventStatus.SENDING;
|
||||
@ -1609,7 +1609,7 @@ describe("Room", function() {
|
||||
});
|
||||
|
||||
it("should remove cancelled events from the timeline", function() {
|
||||
const room = new Room(roomId, null, userA);
|
||||
const room = new Room(roomId, null!, userA);
|
||||
const eventA = utils.mkMessage({
|
||||
room: roomId, user: userA, event: true,
|
||||
});
|
||||
@ -1643,7 +1643,7 @@ describe("Room", function() {
|
||||
});
|
||||
|
||||
describe("loadMembersIfNeeded", function() {
|
||||
function createClientMock(serverResponse, storageResponse = null) {
|
||||
function createClientMock(serverResponse, storageResponse: MatrixEvent[] | Error | null = null) {
|
||||
return {
|
||||
getEventMapper: function() {
|
||||
// events should already be MatrixEvents
|
||||
@ -1664,7 +1664,7 @@ describe("Room", function() {
|
||||
}),
|
||||
store: {
|
||||
storageResponse,
|
||||
storedMembers: null,
|
||||
storedMembers: [] as IStateEventWithRoomId[] | null,
|
||||
getOutOfBandMembers: function() {
|
||||
if (this.storageResponse instanceof Error) {
|
||||
return Promise.reject(this.storageResponse);
|
||||
@ -1693,11 +1693,11 @@ describe("Room", function() {
|
||||
|
||||
it("should load members from server on first call", async function() {
|
||||
const client = createClientMock([memberEvent]);
|
||||
const room = new Room(roomId, client as any, null, { lazyLoadMembers: true });
|
||||
const room = new Room(roomId, client as any, null!, { lazyLoadMembers: true });
|
||||
await room.loadMembersIfNeeded();
|
||||
const memberA = room.getMember("@user_a:bar");
|
||||
const memberA = room.getMember("@user_a:bar")!;
|
||||
expect(memberA.name).toEqual("User A");
|
||||
const storedMembers = client.store.storedMembers;
|
||||
const storedMembers = client.store.storedMembers!;
|
||||
expect(storedMembers.length).toEqual(1);
|
||||
expect(storedMembers[0].event_id).toEqual(memberEvent.getId());
|
||||
});
|
||||
@ -1711,17 +1711,17 @@ describe("Room", function() {
|
||||
name: "Ms A",
|
||||
});
|
||||
const client = createClientMock([memberEvent2], [memberEvent]);
|
||||
const room = new Room(roomId, client as any, null, { lazyLoadMembers: true });
|
||||
const room = new Room(roomId, client as any, null!, { lazyLoadMembers: true });
|
||||
|
||||
await room.loadMembersIfNeeded();
|
||||
|
||||
const memberA = room.getMember("@user_a:bar");
|
||||
const memberA = room.getMember("@user_a:bar")!;
|
||||
expect(memberA.name).toEqual("User A");
|
||||
});
|
||||
|
||||
it("should allow retry on error", async function() {
|
||||
const client = createClientMock(new Error("server says no"));
|
||||
const room = new Room(roomId, client as any, null, { lazyLoadMembers: true });
|
||||
const room = new Room(roomId, client as any, null!, { lazyLoadMembers: true });
|
||||
let hasThrown = false;
|
||||
try {
|
||||
await room.loadMembersIfNeeded();
|
||||
@ -1740,27 +1740,68 @@ describe("Room", function() {
|
||||
describe("getMyMembership", function() {
|
||||
it("should return synced membership if membership isn't available yet",
|
||||
function() {
|
||||
const room = new Room(roomId, null, userA);
|
||||
const room = new Room(roomId, null!, userA);
|
||||
room.updateMyMembership(JoinRule.Invite);
|
||||
expect(room.getMyMembership()).toEqual(JoinRule.Invite);
|
||||
});
|
||||
it("should emit a Room.myMembership event on a change",
|
||||
function() {
|
||||
const room = new Room(roomId, null, userA);
|
||||
const events = [];
|
||||
room.on(RoomEvent.MyMembership, (_room, membership, oldMembership) => {
|
||||
events.push({ membership, oldMembership });
|
||||
});
|
||||
room.updateMyMembership(JoinRule.Invite);
|
||||
expect(room.getMyMembership()).toEqual(JoinRule.Invite);
|
||||
expect(events[0]).toEqual({ membership: "invite", oldMembership: undefined });
|
||||
events.splice(0); //clear
|
||||
room.updateMyMembership(JoinRule.Invite);
|
||||
expect(events.length).toEqual(0);
|
||||
room.updateMyMembership("join");
|
||||
expect(room.getMyMembership()).toEqual("join");
|
||||
expect(events[0]).toEqual({ membership: "join", oldMembership: "invite" });
|
||||
it("should emit a Room.myMembership event on a change", function() {
|
||||
const room = new Room(roomId, null!, userA);
|
||||
const events: {
|
||||
membership: string;
|
||||
oldMembership?: string;
|
||||
}[] = [];
|
||||
room.on(RoomEvent.MyMembership, (_room, membership, oldMembership) => {
|
||||
events.push({ membership, oldMembership });
|
||||
});
|
||||
room.updateMyMembership(JoinRule.Invite);
|
||||
expect(room.getMyMembership()).toEqual(JoinRule.Invite);
|
||||
expect(events[0]).toEqual({ membership: "invite", oldMembership: undefined });
|
||||
events.splice(0); //clear
|
||||
room.updateMyMembership(JoinRule.Invite);
|
||||
expect(events.length).toEqual(0);
|
||||
room.updateMyMembership("join");
|
||||
expect(room.getMyMembership()).toEqual("join");
|
||||
expect(events[0]).toEqual({ membership: "join", oldMembership: "invite" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("getDMInviter", () => {
|
||||
it("should delegate to RoomMember::getDMInviter if available", () => {
|
||||
const room = new Room(roomId, null!, userA);
|
||||
room.currentState.markOutOfBandMembersStarted();
|
||||
room.currentState.setOutOfBandMembers([
|
||||
new MatrixEvent({
|
||||
type: EventType.RoomMember,
|
||||
state_key: userA,
|
||||
sender: userB,
|
||||
content: {
|
||||
membership: "invite",
|
||||
is_direct: true,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(room.getDMInviter()).toBe(userB);
|
||||
});
|
||||
|
||||
it("should fall back to summary heroes and return the first one", () => {
|
||||
const room = new Room(roomId, null!, userA);
|
||||
room.updateMyMembership("invite");
|
||||
room.setSummary({
|
||||
"m.heroes": [userA, userC],
|
||||
"m.joined_member_count": 1,
|
||||
"m.invited_member_count": 1,
|
||||
});
|
||||
|
||||
expect(room.getDMInviter()).toBe(userC);
|
||||
});
|
||||
|
||||
it("should return undefined if we're not joined or invited to the room", () => {
|
||||
const room = new Room(roomId, null!, userA);
|
||||
expect(room.getDMInviter()).toBeUndefined();
|
||||
room.updateMyMembership("leave");
|
||||
expect(room.getDMInviter()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("guessDMUserId", function() {
|
||||
@ -1789,6 +1830,36 @@ describe("Room", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAvatarFallbackMember", () => {
|
||||
it("should should return undefined if the room isn't a 1:1", () => {
|
||||
const room = new Room(roomId, null!, userA);
|
||||
room.currentState.setJoinedMemberCount(2);
|
||||
room.currentState.setInvitedMemberCount(1);
|
||||
expect(room.getAvatarFallbackMember()).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should use summary heroes member if 1:1", () => {
|
||||
const room = new Room(roomId, null!, userA);
|
||||
room.currentState.markOutOfBandMembersStarted();
|
||||
room.currentState.setOutOfBandMembers([
|
||||
new MatrixEvent({
|
||||
type: EventType.RoomMember,
|
||||
state_key: userD,
|
||||
sender: userD,
|
||||
content: {
|
||||
membership: "join",
|
||||
},
|
||||
}),
|
||||
]);
|
||||
room.setSummary({
|
||||
"m.heroes": [userA, userD],
|
||||
"m.joined_member_count": 1,
|
||||
"m.invited_member_count": 1,
|
||||
});
|
||||
expect(room.getAvatarFallbackMember()?.userId).toBe(userD);
|
||||
});
|
||||
});
|
||||
|
||||
describe("maySendMessage", function() {
|
||||
it("should return false if synced membership not join", function() {
|
||||
const room = new Room(roomId, { isRoomEncrypted: () => false } as any, userA);
|
||||
@ -2118,7 +2189,7 @@ describe("Room", function() {
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => room.createThread(rootEvent.getId(), rootEvent, [])).not.toThrow();
|
||||
expect(() => room.createThread(rootEvent.getId()!, rootEvent, [])).not.toThrow();
|
||||
});
|
||||
|
||||
it("creating thread from edited event should not conflate old versions of the event", () => {
|
||||
@ -2339,7 +2410,7 @@ describe("Room", function() {
|
||||
const threadReaction2 = mkReaction(threadRoot);
|
||||
const threadReaction2Redaction = mkRedaction(threadReaction2);
|
||||
|
||||
const roots = new Set([threadRoot.getId()]);
|
||||
const roots = new Set([threadRoot.getId()!]);
|
||||
const events = [
|
||||
randomMessage,
|
||||
threadRoot,
|
||||
@ -2377,7 +2448,7 @@ describe("Room", function() {
|
||||
const threadReaction2 = mkReaction(threadResponse1);
|
||||
const threadReaction2Redaction = mkRedaction(threadReaction2);
|
||||
|
||||
const roots = new Set([threadRoot.getId()]);
|
||||
const roots = new Set([threadRoot.getId()!]);
|
||||
const events = [threadRoot, threadResponse1, threadReaction1, threadReaction2, threadReaction2Redaction];
|
||||
|
||||
expect(room.eventShouldLiveIn(threadReaction1, events, roots).shouldLiveInRoom).toBeFalsy();
|
||||
@ -2399,7 +2470,7 @@ describe("Room", function() {
|
||||
const reaction2 = mkReaction(reply1);
|
||||
const reaction2Redaction = mkRedaction(reply1);
|
||||
|
||||
const roots = new Set([threadRoot.getId()]);
|
||||
const roots = new Set([threadRoot.getId()!]);
|
||||
const events = [
|
||||
threadRoot,
|
||||
threadResponse1,
|
||||
@ -2425,7 +2496,7 @@ describe("Room", function() {
|
||||
const reply1 = mkReply(threadRoot);
|
||||
const reply2 = mkReply(reply1);
|
||||
|
||||
const roots = new Set([threadRoot.getId()]);
|
||||
const roots = new Set([threadRoot.getId()!]);
|
||||
const events = [
|
||||
threadRoot,
|
||||
threadResponse1,
|
||||
@ -2459,7 +2530,7 @@ describe("Room", function() {
|
||||
expect(thread.rootEvent).toBe(threadRoot);
|
||||
|
||||
const rootRelations = thread.timelineSet.relations.getChildEventsForEvent(
|
||||
threadRoot.getId(),
|
||||
threadRoot.getId()!,
|
||||
RelationType.Annotation,
|
||||
EventType.Reaction,
|
||||
)!.getSortedAnnotationsByKey();
|
||||
@ -2469,7 +2540,7 @@ describe("Room", function() {
|
||||
expect(rootRelations![0][1].has(rootReaction)).toBeTruthy();
|
||||
|
||||
const responseRelations = thread.timelineSet.relations.getChildEventsForEvent(
|
||||
threadResponse.getId(),
|
||||
threadResponse.getId()!,
|
||||
RelationType.Annotation,
|
||||
EventType.Reaction,
|
||||
)!.getSortedAnnotationsByKey();
|
||||
@ -2744,7 +2815,7 @@ describe("Room", function() {
|
||||
expect(pendingEvents[1].isBeingDecrypted()).toBeFalsy();
|
||||
expect(pendingEvents[1].isEncrypted()).toBeTruthy();
|
||||
for (const ev of pendingEvents) {
|
||||
expect(room.getPendingEvent(ev.getId())).toBe(ev);
|
||||
expect(room.getPendingEvent(ev.getId()!)).toBe(ev);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -160,10 +160,10 @@ describe("MatrixScheduler", function() {
|
||||
const eventD = utils.mkMessage({ user: "@b:bar", room: roomId, event: true });
|
||||
|
||||
const buckets = {};
|
||||
buckets[eventA.getId()] = "queue_A";
|
||||
buckets[eventD.getId()] = "queue_A";
|
||||
buckets[eventB.getId()] = "queue_B";
|
||||
buckets[eventC.getId()] = "queue_B";
|
||||
buckets[eventA.getId()!] = "queue_A";
|
||||
buckets[eventD.getId()!] = "queue_A";
|
||||
buckets[eventB.getId()!] = "queue_B";
|
||||
buckets[eventC.getId()!] = "queue_B";
|
||||
|
||||
retryFn = function() {
|
||||
return 0;
|
||||
|
@ -62,7 +62,7 @@ export class ReEmitter {
|
||||
if (!reEmittersByEvent) return; // We were never re-emitting these events in the first place
|
||||
|
||||
for (const eventName of eventNames) {
|
||||
source.off(eventName, reEmittersByEvent.get(eventName));
|
||||
source.off(eventName, reEmittersByEvent.get(eventName)!);
|
||||
reEmittersByEvent.delete(eventName);
|
||||
}
|
||||
|
||||
|
247
src/client.ts
247
src/client.ts
@ -59,7 +59,11 @@ import {
|
||||
retryNetworkOperation,
|
||||
ClientPrefix,
|
||||
MediaPrefix,
|
||||
IdentityPrefix, IHttpOpts, FileType, UploadResponse,
|
||||
IdentityPrefix,
|
||||
IHttpOpts,
|
||||
FileType,
|
||||
UploadResponse,
|
||||
HTTPError,
|
||||
} from "./http-api";
|
||||
import {
|
||||
Crypto,
|
||||
@ -909,25 +913,25 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
public static readonly RESTORE_BACKUP_ERROR_BAD_KEY = 'RESTORE_BACKUP_ERROR_BAD_KEY';
|
||||
|
||||
public reEmitter = new TypedReEmitter<EmittedEvents, ClientEventHandlerMap>(this);
|
||||
public olmVersion: [number, number, number] = null; // populated after initCrypto
|
||||
public olmVersion: [number, number, number] | null = null; // populated after initCrypto
|
||||
public usingExternalCrypto = false;
|
||||
public store: Store;
|
||||
public deviceId: string | null;
|
||||
public credentials: { userId?: string };
|
||||
public credentials: { userId: string | null };
|
||||
public pickleKey?: string;
|
||||
public scheduler: MatrixScheduler;
|
||||
public scheduler?: MatrixScheduler;
|
||||
public clientRunning = false;
|
||||
public timelineSupport = false;
|
||||
public urlPreviewCache: { [key: string]: Promise<IPreviewUrlResponse> } = {};
|
||||
public identityServer: IIdentityServerProvider;
|
||||
public identityServer?: IIdentityServerProvider;
|
||||
public http: MatrixHttpApi<IHttpOpts & { onlyData: true }>; // XXX: Intended private, used in code.
|
||||
public crypto?: Crypto; // XXX: Intended private, used in code.
|
||||
public cryptoCallbacks: ICryptoCallbacks; // XXX: Intended private, used in code.
|
||||
public callEventHandler: CallEventHandler; // XXX: Intended private, used in code.
|
||||
public callEventHandler?: CallEventHandler; // XXX: Intended private, used in code.
|
||||
public supportsCallTransfer = false; // XXX: Intended private, used in code.
|
||||
public forceTURN = false; // XXX: Intended private, used in code.
|
||||
public iceCandidatePoolSize = 0; // XXX: Intended private, used in code.
|
||||
public idBaseUrl: string;
|
||||
public idBaseUrl?: string;
|
||||
public baseUrl: string;
|
||||
|
||||
// Note: these are all `protected` to let downstream consumers make mistakes if they want to.
|
||||
@ -938,8 +942,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
protected isGuestAccount = false;
|
||||
protected ongoingScrollbacks: {[roomId: string]: {promise?: Promise<Room>, errorTs?: number}} = {};
|
||||
protected notifTimelineSet: EventTimelineSet | null = null;
|
||||
protected cryptoStore: CryptoStore;
|
||||
protected verificationMethods: VerificationMethod[];
|
||||
protected cryptoStore?: CryptoStore;
|
||||
protected verificationMethods?: VerificationMethod[];
|
||||
protected fallbackICEServerAllowed = false;
|
||||
protected roomList: RoomList;
|
||||
protected syncApi?: SlidingSyncSdk | SyncApi;
|
||||
@ -960,16 +964,16 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
// TODO: This should expire: https://github.com/matrix-org/matrix-js-sdk/issues/1020
|
||||
protected serverVersionsPromise?: Promise<IServerVersions>;
|
||||
|
||||
public cachedCapabilities: {
|
||||
public cachedCapabilities?: {
|
||||
capabilities: ICapabilities;
|
||||
expiration: number;
|
||||
};
|
||||
protected clientWellKnown: IClientWellKnown;
|
||||
protected clientWellKnownPromise: Promise<IClientWellKnown>;
|
||||
protected clientWellKnown?: IClientWellKnown;
|
||||
protected clientWellKnownPromise?: Promise<IClientWellKnown>;
|
||||
protected turnServers: ITurnServer[] = [];
|
||||
protected turnServersExpiry = 0;
|
||||
protected checkTurnServersIntervalID: ReturnType<typeof setInterval> | null = null;
|
||||
protected exportedOlmDeviceToImport: IExportedOlmDevice;
|
||||
protected checkTurnServersIntervalID?: ReturnType<typeof setInterval>;
|
||||
protected exportedOlmDeviceToImport?: IExportedOlmDevice;
|
||||
protected txnCtr = 0;
|
||||
protected mediaHandler = new MediaHandler(this);
|
||||
protected pendingEventEncryption = new Map<string, Promise<void>>();
|
||||
@ -1096,7 +1100,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
if (!utils.isSupportedReceiptType(key)) continue;
|
||||
if (!value) continue;
|
||||
|
||||
if (Object.keys(value).includes(this.getUserId())) return true;
|
||||
if (Object.keys(value).includes(this.getUserId()!)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -1117,14 +1121,13 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
|
||||
const event = events[i];
|
||||
|
||||
if (room.hasUserReadEvent(this.getUserId(), event.getId())) {
|
||||
if (room.hasUserReadEvent(this.getUserId()!, event.getId()!)) {
|
||||
// If the user has read the event, then the counting is done.
|
||||
break;
|
||||
}
|
||||
|
||||
const pushActions = this.getPushActionsForEvent(event);
|
||||
highlightCount += pushActions.tweaks &&
|
||||
pushActions.tweaks.highlight ? 1 : 0;
|
||||
highlightCount += pushActions?.tweaks?.highlight ? 1 : 0;
|
||||
}
|
||||
|
||||
// Note: we don't need to handle 'total' notifications because the counts
|
||||
@ -1238,10 +1241,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
this.peekSync?.stopPeeking();
|
||||
|
||||
this.callEventHandler?.stop();
|
||||
this.callEventHandler = null;
|
||||
this.callEventHandler = undefined;
|
||||
|
||||
global.clearInterval(this.checkTurnServersIntervalID);
|
||||
this.checkTurnServersIntervalID = null;
|
||||
this.checkTurnServersIntervalID = undefined;
|
||||
|
||||
if (this.clientWellKnownIntervalID !== undefined) {
|
||||
global.clearInterval(this.clientWellKnownIntervalID);
|
||||
@ -1334,7 +1337,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
* Get the current dehydrated device, if any
|
||||
* @return {Promise} A promise of an object containing the dehydrated device
|
||||
*/
|
||||
public async getDehydratedDevice(): Promise<IDehydratedDevice> {
|
||||
public async getDehydratedDevice(): Promise<IDehydratedDevice | undefined> {
|
||||
try {
|
||||
return await this.http.authedRequest<IDehydratedDevice>(
|
||||
Method.Get,
|
||||
@ -1345,7 +1348,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
logger.info("could not get dehydrated device", e.toString());
|
||||
logger.info("could not get dehydrated device", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1361,7 +1364,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
* dehydrated device.
|
||||
* @return {Promise} A promise that resolves when the dehydrated device is stored.
|
||||
*/
|
||||
public setDehydrationKey(
|
||||
public async setDehydrationKey(
|
||||
key: Uint8Array,
|
||||
keyInfo: IDehydratedDeviceKeyInfo,
|
||||
deviceDisplayName?: string,
|
||||
@ -1401,8 +1404,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
return;
|
||||
}
|
||||
return {
|
||||
userId: this.credentials.userId,
|
||||
deviceId: this.deviceId,
|
||||
userId: this.credentials.userId!,
|
||||
deviceId: this.deviceId!,
|
||||
// XXX: Private member access.
|
||||
olmDevice: await this.crypto.olmDevice.export(),
|
||||
};
|
||||
@ -1418,7 +1421,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
throw new Error("Cannot clear stores while client is running");
|
||||
}
|
||||
|
||||
const promises = [];
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
promises.push(this.store.deleteAllData());
|
||||
if (this.cryptoStore) {
|
||||
@ -1465,7 +1468,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
* Get the device ID of this client
|
||||
* @return {?string} device ID
|
||||
*/
|
||||
public getDeviceId(): string {
|
||||
public getDeviceId(): string | null {
|
||||
return this.deviceId;
|
||||
}
|
||||
|
||||
@ -1572,9 +1575,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
|
||||
/**
|
||||
* Return the provided scheduler, if any.
|
||||
* @return {?module:scheduler~MatrixScheduler} The scheduler or null
|
||||
* @return {?module:scheduler~MatrixScheduler} The scheduler or undefined
|
||||
*/
|
||||
public getScheduler(): MatrixScheduler {
|
||||
public getScheduler(): MatrixScheduler | undefined {
|
||||
return this.scheduler;
|
||||
}
|
||||
|
||||
@ -1626,10 +1629,12 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
}
|
||||
|
||||
return this.http.authedRequest(Method.Get, "/capabilities").catch((e: Error): void => {
|
||||
return this.http.authedRequest<{
|
||||
capabilities?: ICapabilities;
|
||||
}>(Method.Get, "/capabilities").catch((e: Error): void => {
|
||||
// We swallow errors because we need a default object anyhow
|
||||
logger.error(e);
|
||||
}).then((r: { capabilities?: ICapabilities } = {}) => {
|
||||
}).then((r = {}) => {
|
||||
const capabilities: ICapabilities = r["capabilities"] || {};
|
||||
|
||||
// If the capabilities missed the cache, cache it for a shorter amount
|
||||
@ -1703,7 +1708,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
this.store,
|
||||
this.cryptoStore,
|
||||
this.roomList,
|
||||
this.verificationMethods,
|
||||
this.verificationMethods!,
|
||||
);
|
||||
|
||||
this.reEmitter.reEmit(crypto, [
|
||||
@ -1919,7 +1924,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
*
|
||||
* @returns {module:crypto/verification/request/VerificationRequest?} the VerificationRequest that is in progress, if any
|
||||
*/
|
||||
public findVerificationRequestDMInProgress(roomId: string): VerificationRequest {
|
||||
public findVerificationRequestDMInProgress(roomId: string): VerificationRequest | undefined {
|
||||
if (!this.crypto) {
|
||||
throw new Error("End-to-end encryption disabled");
|
||||
}
|
||||
@ -2488,7 +2493,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
*
|
||||
* @return {Promise<module:crypto/deviceinfo?>}
|
||||
*/
|
||||
public async getEventSenderDeviceInfo(event: MatrixEvent): Promise<DeviceInfo> {
|
||||
public async getEventSenderDeviceInfo(event: MatrixEvent): Promise<DeviceInfo | null> {
|
||||
if (!this.crypto) {
|
||||
return null;
|
||||
}
|
||||
@ -2519,7 +2524,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
* @return {Promise} A promise that will resolve when the key request is queued
|
||||
*/
|
||||
public cancelAndResendEventRoomKeyRequest(event: MatrixEvent): Promise<void> {
|
||||
return event.cancelAndResendKeyRequest(this.crypto, this.getUserId());
|
||||
if (!this.crypto) {
|
||||
throw new Error("End-to-End encryption disabled");
|
||||
}
|
||||
return event.cancelAndResendKeyRequest(this.crypto, this.getUserId()!);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2857,10 +2865,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
private makeKeyBackupPath(roomId: string, sessionId: undefined, version?: string): IKeyBackupPath;
|
||||
private makeKeyBackupPath(roomId: string, sessionId: string, version?: string): IKeyBackupPath;
|
||||
private makeKeyBackupPath(roomId?: string, sessionId?: string, version?: string): IKeyBackupPath {
|
||||
let path;
|
||||
let path: string;
|
||||
if (sessionId !== undefined) {
|
||||
path = utils.encodeUri("/room_keys/keys/$roomId/$sessionId", {
|
||||
$roomId: roomId,
|
||||
$roomId: roomId!,
|
||||
$sessionId: sessionId,
|
||||
});
|
||||
} else if (roomId !== undefined) {
|
||||
@ -2911,7 +2919,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
throw new Error("End-to-end encryption disabled");
|
||||
}
|
||||
|
||||
const path = this.makeKeyBackupPath(roomId, sessionId, version);
|
||||
const path = this.makeKeyBackupPath(roomId!, sessionId!, version!);
|
||||
await this.http.authedRequest(
|
||||
Method.Put, path.path, path.queryData, data,
|
||||
{ prefix: ClientPrefix.V3 },
|
||||
@ -3021,9 +3029,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
opts: IKeyBackupRestoreOpts,
|
||||
): Promise<IKeyBackupRestoreResult> {
|
||||
const privKey = await keyFromAuthData(backupInfo.auth_data, password);
|
||||
return this.restoreKeyBackup(
|
||||
privKey, targetRoomId, targetSessionId, backupInfo, opts,
|
||||
);
|
||||
return this.restoreKeyBackup(privKey, targetRoomId!, targetSessionId!, backupInfo, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3059,9 +3065,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
|
||||
const privKey = decodeBase64(fixedKey || storedKey!);
|
||||
return this.restoreKeyBackup(
|
||||
privKey, targetRoomId, targetSessionId, backupInfo, opts,
|
||||
);
|
||||
return this.restoreKeyBackup(privKey, targetRoomId!, targetSessionId!, backupInfo, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3134,11 +3138,14 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
backupInfo: IKeyBackupInfo,
|
||||
opts?: IKeyBackupRestoreOpts,
|
||||
): Promise<IKeyBackupRestoreResult> {
|
||||
if (!this.crypto) {
|
||||
throw new Error("End-to-end encryption disabled");
|
||||
}
|
||||
const privKey = await this.crypto.getSessionBackupPrivateKey();
|
||||
if (!privKey) {
|
||||
throw new Error("Couldn't get key");
|
||||
}
|
||||
return this.restoreKeyBackup(privKey, targetRoomId, targetSessionId, backupInfo, opts);
|
||||
return this.restoreKeyBackup(privKey, targetRoomId!, targetSessionId!, backupInfo, opts);
|
||||
}
|
||||
|
||||
private async restoreKeyBackup(
|
||||
@ -3179,7 +3186,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
let totalKeyCount = 0;
|
||||
let keys: IMegolmSessionData[] = [];
|
||||
|
||||
const path = this.makeKeyBackupPath(targetRoomId, targetSessionId, backupInfo.version);
|
||||
const path = this.makeKeyBackupPath(targetRoomId!, targetSessionId!, backupInfo.version);
|
||||
|
||||
const algorithm = await BackupManager.makeAlgorithm(backupInfo, async () => { return privKey; });
|
||||
|
||||
@ -3228,16 +3235,16 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
totalKeyCount = Object.keys(sessions).length;
|
||||
keys = await algorithm.decryptSessions(sessions);
|
||||
for (const k of keys) {
|
||||
k.room_id = targetRoomId;
|
||||
k.room_id = targetRoomId!;
|
||||
}
|
||||
} else {
|
||||
totalKeyCount = 1;
|
||||
try {
|
||||
const [key] = await algorithm.decryptSessions({
|
||||
[targetSessionId]: res as IKeyBackupSession,
|
||||
[targetSessionId!]: res as IKeyBackupSession,
|
||||
});
|
||||
key.room_id = targetRoomId;
|
||||
key.session_id = targetSessionId;
|
||||
key.room_id = targetRoomId!;
|
||||
key.session_id = targetSessionId!;
|
||||
keys.push(key);
|
||||
} catch (e) {
|
||||
logger.log("Failed to decrypt megolm session from backup", e);
|
||||
@ -3443,7 +3450,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
try {
|
||||
return await this.http.authedRequest(Method.Get, path);
|
||||
} catch (e) {
|
||||
if (e.data?.errcode === 'M_NOT_FOUND') {
|
||||
if ((<MatrixError>e).data?.errcode === 'M_NOT_FOUND') {
|
||||
return null;
|
||||
}
|
||||
throw e;
|
||||
@ -3527,9 +3534,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
|
||||
const path = utils.encodeUri("/join/$roomid", { $roomid: roomIdOrAlias });
|
||||
const res = await this.http.authedRequest(Method.Post, path, queryString, data);
|
||||
const res = await this.http.authedRequest<{ room_id: string }>(Method.Post, path, queryString, data);
|
||||
|
||||
const roomId = res['room_id'];
|
||||
const roomId = res.room_id;
|
||||
const syncApi = new SyncApi(this, this.clientOpts);
|
||||
const room = syncApi.createRoom(roomId);
|
||||
if (opts.syncRoom) {
|
||||
@ -3571,7 +3578,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
|
||||
// if the event is currently being encrypted then
|
||||
if (event.status === EventStatus.ENCRYPTING) {
|
||||
this.pendingEventEncryption.delete(event.getId());
|
||||
this.pendingEventEncryption.delete(event.getId()!);
|
||||
} else if (this.scheduler && event.status === EventStatus.QUEUED) {
|
||||
// tell the scheduler to forget about it, if it's queued
|
||||
this.scheduler.removeEventFromQueue(event);
|
||||
@ -3843,7 +3850,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
if (targetId?.startsWith("~")) {
|
||||
const target = room?.getPendingEvents().find(e => e.getId() === targetId);
|
||||
target?.once(MatrixEventEvent.LocalEventIdReplaced, () => {
|
||||
localEvent.updateAssociatedId(target.getId());
|
||||
localEvent.updateAssociatedId(target.getId()!);
|
||||
});
|
||||
}
|
||||
|
||||
@ -3882,10 +3889,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
const encryptionPromise = this.encryptEventIfNeeded(event, room ?? undefined);
|
||||
if (!encryptionPromise) return null; // doesn't need encryption
|
||||
|
||||
this.pendingEventEncryption.set(event.getId(), encryptionPromise);
|
||||
this.pendingEventEncryption.set(event.getId()!, encryptionPromise);
|
||||
this.updatePendingEventStatus(room, event, EventStatus.ENCRYPTING);
|
||||
return encryptionPromise.then(() => {
|
||||
if (!this.pendingEventEncryption.has(event.getId())) {
|
||||
if (!this.pendingEventEncryption.has(event.getId()!)) {
|
||||
// cancelled via MatrixClient::cancelPendingEvent
|
||||
cancelled = true;
|
||||
return;
|
||||
@ -3951,7 +3958,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this.isRoomEncrypted(event.getRoomId())) {
|
||||
if (!this.isRoomEncrypted(event.getRoomId()!)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -3993,7 +4000,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
* @param {string} eventType the event type
|
||||
* @return {string} the event type taking encryption into account
|
||||
*/
|
||||
private getEncryptedIfNeededEventType(roomId: string, eventType: string): string {
|
||||
private getEncryptedIfNeededEventType(
|
||||
roomId: string,
|
||||
eventType?: EventType | string | null,
|
||||
): EventType | string | null | undefined {
|
||||
if (eventType === EventType.Reaction) return eventType;
|
||||
return this.isRoomEncrypted(roomId) ? EventType.RoomMessageEncrypted : eventType;
|
||||
}
|
||||
@ -4014,9 +4024,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
|
||||
const pathParams = {
|
||||
$roomId: event.getRoomId(),
|
||||
$roomId: event.getRoomId()!,
|
||||
$eventType: event.getWireType(),
|
||||
$stateKey: event.getStateKey(),
|
||||
$stateKey: event.getStateKey()!,
|
||||
$txnId: txnId,
|
||||
};
|
||||
|
||||
@ -4024,15 +4034,16 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
|
||||
if (event.isState()) {
|
||||
let pathTemplate = "/rooms/$roomId/state/$eventType";
|
||||
if (event.getStateKey() && event.getStateKey().length > 0) {
|
||||
if (event.getStateKey() && event.getStateKey()!.length > 0) {
|
||||
pathTemplate = "/rooms/$roomId/state/$eventType/$stateKey";
|
||||
}
|
||||
path = utils.encodeUri(pathTemplate, pathParams);
|
||||
} else if (event.isRedaction()) {
|
||||
const pathTemplate = `/rooms/$roomId/redact/$redactsEventId/$txnId`;
|
||||
path = utils.encodeUri(pathTemplate, Object.assign({
|
||||
$redactsEventId: event.event.redacts,
|
||||
}, pathParams));
|
||||
path = utils.encodeUri(pathTemplate, {
|
||||
$redactsEventId: event.event.redacts!,
|
||||
...pathParams,
|
||||
});
|
||||
} else {
|
||||
path = utils.encodeUri("/rooms/$roomId/send/$eventType/$txnId", pathParams);
|
||||
}
|
||||
@ -4386,7 +4397,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
body = threadId;
|
||||
threadId = null;
|
||||
}
|
||||
const content = ContentHelpers.makeHtmlMessage(body, htmlBody);
|
||||
const content = ContentHelpers.makeHtmlMessage(body, htmlBody!);
|
||||
return this.sendMessage(roomId, threadId, content);
|
||||
}
|
||||
|
||||
@ -4419,7 +4430,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
body = threadId;
|
||||
threadId = null;
|
||||
}
|
||||
const content = ContentHelpers.makeHtmlNotice(body, htmlBody);
|
||||
const content = ContentHelpers.makeHtmlNotice(body, htmlBody!);
|
||||
return this.sendMessage(roomId, threadId, content);
|
||||
}
|
||||
|
||||
@ -4453,7 +4464,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
body = threadId;
|
||||
threadId = null;
|
||||
}
|
||||
const content = ContentHelpers.makeHtmlEmote(body, htmlBody);
|
||||
const content = ContentHelpers.makeHtmlEmote(body, htmlBody!);
|
||||
return this.sendMessage(roomId, threadId, content);
|
||||
}
|
||||
|
||||
@ -4476,9 +4487,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
|
||||
const path = utils.encodeUri("/rooms/$roomId/receipt/$receiptType/$eventId", {
|
||||
$roomId: event.getRoomId(),
|
||||
$roomId: event.getRoomId()!,
|
||||
$receiptType: receiptType,
|
||||
$eventId: event.getId(),
|
||||
$eventId: event.getId()!,
|
||||
});
|
||||
|
||||
// TODO: Add a check for which spec version this will be released in
|
||||
@ -4489,7 +4500,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
: MAIN_ROOM_TIMELINE;
|
||||
}
|
||||
|
||||
const promise = this.http.authedRequest(Method.Post, path, undefined, body || {});
|
||||
const promise = this.http.authedRequest<{}>(Method.Post, path, undefined, body || {});
|
||||
|
||||
const room = this.getRoom(event.getRoomId());
|
||||
if (room && this.credentials.userId) {
|
||||
@ -4510,9 +4521,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
receiptType = ReceiptType.Read,
|
||||
): Promise<{} | undefined> {
|
||||
if (!event) return;
|
||||
const eventId = event.getId();
|
||||
const eventId = event.getId()!;
|
||||
const room = this.getRoom(event.getRoomId());
|
||||
if (room && room.hasPendingEvent(eventId)) {
|
||||
if (room?.hasPendingEvent(eventId)) {
|
||||
throw new Error(`Cannot set read receipt to a pending event (${eventId})`);
|
||||
}
|
||||
|
||||
@ -4545,23 +4556,23 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
|
||||
// Add the optional RR update, do local echo like `sendReceipt`
|
||||
let rrEventId: string;
|
||||
let rrEventId: string | undefined;
|
||||
if (rrEvent) {
|
||||
rrEventId = rrEvent.getId();
|
||||
rrEventId = rrEvent.getId()!;
|
||||
if (room?.hasPendingEvent(rrEventId)) {
|
||||
throw new Error(`Cannot set read receipt to a pending event (${rrEventId})`);
|
||||
}
|
||||
room?.addLocalEchoReceipt(this.credentials.userId, rrEvent, ReceiptType.Read);
|
||||
room?.addLocalEchoReceipt(this.credentials.userId!, rrEvent, ReceiptType.Read);
|
||||
}
|
||||
|
||||
// Add the optional private RR update, do local echo like `sendReceipt`
|
||||
let rpEventId: string;
|
||||
let rpEventId: string | undefined;
|
||||
if (rpEvent) {
|
||||
rpEventId = rpEvent.getId();
|
||||
rpEventId = rpEvent.getId()!;
|
||||
if (room?.hasPendingEvent(rpEventId)) {
|
||||
throw new Error(`Cannot set read receipt to a pending event (${rpEventId})`);
|
||||
}
|
||||
room?.addLocalEchoReceipt(this.credentials.userId, rpEvent, ReceiptType.ReadPrivate);
|
||||
room?.addLocalEchoReceipt(this.credentials.userId!, rpEvent, ReceiptType.ReadPrivate);
|
||||
}
|
||||
|
||||
return await this.setRoomReadMarkersHttpRequest(roomId, rmEventId, rrEventId, rpEventId);
|
||||
@ -4623,7 +4634,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
|
||||
const path = utils.encodeUri("/rooms/$roomId/typing/$userId", {
|
||||
$roomId: roomId,
|
||||
$userId: this.credentials.userId,
|
||||
$userId: this.getUserId()!,
|
||||
});
|
||||
const data: any = {
|
||||
typing: isTyping,
|
||||
@ -4816,7 +4827,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
|
||||
const doLeave = (roomId: string) => {
|
||||
return this.leave(roomId).then(() => {
|
||||
populationResults[roomId] = null;
|
||||
delete populationResults[roomId];
|
||||
}).catch((err) => {
|
||||
populationResults[roomId] = err;
|
||||
return null; // suppress error
|
||||
@ -4925,7 +4936,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
* Useful when an event just got decrypted
|
||||
* @return {module:pushprocessor~PushAction} A dict of actions to perform.
|
||||
*/
|
||||
public getPushActionsForEvent(event: MatrixEvent, forceRecalculate = false): IActionsObject {
|
||||
public getPushActionsForEvent(event: MatrixEvent, forceRecalculate = false): IActionsObject | null {
|
||||
if (!event.getPushActions() || forceRecalculate) {
|
||||
event.setPushActions(this.pushProcessor.actionsForEvent(event));
|
||||
}
|
||||
@ -4943,7 +4954,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
public setProfileInfo(info: "displayname", data: { displayname: string }): Promise<{}>;
|
||||
public setProfileInfo(info: "avatar_url" | "displayname", data: object): Promise<{}> {
|
||||
const path = utils.encodeUri("/profile/$userId/$info", {
|
||||
$userId: this.credentials.userId,
|
||||
$userId: this.credentials.userId!,
|
||||
$info: info,
|
||||
});
|
||||
return this.http.authedRequest(Method.Put, path, undefined, data);
|
||||
@ -4957,7 +4968,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
public async setDisplayName(name: string): Promise<{}> {
|
||||
const prom = await this.setProfileInfo("displayname", { displayname: name });
|
||||
// XXX: synthesise a profile update for ourselves because Synapse is broken and won't
|
||||
const user = this.getUser(this.getUserId());
|
||||
const user = this.getUser(this.getUserId()!);
|
||||
if (user) {
|
||||
user.displayName = name;
|
||||
user.emit(UserEvent.DisplayName, user.events.presence, user);
|
||||
@ -4973,7 +4984,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
public async setAvatarUrl(url: string): Promise<{}> {
|
||||
const prom = await this.setProfileInfo("avatar_url", { avatar_url: url });
|
||||
// XXX: synthesise a profile update for ourselves because Synapse is broken and won't
|
||||
const user = this.getUser(this.getUserId());
|
||||
const user = this.getUser(this.getUserId()!);
|
||||
if (user) {
|
||||
user.avatarUrl = url;
|
||||
user.emit(UserEvent.AvatarUrl, user.events.presence, user);
|
||||
@ -5227,7 +5238,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
|
||||
// Here we handle non-thread timelines only, but still process any thread events to populate thread summaries.
|
||||
let timeline = timelineSet.getTimelineForEvent(events[0].getId());
|
||||
let timeline = timelineSet.getTimelineForEvent(events[0].getId()!);
|
||||
if (timeline) {
|
||||
timeline.getState(EventTimeline.BACKWARDS).setUnknownStateEvents(res.state.map(mapper));
|
||||
} else {
|
||||
@ -5379,7 +5390,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
params.from = fromToken;
|
||||
}
|
||||
|
||||
let filter: IRoomEventFilter | null = null;
|
||||
let filter: IRoomEventFilter = {};
|
||||
if (this.clientOpts?.lazyLoadMembers) {
|
||||
// create a shallow copy of LAZY_LOADING_MESSAGES_FILTER,
|
||||
// so the timelineFilter doesn't get written into it below
|
||||
@ -5396,7 +5407,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
...timelineFilter.getRoomTimelineFilterComponent()?.toJSON(),
|
||||
};
|
||||
}
|
||||
if (filter) {
|
||||
if (Object.keys(filter).length) {
|
||||
params.filter = JSON.stringify(filter);
|
||||
}
|
||||
|
||||
@ -5941,7 +5952,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
if (!mute) {
|
||||
// Remove the rule only if it is a muting rule
|
||||
if (hasDontNotifyRule) {
|
||||
promise = this.deletePushRule(scope, PushRuleKind.RoomSpecific, roomPushRule.rule_id);
|
||||
promise = this.deletePushRule(scope, PushRuleKind.RoomSpecific, roomPushRule!.rule_id);
|
||||
}
|
||||
} else {
|
||||
if (!roomPushRule) {
|
||||
@ -6078,14 +6089,14 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
|
||||
const searchOpts = {
|
||||
body: searchResults._query,
|
||||
body: searchResults._query!,
|
||||
next_batch: searchResults.next_batch,
|
||||
};
|
||||
|
||||
const promise = this.search(searchOpts)
|
||||
.then(res => this.processRoomEventsSearch(searchResults, res))
|
||||
.finally(() => {
|
||||
searchResults.pendingRequest = null;
|
||||
searchResults.pendingRequest = undefined;
|
||||
});
|
||||
searchResults.pendingRequest = promise;
|
||||
|
||||
@ -6127,7 +6138,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
if (room) {
|
||||
// Copy over a known event sender if we can
|
||||
for (const ev of sr.context.getTimeline()) {
|
||||
const sender = room.getMember(ev.getSender());
|
||||
const sender = room.getMember(ev.getSender()!);
|
||||
if (!ev.sender && sender) ev.sender = sender;
|
||||
}
|
||||
}
|
||||
@ -6172,7 +6183,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
*/
|
||||
public createFilter(content: IFilterDefinition): Promise<Filter> {
|
||||
const path = utils.encodeUri("/user/$userId/filter", {
|
||||
$userId: this.credentials.userId,
|
||||
$userId: this.credentials.userId!,
|
||||
});
|
||||
return this.http.authedRequest<IFilterResponse>(Method.Post, path, undefined, content)
|
||||
.then((response) => {
|
||||
@ -6264,7 +6275,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
// debuglog("Created new filter ID %s: %s", createdFilter.filterId,
|
||||
// JSON.stringify(createdFilter.getDefinition()));
|
||||
this.store.setFilterIdByName(filterName, createdFilter.filterId);
|
||||
return createdFilter.filterId;
|
||||
return createdFilter.filterId!;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -6276,7 +6287,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
*/
|
||||
public getOpenIdToken(): Promise<IOpenIDToken> {
|
||||
const path = utils.encodeUri("/user/$userId/openid/request_token", {
|
||||
$userId: this.credentials.userId,
|
||||
$userId: this.credentials.userId!,
|
||||
});
|
||||
|
||||
return this.http.authedRequest(Method.Post, path, undefined, {});
|
||||
@ -6284,7 +6295,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
|
||||
private startCallEventHandler = (): void => {
|
||||
if (this.isInitialSyncComplete()) {
|
||||
this.callEventHandler.start();
|
||||
this.callEventHandler?.start();
|
||||
this.off(ClientEvent.Sync, this.startCallEventHandler);
|
||||
}
|
||||
};
|
||||
@ -6308,18 +6319,18 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
/**
|
||||
* Get the unix timestamp (in milliseconds) at which the current
|
||||
* TURN credentials (from getTurnServers) expire
|
||||
* @return {number} The expiry timestamp, in milliseconds, or null if no credentials
|
||||
* @return {number} The expiry timestamp in milliseconds
|
||||
*/
|
||||
public getTurnServersExpiry(): number | null {
|
||||
public getTurnServersExpiry(): number {
|
||||
return this.turnServersExpiry;
|
||||
}
|
||||
|
||||
public get pollingTurnServers(): boolean {
|
||||
return this.checkTurnServersIntervalID !== null;
|
||||
return this.checkTurnServersIntervalID !== undefined;
|
||||
}
|
||||
|
||||
// XXX: Intended private, used in code.
|
||||
public async checkTurnServers(): Promise<boolean> {
|
||||
public async checkTurnServers(): Promise<boolean | undefined> {
|
||||
if (!this.canSupportVoip) {
|
||||
return;
|
||||
}
|
||||
@ -6349,15 +6360,15 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error("Failed to get TURN URIs", err);
|
||||
if (err.httpStatus === 403) {
|
||||
if ((<HTTPError>err).httpStatus === 403) {
|
||||
// We got a 403, so there's no point in looping forever.
|
||||
logger.info("TURN access unavailable for this account: stopping credentials checks");
|
||||
if (this.checkTurnServersIntervalID !== null) global.clearInterval(this.checkTurnServersIntervalID);
|
||||
this.checkTurnServersIntervalID = null;
|
||||
this.emit(ClientEvent.TurnServersError, err, true); // fatal
|
||||
this.checkTurnServersIntervalID = undefined;
|
||||
this.emit(ClientEvent.TurnServersError, <HTTPError>err, true); // fatal
|
||||
} else {
|
||||
// otherwise, if we failed for whatever reason, try again the next time we're called.
|
||||
this.emit(ClientEvent.TurnServersError, err, false); // non-fatal
|
||||
this.emit(ClientEvent.TurnServersError, <Error>err, false); // non-fatal
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6399,9 +6410,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
"/_synapse/admin/v1/users/$userId/admin",
|
||||
{ $userId: this.getUserId()! },
|
||||
);
|
||||
return this.http.authedRequest(
|
||||
return this.http.authedRequest<{ admin: boolean }>(
|
||||
Method.Get, path, undefined, undefined, { prefix: '' },
|
||||
).then(r => r['admin']); // pull out the specific boolean we want
|
||||
).then(r => r.admin); // pull out the specific boolean we want
|
||||
}
|
||||
|
||||
/**
|
||||
@ -6441,12 +6452,15 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
this.emit(ClientEvent.ClientWellKnown, this.clientWellKnown);
|
||||
}
|
||||
|
||||
public getClientWellKnown(): IClientWellKnown {
|
||||
public getClientWellKnown(): IClientWellKnown | undefined {
|
||||
return this.clientWellKnown;
|
||||
}
|
||||
|
||||
public waitForClientWellKnown(): Promise<IClientWellKnown> {
|
||||
return this.clientWellKnownPromise;
|
||||
if (!this.clientRunning) {
|
||||
throw new Error("Client is not running");
|
||||
}
|
||||
return this.clientWellKnownPromise!;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -6817,9 +6831,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
* @param {boolean} stripProto whether or not to strip the protocol from the URL
|
||||
* @return {string} Identity server URL of this client
|
||||
*/
|
||||
public getIdentityServerUrl(stripProto = false): string {
|
||||
if (stripProto && (this.idBaseUrl.startsWith("http://") ||
|
||||
this.idBaseUrl.startsWith("https://"))) {
|
||||
public getIdentityServerUrl(stripProto = false): string | undefined {
|
||||
if (stripProto && (this.idBaseUrl?.startsWith("http://") || this.idBaseUrl?.startsWith("https://"))) {
|
||||
return this.idBaseUrl.split("://")[1];
|
||||
}
|
||||
return this.idBaseUrl;
|
||||
@ -7286,8 +7299,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
templatedUrl + "?" + queryString, {
|
||||
$roomId: roomId,
|
||||
$eventId: eventId,
|
||||
$relationType: relationType,
|
||||
$eventType: eventType,
|
||||
$relationType: relationType!,
|
||||
$eventType: eventType!,
|
||||
});
|
||||
return this.http.authedRequest(
|
||||
Method.Get, path, undefined, undefined, {
|
||||
@ -7453,8 +7466,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
public async setRoomReadMarkersHttpRequest(
|
||||
roomId: string,
|
||||
rmEventId: string,
|
||||
rrEventId: string,
|
||||
rpEventId: string,
|
||||
rrEventId?: string,
|
||||
rpEventId?: string,
|
||||
): Promise<{}> {
|
||||
const path = utils.encodeUri("/rooms/$roomId/read_markers", {
|
||||
$roomId: roomId,
|
||||
@ -9021,8 +9034,8 @@ export function fixNotificationCountOnDecryption(cli: MatrixClient, event: Matri
|
||||
// TODO: Handle mentions received while the client is offline
|
||||
// See also https://github.com/vector-im/element-web/issues/9069
|
||||
const hasReadEvent = isThreadEvent
|
||||
? room.getThread(event.threadRootId)?.hasUserReadEvent(cli.getUserId()!, event.getId())
|
||||
: room.hasUserReadEvent(cli.getUserId()!, event.getId());
|
||||
? room.getThread(event.threadRootId)?.hasUserReadEvent(cli.getUserId()!, event.getId()!)
|
||||
: room.hasUserReadEvent(cli.getUserId()!, event.getId()!);
|
||||
|
||||
if (!hasReadEvent) {
|
||||
let newCount = currentCount;
|
||||
|
@ -139,8 +139,7 @@ export const getTextForLocationEvent = (
|
||||
/**
|
||||
* Generates the content for a Location event
|
||||
* @param uri a geo:// uri for the location
|
||||
* @param timestamp the timestamp when the location was correct (milliseconds since
|
||||
* the UNIX epoch)
|
||||
* @param timestamp the timestamp when the location was correct (milliseconds since the UNIX epoch)
|
||||
* @param description the (optional) label for this location on the map
|
||||
* @param assetType the (optional) asset type of this location e.g. "m.self"
|
||||
* @param text optional. A text for the location
|
||||
@ -150,7 +149,7 @@ export const makeLocationContent = (
|
||||
// to avoid a breaking change
|
||||
text: string | undefined,
|
||||
uri: string,
|
||||
timestamp?: number,
|
||||
timestamp: number,
|
||||
description?: string,
|
||||
assetType?: LocationAssetType,
|
||||
): LegacyLocationEventContent & MLocationEventContent => {
|
||||
|
@ -44,8 +44,8 @@ function publicKeyFromKeyInfo(keyInfo: ICrossSigningKey): string {
|
||||
}
|
||||
|
||||
export interface ICacheCallbacks {
|
||||
getCrossSigningKeyCache?(type: string, expectedPublicKey?: string): Promise<Uint8Array>;
|
||||
storeCrossSigningKeyCache?(type: string, key: Uint8Array): Promise<void>;
|
||||
getCrossSigningKeyCache?(type: string, expectedPublicKey?: string): Promise<Uint8Array | null>;
|
||||
storeCrossSigningKeyCache?(type: string, key?: Uint8Array): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ICrossSigningInfo {
|
||||
@ -169,7 +169,9 @@ export class CrossSigningInfo {
|
||||
* with, or null if it is not present or not encrypted with a trusted
|
||||
* key
|
||||
*/
|
||||
public async isStoredInSecretStorage(secretStorage: SecretStorage): Promise<Record<string, object> | null> {
|
||||
public async isStoredInSecretStorage(
|
||||
secretStorage: SecretStorage<MatrixClient | undefined>,
|
||||
): Promise<Record<string, object> | null> {
|
||||
// check what SSSS keys have encrypted the master key (if any)
|
||||
const stored = await secretStorage.isStored("m.cross_signing.master") || {};
|
||||
// then check which of those SSSS keys have also encrypted the SSK and USK
|
||||
@ -196,7 +198,7 @@ export class CrossSigningInfo {
|
||||
*/
|
||||
public static async storeInSecretStorage(
|
||||
keys: Map<string, Uint8Array>,
|
||||
secretStorage: SecretStorage,
|
||||
secretStorage: SecretStorage<undefined>,
|
||||
): Promise<void> {
|
||||
for (const [type, privateKey] of keys) {
|
||||
const encodedKey = encodeBase64(privateKey);
|
||||
@ -433,10 +435,9 @@ export class CrossSigningInfo {
|
||||
// if everything checks out, then save the keys
|
||||
if (keys.master) {
|
||||
this.keys.master = keys.master;
|
||||
// if the master key is set, then the old self-signing and
|
||||
// user-signing keys are obsolete
|
||||
this.keys.self_signing = null;
|
||||
this.keys.user_signing = null;
|
||||
// if the master key is set, then the old self-signing and user-signing keys are obsolete
|
||||
delete this.keys["self_signing"];
|
||||
delete this.keys["user_signing"];
|
||||
}
|
||||
if (keys.self_signing) {
|
||||
this.keys.self_signing = keys.self_signing;
|
||||
@ -723,7 +724,7 @@ export function createCryptoStoreCacheCallbacks(store: CryptoStore, olmDevice: O
|
||||
},
|
||||
storeCrossSigningKeyCache: async function(
|
||||
type: keyof SecretStorePrivateKeys,
|
||||
key: Uint8Array,
|
||||
key?: Uint8Array,
|
||||
): Promise<void> {
|
||||
if (!(key instanceof Uint8Array)) {
|
||||
throw new Error(
|
||||
|
@ -252,7 +252,7 @@ export class DeviceList extends TypedEventEmitter<EmittedEvents, CryptoEventHand
|
||||
*
|
||||
* @param {string} st The sync token
|
||||
*/
|
||||
public setSyncToken(st: string): void {
|
||||
public setSyncToken(st: string | null): void {
|
||||
this.syncToken = st;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import { logger } from "../logger";
|
||||
import { MatrixEvent } from "../models/event";
|
||||
import { IContent, MatrixEvent } from "../models/event";
|
||||
import { createCryptoStoreCacheCallbacks, ICacheCallbacks } from "./CrossSigning";
|
||||
import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store';
|
||||
import { Method, ClientPrefix } from "../http-api";
|
||||
@ -53,10 +53,10 @@ export class EncryptionSetupBuilder {
|
||||
public readonly crossSigningCallbacks: CrossSigningCallbacks;
|
||||
public readonly ssssCryptoCallbacks: SSSSCryptoCallbacks;
|
||||
|
||||
private crossSigningKeys: ICrossSigningKeys = null;
|
||||
private keySignatures: KeySignatures = null;
|
||||
private keyBackupInfo: IKeyBackupInfo = null;
|
||||
private sessionBackupPrivateKey: Uint8Array;
|
||||
private crossSigningKeys?: ICrossSigningKeys;
|
||||
private keySignatures?: KeySignatures;
|
||||
private keyBackupInfo?: IKeyBackupInfo;
|
||||
private sessionBackupPrivateKey?: Uint8Array;
|
||||
|
||||
/**
|
||||
* @param {Object.<String, MatrixEvent>} accountData pre-existing account data, will only be read, not written.
|
||||
@ -162,14 +162,14 @@ export class EncryptionSetupBuilder {
|
||||
for (const type of ["master", "self_signing", "user_signing"]) {
|
||||
logger.log(`Cache ${type} cross-signing private key locally`);
|
||||
const privateKey = this.crossSigningCallbacks.privateKeys.get(type);
|
||||
await cacheCallbacks.storeCrossSigningKeyCache(type, privateKey);
|
||||
await cacheCallbacks.storeCrossSigningKeyCache?.(type, privateKey);
|
||||
}
|
||||
// store own cross-sign pubkeys as trusted
|
||||
await crypto.cryptoStore.doTxn(
|
||||
'readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT],
|
||||
(txn) => {
|
||||
crypto.cryptoStore.storeCrossSigningKeys(
|
||||
txn, this.crossSigningKeys.keys);
|
||||
txn, this.crossSigningKeys!.keys);
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -195,9 +195,9 @@ export class EncryptionSetupOperation {
|
||||
*/
|
||||
constructor(
|
||||
private readonly accountData: Map<string, object>,
|
||||
private readonly crossSigningKeys: ICrossSigningKeys,
|
||||
private readonly keyBackupInfo: IKeyBackupInfo,
|
||||
private readonly keySignatures: KeySignatures,
|
||||
private readonly crossSigningKeys?: ICrossSigningKeys,
|
||||
private readonly keyBackupInfo?: IKeyBackupInfo,
|
||||
private readonly keySignatures?: KeySignatures,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -215,7 +215,7 @@ export class EncryptionSetupOperation {
|
||||
|
||||
// We must only call `uploadDeviceSigningKeys` from inside this auth
|
||||
// helper to ensure we properly handle auth errors.
|
||||
await this.crossSigningKeys.authUpload(authDict => {
|
||||
await this.crossSigningKeys.authUpload?.(authDict => {
|
||||
return baseApis.uploadDeviceSigningKeys(authDict, keys as CrossSigningKeys);
|
||||
});
|
||||
|
||||
@ -281,15 +281,15 @@ class AccountDataClientAdapter
|
||||
* @param {String} type
|
||||
* @return {Promise<Object>} the content of the account data
|
||||
*/
|
||||
public getAccountDataFromServer(type: string): Promise<any> {
|
||||
return Promise.resolve(this.getAccountData(type));
|
||||
public getAccountDataFromServer<T extends {[k: string]: any}>(type: string): Promise<T> {
|
||||
return Promise.resolve(this.getAccountData(type) as T);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} type
|
||||
* @return {Object} the content of the account data
|
||||
*/
|
||||
public getAccountData(type: string): MatrixEvent {
|
||||
public getAccountData(type: string): IContent | null {
|
||||
const modifiedValue = this.values.get(type);
|
||||
if (modifiedValue) {
|
||||
return modifiedValue;
|
||||
@ -329,7 +329,7 @@ class CrossSigningCallbacks implements ICryptoCallbacks, ICacheCallbacks {
|
||||
public readonly privateKeys = new Map<string, Uint8Array>();
|
||||
|
||||
// cache callbacks
|
||||
public getCrossSigningKeyCache(type: string, expectedPublicKey: string): Promise<Uint8Array> {
|
||||
public getCrossSigningKeyCache(type: string, expectedPublicKey: string): Promise<Uint8Array | null> {
|
||||
return this.getCrossSigningKey(type, expectedPublicKey);
|
||||
}
|
||||
|
||||
@ -339,8 +339,8 @@ class CrossSigningCallbacks implements ICryptoCallbacks, ICacheCallbacks {
|
||||
}
|
||||
|
||||
// non-cache callbacks
|
||||
public getCrossSigningKey(type: string, expectedPubkey: string): Promise<Uint8Array> {
|
||||
return Promise.resolve(this.privateKeys.get(type));
|
||||
public getCrossSigningKey(type: string, expectedPubkey: string): Promise<Uint8Array | null> {
|
||||
return Promise.resolve(this.privateKeys.get(type) ?? null);
|
||||
}
|
||||
|
||||
public saveCrossSigningKeys(privateKeys: Record<string, Uint8Array>) {
|
||||
|
@ -680,7 +680,7 @@ export class OlmDevice {
|
||||
public async getSessionIdsForDevice(theirDeviceIdentityKey: string): Promise<string[]> {
|
||||
const log = logger.withPrefix("[getSessionIdsForDevice]");
|
||||
|
||||
if (this.sessionsInProgress[theirDeviceIdentityKey]) {
|
||||
if (theirDeviceIdentityKey in this.sessionsInProgress) {
|
||||
log.debug(`Waiting for Olm session for ${theirDeviceIdentityKey} to be created`);
|
||||
try {
|
||||
await this.sessionsInProgress[theirDeviceIdentityKey];
|
||||
@ -770,7 +770,7 @@ export class OlmDevice {
|
||||
): Promise<{ sessionId: string, lastReceivedMessageTs: number, hasReceivedMessage: boolean }[]> {
|
||||
log = log.withPrefix("[getSessionInfoForDevice]");
|
||||
|
||||
if (this.sessionsInProgress[deviceIdentityKey] && !nowait) {
|
||||
if (deviceIdentityKey in this.sessionsInProgress && !nowait) {
|
||||
log.debug(`Waiting for Olm session for ${deviceIdentityKey} to be created`);
|
||||
try {
|
||||
await this.sessionsInProgress[deviceIdentityKey];
|
||||
|
@ -75,10 +75,26 @@ export enum RoomKeyRequestState {
|
||||
CancellationPendingAndWillResend,
|
||||
}
|
||||
|
||||
interface RequestMessageBase {
|
||||
requesting_device_id: string;
|
||||
request_id: string;
|
||||
}
|
||||
|
||||
interface RequestMessageRequest extends RequestMessageBase {
|
||||
action: "request";
|
||||
body: IRoomKeyRequestBody;
|
||||
}
|
||||
|
||||
interface RequestMessageCancellation extends RequestMessageBase {
|
||||
action: "request_cancellation";
|
||||
}
|
||||
|
||||
type RequestMessage = RequestMessageRequest | RequestMessageCancellation;
|
||||
|
||||
export class OutgoingRoomKeyRequestManager {
|
||||
// handle for the delayed call to sendOutgoingRoomKeyRequests. Non-null
|
||||
// if the callback has been set, or if it is still running.
|
||||
private sendOutgoingRoomKeyRequestsTimer: ReturnType<typeof setTimeout> = null;
|
||||
private sendOutgoingRoomKeyRequestsTimer?: ReturnType<typeof setTimeout>;
|
||||
|
||||
// sanity check to ensure that we don't end up with two concurrent runs
|
||||
// of sendOutgoingRoomKeyRequests
|
||||
@ -369,43 +385,42 @@ export class OutgoingRoomKeyRequestManager {
|
||||
// look for and send any queued requests. Runs itself recursively until
|
||||
// there are no more requests, or there is an error (in which case, the
|
||||
// timer will be restarted before the promise resolves).
|
||||
private sendOutgoingRoomKeyRequests(): Promise<void> {
|
||||
private async sendOutgoingRoomKeyRequests(): Promise<void> {
|
||||
if (!this.clientRunning) {
|
||||
this.sendOutgoingRoomKeyRequestsTimer = null;
|
||||
return Promise.resolve();
|
||||
this.sendOutgoingRoomKeyRequestsTimer = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
return this.cryptoStore.getOutgoingRoomKeyRequestByState([
|
||||
const req = await this.cryptoStore.getOutgoingRoomKeyRequestByState([
|
||||
RoomKeyRequestState.CancellationPending,
|
||||
RoomKeyRequestState.CancellationPendingAndWillResend,
|
||||
RoomKeyRequestState.Unsent,
|
||||
]).then((req: OutgoingRoomKeyRequest) => {
|
||||
if (!req) {
|
||||
this.sendOutgoingRoomKeyRequestsTimer = null;
|
||||
return;
|
||||
}
|
||||
]);
|
||||
|
||||
let prom;
|
||||
if (!req) {
|
||||
this.sendOutgoingRoomKeyRequestsTimer = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
switch (req.state) {
|
||||
case RoomKeyRequestState.Unsent:
|
||||
prom = this.sendOutgoingRoomKeyRequest(req);
|
||||
await this.sendOutgoingRoomKeyRequest(req);
|
||||
break;
|
||||
case RoomKeyRequestState.CancellationPending:
|
||||
prom = this.sendOutgoingRoomKeyRequestCancellation(req);
|
||||
await this.sendOutgoingRoomKeyRequestCancellation(req);
|
||||
break;
|
||||
case RoomKeyRequestState.CancellationPendingAndWillResend:
|
||||
prom = this.sendOutgoingRoomKeyRequestCancellation(req, true);
|
||||
await this.sendOutgoingRoomKeyRequestCancellation(req, true);
|
||||
break;
|
||||
}
|
||||
|
||||
return prom.then(() => {
|
||||
// go around the loop again
|
||||
return this.sendOutgoingRoomKeyRequests();
|
||||
}).catch((e) => {
|
||||
logger.error("Error sending room key request; will retry later.", e);
|
||||
this.sendOutgoingRoomKeyRequestsTimer = null;
|
||||
});
|
||||
});
|
||||
// go around the loop again
|
||||
return this.sendOutgoingRoomKeyRequests();
|
||||
} catch (e) {
|
||||
logger.error("Error sending room key request; will retry later.", e);
|
||||
this.sendOutgoingRoomKeyRequestsTimer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// given a RoomKeyRequest, send it and update the request record
|
||||
@ -416,16 +431,14 @@ export class OutgoingRoomKeyRequestManager {
|
||||
`(id ${req.requestId})`,
|
||||
);
|
||||
|
||||
const requestMessage = {
|
||||
const requestMessage: RequestMessage = {
|
||||
action: "request",
|
||||
requesting_device_id: this.deviceId,
|
||||
request_id: req.requestId,
|
||||
body: req.requestBody,
|
||||
};
|
||||
|
||||
return this.sendMessageToDevices(
|
||||
requestMessage, req.recipients, req.requestTxnId || req.requestId,
|
||||
).then(() => {
|
||||
return this.sendMessageToDevices(requestMessage, req.recipients, req.requestTxnId || req.requestId).then(() => {
|
||||
return this.cryptoStore.updateOutgoingRoomKeyRequest(
|
||||
req.requestId, RoomKeyRequestState.Unsent,
|
||||
{ state: RoomKeyRequestState.Sent },
|
||||
@ -443,7 +456,7 @@ export class OutgoingRoomKeyRequestManager {
|
||||
`(cancellation id ${req.cancellationTxnId})`,
|
||||
);
|
||||
|
||||
const requestMessage = {
|
||||
const requestMessage: RequestMessage = {
|
||||
action: "request_cancellation",
|
||||
requesting_device_id: this.deviceId,
|
||||
request_id: req.requestId,
|
||||
@ -467,7 +480,11 @@ export class OutgoingRoomKeyRequestManager {
|
||||
}
|
||||
|
||||
// send a RoomKeyRequest to a list of recipients
|
||||
private sendMessageToDevices(message, recipients, txnId: string): Promise<{}> {
|
||||
private sendMessageToDevices(
|
||||
message: RequestMessage,
|
||||
recipients: IRoomKeyRequestRecipient[],
|
||||
txnId?: string,
|
||||
): Promise<{}> {
|
||||
const contentMap: Record<string, Record<string, Record<string, any>>> = {};
|
||||
for (const recip of recipients) {
|
||||
if (!contentMap[recip.userId]) {
|
||||
@ -480,15 +497,13 @@ export class OutgoingRoomKeyRequestManager {
|
||||
}
|
||||
}
|
||||
|
||||
function stringifyRequestBody(requestBody) {
|
||||
function stringifyRequestBody(requestBody: IRoomKeyRequestBody): string {
|
||||
// we assume that the request is for megolm keys, which are identified by
|
||||
// room id and session id
|
||||
return requestBody.room_id + " / " + requestBody.session_id;
|
||||
}
|
||||
|
||||
function stringifyRecipientList(recipients) {
|
||||
return '['
|
||||
+ recipients.map((r) => `${r.userId}:${r.deviceId}`).join(",")
|
||||
+ ']';
|
||||
function stringifyRecipientList(recipients: IRoomKeyRequestRecipient[]): string {
|
||||
return `[${recipients.map((r) => `${r.userId}:${r.deviceId}`).join(",")}]`;
|
||||
}
|
||||
|
||||
|
@ -38,12 +38,12 @@ export class RoomList {
|
||||
// Object of roomId -> room e2e info object (body of the m.room.encryption event)
|
||||
private roomEncryption: Record<string, IRoomEncryption> = {};
|
||||
|
||||
constructor(private readonly cryptoStore: CryptoStore) {}
|
||||
constructor(private readonly cryptoStore?: CryptoStore) {}
|
||||
|
||||
public async init(): Promise<void> {
|
||||
await this.cryptoStore.doTxn(
|
||||
await this.cryptoStore!.doTxn(
|
||||
'readwrite', [IndexedDBCryptoStore.STORE_ROOMS], (txn) => {
|
||||
this.cryptoStore.getEndToEndRooms(txn, (result) => {
|
||||
this.cryptoStore!.getEndToEndRooms(txn, (result) => {
|
||||
this.roomEncryption = result;
|
||||
});
|
||||
},
|
||||
@ -63,9 +63,9 @@ export class RoomList {
|
||||
// as it prevents the Crypto::setRoomEncryption from calling
|
||||
// this twice for consecutive m.room.encryption events
|
||||
this.roomEncryption[roomId] = roomInfo;
|
||||
await this.cryptoStore.doTxn(
|
||||
await this.cryptoStore!.doTxn(
|
||||
'readwrite', [IndexedDBCryptoStore.STORE_ROOMS], (txn) => {
|
||||
this.cryptoStore.storeEndToEndRoom(roomId, roomInfo, txn);
|
||||
this.cryptoStore!.storeEndToEndRoom(roomId, roomInfo, txn);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -19,10 +19,11 @@ import * as olmlib from './olmlib';
|
||||
import { encodeBase64 } from './olmlib';
|
||||
import { randomString } from '../randomstring';
|
||||
import { calculateKeyCheck, decryptAES, encryptAES, IEncryptedPayload } from './aes';
|
||||
import { ClientEvent, ICryptoCallbacks, MatrixEvent } from '../matrix';
|
||||
import { ClientEvent, IContent, ICryptoCallbacks, MatrixEvent } from '../matrix';
|
||||
import { ClientEventHandlerMap, MatrixClient } from "../client";
|
||||
import { IAddSecretStorageKeyOpts, ISecretStorageKeyInfo } from './api';
|
||||
import { TypedEventEmitter } from '../models/typed-event-emitter';
|
||||
import { defer, IDeferred } from "../utils";
|
||||
|
||||
export const SECRET_STORAGE_ALGORITHM_V1_AES = "m.secret_storage.v1.aes-hmac-sha2";
|
||||
|
||||
@ -39,15 +40,14 @@ export interface ISecretRequest {
|
||||
export interface IAccountDataClient extends TypedEventEmitter<ClientEvent.AccountData, ClientEventHandlerMap> {
|
||||
// Subset of MatrixClient (which also uses any for the event content)
|
||||
getAccountDataFromServer: <T extends {[k: string]: any}>(eventType: string) => Promise<T>;
|
||||
getAccountData: (eventType: string) => MatrixEvent;
|
||||
getAccountData: (eventType: string) => IContent | null;
|
||||
setAccountData: (eventType: string, content: any) => Promise<{}>;
|
||||
}
|
||||
|
||||
interface ISecretRequestInternal {
|
||||
name: string;
|
||||
devices: string[];
|
||||
resolve: (reason: string) => void;
|
||||
reject: (error: Error) => void;
|
||||
deferred: IDeferred<string>;
|
||||
}
|
||||
|
||||
interface IDecryptors {
|
||||
@ -66,7 +66,7 @@ interface ISecretInfo {
|
||||
* Implements Secure Secret Storage and Sharing (MSC1946)
|
||||
* @module crypto/SecretStorage
|
||||
*/
|
||||
export class SecretStorage {
|
||||
export class SecretStorage<B extends MatrixClient | undefined = MatrixClient> {
|
||||
private requests = new Map<string, ISecretRequestInternal>();
|
||||
|
||||
// In it's pure javascript days, this was relying on some proper Javascript-style
|
||||
@ -80,7 +80,7 @@ export class SecretStorage {
|
||||
constructor(
|
||||
private readonly accountDataAdapter: IAccountDataClient,
|
||||
private readonly cryptoCallbacks: ICryptoCallbacks,
|
||||
private readonly baseApis?: MatrixClient,
|
||||
private readonly baseApis: B,
|
||||
) {}
|
||||
|
||||
public async getDefaultKeyId(): Promise<string | null> {
|
||||
@ -129,13 +129,11 @@ export class SecretStorage {
|
||||
*/
|
||||
public async addKey(
|
||||
algorithm: string,
|
||||
opts: IAddSecretStorageKeyOpts,
|
||||
opts: IAddSecretStorageKeyOpts = {},
|
||||
keyId?: string,
|
||||
): Promise<SecretStorageKeyObject> {
|
||||
const keyInfo = { algorithm } as ISecretStorageKeyInfo;
|
||||
|
||||
if (!opts) opts = {} as IAddSecretStorageKeyOpts;
|
||||
|
||||
if (opts.name) {
|
||||
keyInfo.name = opts.name;
|
||||
}
|
||||
@ -376,21 +374,11 @@ export class SecretStorage {
|
||||
* @param {string} name the name of the secret to request
|
||||
* @param {string[]} devices the devices to request the secret from
|
||||
*/
|
||||
public request(name: string, devices: string[]): ISecretRequest {
|
||||
public request(this: SecretStorage<MatrixClient>, name: string, devices: string[]): ISecretRequest {
|
||||
const requestId = this.baseApis.makeTxnId();
|
||||
|
||||
let resolve: (s: string) => void;
|
||||
let reject: (e: Error) => void;
|
||||
const promise = new Promise<string>((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
this.requests.set(requestId, {
|
||||
name,
|
||||
devices,
|
||||
resolve,
|
||||
reject,
|
||||
});
|
||||
const deferred = defer<string>();
|
||||
this.requests.set(requestId, { name, devices, deferred });
|
||||
|
||||
const cancel = (reason: string) => {
|
||||
// send cancellation event
|
||||
@ -404,12 +392,12 @@ export class SecretStorage {
|
||||
toDevice[device] = cancelData;
|
||||
}
|
||||
this.baseApis.sendToDevice("m.secret.request", {
|
||||
[this.baseApis.getUserId()]: toDevice,
|
||||
[this.baseApis.getUserId()!]: toDevice,
|
||||
});
|
||||
|
||||
// and reject the promise so that anyone waiting on it will be
|
||||
// notified
|
||||
reject(new Error(reason || "Cancelled"));
|
||||
deferred.reject(new Error(reason || "Cancelled"));
|
||||
};
|
||||
|
||||
// send request to devices
|
||||
@ -425,22 +413,23 @@ export class SecretStorage {
|
||||
}
|
||||
logger.info(`Request secret ${name} from ${devices}, id ${requestId}`);
|
||||
this.baseApis.sendToDevice("m.secret.request", {
|
||||
[this.baseApis.getUserId()]: toDevice,
|
||||
[this.baseApis.getUserId()!]: toDevice,
|
||||
});
|
||||
|
||||
return {
|
||||
requestId,
|
||||
promise,
|
||||
promise: deferred.promise,
|
||||
cancel,
|
||||
};
|
||||
}
|
||||
|
||||
public async onRequestReceived(event: MatrixEvent): Promise<void> {
|
||||
public async onRequestReceived(this: SecretStorage<MatrixClient>, event: MatrixEvent): Promise<void> {
|
||||
const sender = event.getSender();
|
||||
const content = event.getContent();
|
||||
if (sender !== this.baseApis.getUserId()
|
||||
|| !(content.name && content.action
|
||||
&& content.requesting_device_id && content.request_id)) {
|
||||
&& content.requesting_device_id && content.request_id)
|
||||
) {
|
||||
// ignore requests from anyone else, for now
|
||||
return;
|
||||
}
|
||||
@ -498,25 +487,25 @@ export class SecretStorage {
|
||||
};
|
||||
const encryptedContent = {
|
||||
algorithm: olmlib.OLM_ALGORITHM,
|
||||
sender_key: this.baseApis.crypto.olmDevice.deviceCurve25519Key,
|
||||
sender_key: this.baseApis.crypto!.olmDevice.deviceCurve25519Key,
|
||||
ciphertext: {},
|
||||
};
|
||||
await olmlib.ensureOlmSessionsForDevices(
|
||||
this.baseApis.crypto.olmDevice,
|
||||
this.baseApis.crypto!.olmDevice,
|
||||
this.baseApis,
|
||||
{
|
||||
[sender]: [
|
||||
this.baseApis.getStoredDevice(sender, deviceId),
|
||||
this.baseApis.getStoredDevice(sender, deviceId)!,
|
||||
],
|
||||
},
|
||||
);
|
||||
await olmlib.encryptMessageForDevice(
|
||||
encryptedContent.ciphertext,
|
||||
this.baseApis.getUserId(),
|
||||
this.baseApis.deviceId,
|
||||
this.baseApis.crypto.olmDevice,
|
||||
this.baseApis.getUserId()!,
|
||||
this.baseApis.deviceId!,
|
||||
this.baseApis.crypto!.olmDevice,
|
||||
sender,
|
||||
this.baseApis.getStoredDevice(sender, deviceId),
|
||||
this.baseApis.getStoredDevice(sender, deviceId)!,
|
||||
payload,
|
||||
);
|
||||
const contentMap = {
|
||||
@ -533,7 +522,7 @@ export class SecretStorage {
|
||||
}
|
||||
}
|
||||
|
||||
public onSecretReceived(event: MatrixEvent): void {
|
||||
public onSecretReceived(this: SecretStorage<MatrixClient>, event: MatrixEvent): void {
|
||||
if (event.getSender() !== this.baseApis.getUserId()) {
|
||||
// we shouldn't be receiving secrets from anyone else, so ignore
|
||||
// because someone could be trying to send us bogus data
|
||||
@ -547,7 +536,7 @@ export class SecretStorage {
|
||||
|
||||
const content = event.getContent();
|
||||
|
||||
const senderKeyUser = this.baseApis.crypto.deviceList.getUserByIdentityKey(
|
||||
const senderKeyUser = this.baseApis.crypto!.deviceList.getUserByIdentityKey(
|
||||
olmlib.OLM_ALGORITHM,
|
||||
event.getSenderKey() || "",
|
||||
);
|
||||
@ -561,9 +550,9 @@ export class SecretStorage {
|
||||
if (requestControl) {
|
||||
// make sure that the device that sent it is one of the devices that
|
||||
// we requested from
|
||||
const deviceInfo = this.baseApis.crypto.deviceList.getDeviceByIdentityKey(
|
||||
const deviceInfo = this.baseApis.crypto!.deviceList.getDeviceByIdentityKey(
|
||||
olmlib.OLM_ALGORITHM,
|
||||
event.getSenderKey(),
|
||||
event.getSenderKey()!,
|
||||
);
|
||||
if (!deviceInfo) {
|
||||
logger.log(
|
||||
@ -578,7 +567,7 @@ export class SecretStorage {
|
||||
// unsure that the sender is trusted. In theory, this check is
|
||||
// unnecessary since we only accept secret shares from devices that
|
||||
// we requested from, but it doesn't hurt.
|
||||
const deviceTrust = this.baseApis.crypto.checkDeviceInfoTrust(event.getSender(), deviceInfo);
|
||||
const deviceTrust = this.baseApis.crypto!.checkDeviceInfoTrust(event.getSender()!, deviceInfo);
|
||||
if (!deviceTrust.isVerified()) {
|
||||
logger.log("secret share from unverified device");
|
||||
return;
|
||||
@ -588,7 +577,7 @@ export class SecretStorage {
|
||||
`Successfully received secret ${requestControl.name} ` +
|
||||
`from ${deviceInfo.deviceId}`,
|
||||
);
|
||||
requestControl.resolve(content.secret);
|
||||
requestControl.deferred.resolve(content.secret);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ import { IRoomEncryption } from "../RoomList";
|
||||
*/
|
||||
export const ENCRYPTION_CLASSES = new Map<string, new (params: IParams) => EncryptionAlgorithm>();
|
||||
|
||||
type DecryptionClassParams = Omit<IParams, "deviceId" | "config">;
|
||||
export type DecryptionClassParams<P extends IParams = IParams> = Omit<P, "deviceId" | "config">;
|
||||
|
||||
/**
|
||||
* map of registered encryption algorithm classes. Map from string to {@link
|
||||
@ -52,7 +52,7 @@ export interface IParams {
|
||||
crypto: Crypto;
|
||||
olmDevice: OlmDevice;
|
||||
baseApis: MatrixClient;
|
||||
roomId: string;
|
||||
roomId?: string;
|
||||
config: IRoomEncryption & object;
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ export abstract class EncryptionAlgorithm {
|
||||
protected readonly crypto: Crypto;
|
||||
protected readonly olmDevice: OlmDevice;
|
||||
protected readonly baseApis: MatrixClient;
|
||||
protected readonly roomId: string;
|
||||
protected readonly roomId?: string;
|
||||
|
||||
constructor(params: IParams) {
|
||||
this.userId = params.userId;
|
||||
@ -148,7 +148,7 @@ export abstract class DecryptionAlgorithm {
|
||||
protected readonly crypto: Crypto;
|
||||
protected readonly olmDevice: OlmDevice;
|
||||
protected readonly baseApis: MatrixClient;
|
||||
protected readonly roomId: string;
|
||||
protected readonly roomId?: string;
|
||||
|
||||
constructor(params: DecryptionClassParams) {
|
||||
this.userId = params.userId;
|
||||
@ -296,11 +296,11 @@ export class UnknownDeviceError extends Error {
|
||||
* module:crypto/algorithms/base.DecryptionAlgorithm|DecryptionAlgorithm}
|
||||
* implementation
|
||||
*/
|
||||
export function registerAlgorithm(
|
||||
export function registerAlgorithm<P extends IParams = IParams>(
|
||||
algorithm: string,
|
||||
encryptor: new (params: IParams) => EncryptionAlgorithm,
|
||||
decryptor: new (params: DecryptionClassParams) => DecryptionAlgorithm,
|
||||
encryptor: new (params: P) => EncryptionAlgorithm,
|
||||
decryptor: new (params: DecryptionClassParams<P>) => DecryptionAlgorithm,
|
||||
): void {
|
||||
ENCRYPTION_CLASSES.set(algorithm, encryptor);
|
||||
DECRYPTION_CLASSES.set(algorithm, decryptor);
|
||||
ENCRYPTION_CLASSES.set(algorithm, encryptor as new (params: IParams) => EncryptionAlgorithm);
|
||||
DECRYPTION_CLASSES.set(algorithm, decryptor as new (params: DecryptionClassParams) => DecryptionAlgorithm);
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import { logger } from '../../logger';
|
||||
import * as olmlib from "../olmlib";
|
||||
import {
|
||||
DecryptionAlgorithm,
|
||||
DecryptionClassParams,
|
||||
DecryptionError,
|
||||
EncryptionAlgorithm,
|
||||
IParams,
|
||||
@ -251,8 +252,11 @@ class MegolmEncryption extends EncryptionAlgorithm {
|
||||
startTime: number;
|
||||
};
|
||||
|
||||
constructor(params: IParams) {
|
||||
protected readonly roomId: string;
|
||||
|
||||
constructor(params: IParams & Required<Pick<IParams, "roomId">>) {
|
||||
super(params);
|
||||
this.roomId = params.roomId;
|
||||
|
||||
this.sessionRotationPeriodMsgs = params.config?.rotation_period_msgs ?? 100;
|
||||
this.sessionRotationPeriodMs = params.config?.rotation_period_ms ?? 7 * 24 * 3600 * 1000;
|
||||
@ -1231,6 +1235,13 @@ class MegolmDecryption extends DecryptionAlgorithm {
|
||||
// this gets stubbed out by the unit tests.
|
||||
private olmlib = olmlib;
|
||||
|
||||
protected readonly roomId: string;
|
||||
|
||||
constructor(params: DecryptionClassParams<IParams & Required<Pick<IParams, "roomId">>>) {
|
||||
super(params);
|
||||
this.roomId = params.roomId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
@ -1264,7 +1275,7 @@ class MegolmDecryption extends DecryptionAlgorithm {
|
||||
try {
|
||||
res = await this.olmDevice.decryptGroupMessage(
|
||||
event.getRoomId()!, content.sender_key, content.session_id, content.ciphertext,
|
||||
event.getId(), event.getTs(),
|
||||
event.getId()!, event.getTs(),
|
||||
);
|
||||
} catch (e) {
|
||||
if ((<Error>e).name === "DecryptionError") {
|
||||
@ -1464,7 +1475,7 @@ class MegolmDecryption extends DecryptionAlgorithm {
|
||||
return;
|
||||
}
|
||||
const outgoingRequests = deviceInfo ? await this.crypto.cryptoStore.getOutgoingRoomKeyRequestsByTarget(
|
||||
event.getSender(), deviceInfo.deviceId, [RoomKeyRequestState.Sent],
|
||||
event.getSender()!, deviceInfo.deviceId, [RoomKeyRequestState.Sent],
|
||||
) : [];
|
||||
const weRequested = outgoingRequests.some((req) => (
|
||||
req.requestBody.room_id === content.room_id && req.requestBody.session_id === content.session_id
|
||||
@ -1524,7 +1535,7 @@ class MegolmDecryption extends DecryptionAlgorithm {
|
||||
// that room later
|
||||
if (!room) {
|
||||
const parkedData = {
|
||||
senderId: event.getSender(),
|
||||
senderId: event.getSender()!,
|
||||
senderKey: content.sender_key,
|
||||
sessionId: content.session_id,
|
||||
sessionKey: content.session_key,
|
||||
@ -1544,7 +1555,7 @@ class MegolmDecryption extends DecryptionAlgorithm {
|
||||
olmlib.OLM_ALGORITHM,
|
||||
senderKey,
|
||||
) ?? undefined;
|
||||
const deviceTrust = this.crypto.checkDeviceInfoTrust(event.getSender(), sendingDevice);
|
||||
const deviceTrust = this.crypto.checkDeviceInfoTrust(event.getSender()!, sendingDevice);
|
||||
|
||||
if (fromUs && !deviceTrust.isVerified()) {
|
||||
return;
|
||||
@ -1608,7 +1619,7 @@ class MegolmDecryption extends DecryptionAlgorithm {
|
||||
const senderKey = content.sender_key;
|
||||
|
||||
if (content.code === "m.no_olm") {
|
||||
const sender = event.getSender();
|
||||
const sender = event.getSender()!;
|
||||
logger.warn(
|
||||
`${sender}:${senderKey} was unable to establish an olm session with us`,
|
||||
);
|
||||
|
@ -228,7 +228,7 @@ class OlmDecryption extends DecryptionAlgorithm {
|
||||
// assume that the device logged out. Some event handlers, such as
|
||||
// secret sharing, may be more strict and reject events that come from
|
||||
// unknown devices.
|
||||
await this.crypto.deviceList.downloadKeys([event.getSender()], false);
|
||||
await this.crypto.deviceList.downloadKeys([event.getSender()!], false);
|
||||
const senderKeyUser = this.crypto.deviceList.getUserByIdentityKey(
|
||||
olmlib.OLM_ALGORITHM,
|
||||
deviceKey,
|
||||
@ -250,7 +250,7 @@ class OlmDecryption extends DecryptionAlgorithm {
|
||||
throw new DecryptionError(
|
||||
"OLM_FORWARDED_MESSAGE",
|
||||
"Message forwarded from " + payload.sender, {
|
||||
reported_sender: event.getSender(),
|
||||
reported_sender: event.getSender()!,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -117,10 +117,10 @@ export interface IPassphraseInfo {
|
||||
}
|
||||
|
||||
export interface IAddSecretStorageKeyOpts {
|
||||
pubkey: string;
|
||||
pubkey?: string;
|
||||
passphrase?: IPassphraseInfo;
|
||||
name?: string;
|
||||
key: Uint8Array;
|
||||
key?: Uint8Array;
|
||||
}
|
||||
|
||||
export interface IImportOpts {
|
||||
|
@ -201,7 +201,7 @@ export class BackupManager {
|
||||
}
|
||||
|
||||
const [privateKey, authData] = await Algorithm.prepare(key);
|
||||
const recoveryKey = encodeRecoveryKey(privateKey);
|
||||
const recoveryKey = encodeRecoveryKey(privateKey)!;
|
||||
return {
|
||||
algorithm: Algorithm.algorithmName,
|
||||
auth_data: authData,
|
||||
@ -298,10 +298,10 @@ export class BackupManager {
|
||||
|
||||
const now = new Date().getTime();
|
||||
if (
|
||||
!this.sessionLastCheckAttemptedTime[targetSessionId]
|
||||
|| now - this.sessionLastCheckAttemptedTime[targetSessionId] > KEY_BACKUP_CHECK_RATE_LIMIT
|
||||
!this.sessionLastCheckAttemptedTime[targetSessionId!]
|
||||
|| now - this.sessionLastCheckAttemptedTime[targetSessionId!] > KEY_BACKUP_CHECK_RATE_LIMIT
|
||||
) {
|
||||
this.sessionLastCheckAttemptedTime[targetSessionId] = now;
|
||||
this.sessionLastCheckAttemptedTime[targetSessionId!] = now;
|
||||
await this.baseApis.restoreKeyBackupWithCache(targetRoomId, targetSessionId, this.backupInfo, {});
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ export class DeviceInfo {
|
||||
BLOCKED: DeviceVerification.Blocked,
|
||||
};
|
||||
|
||||
public algorithms: string[];
|
||||
public algorithms: string[] = [];
|
||||
public keys: Record<string, string> = {};
|
||||
public verified = DeviceVerification.Unverified;
|
||||
public known = false;
|
||||
|
@ -199,8 +199,8 @@ export interface IEventDecryptionResult {
|
||||
}
|
||||
|
||||
export interface IRequestsMap {
|
||||
getRequest(event: MatrixEvent): VerificationRequest;
|
||||
getRequestByChannel(channel: IVerificationChannel): VerificationRequest;
|
||||
getRequest(event: MatrixEvent): VerificationRequest | undefined;
|
||||
getRequestByChannel(channel: IVerificationChannel): VerificationRequest | undefined;
|
||||
setRequest(event: MatrixEvent, request: VerificationRequest): void;
|
||||
setRequestByChannel(channel: IVerificationChannel, request: VerificationRequest): void;
|
||||
}
|
||||
@ -302,7 +302,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
// track if an initial tracking of all the room members
|
||||
// has happened for a given room. This is delayed
|
||||
// to avoid loading room members as long as possible.
|
||||
private roomDeviceTrackingState: Record<string, Promise<void>> = {}; // roomId: Promise<void
|
||||
private roomDeviceTrackingState: { [roomId: string]: Promise<void> } = {};
|
||||
|
||||
// The timestamp of the last time we forced establishment
|
||||
// of a new session for each device, in milliseconds.
|
||||
@ -588,7 +588,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
privateKey,
|
||||
};
|
||||
} finally {
|
||||
if (decryption) decryption.free();
|
||||
decryption?.free();
|
||||
}
|
||||
}
|
||||
|
||||
@ -764,7 +764,9 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
) {
|
||||
const secretStorage = new SecretStorage(
|
||||
builder.accountDataClientAdapter,
|
||||
builder.ssssCryptoCallbacks);
|
||||
builder.ssssCryptoCallbacks,
|
||||
undefined,
|
||||
);
|
||||
if (await secretStorage.hasKey()) {
|
||||
logger.log("Storing new cross-signing private keys in secret storage");
|
||||
// This is writing to in-memory account data in
|
||||
@ -836,6 +838,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
const secretStorage = new SecretStorage(
|
||||
builder.accountDataClientAdapter,
|
||||
builder.ssssCryptoCallbacks,
|
||||
undefined,
|
||||
);
|
||||
|
||||
// the ID of the new SSSS key, if we create one
|
||||
@ -2224,7 +2227,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
logger.info("Own device " + deviceId + " marked verified: signing");
|
||||
|
||||
// Signing only needed if other device not already signed
|
||||
let device: ISignedKey;
|
||||
let device: ISignedKey | undefined;
|
||||
const deviceTrust = this.checkDeviceTrust(userId, deviceId);
|
||||
if (deviceTrust.isCrossSigningVerified()) {
|
||||
logger.log(`Own device ${deviceId} already cross-signing verified`);
|
||||
@ -2239,7 +2242,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
logger.info("Uploading signature for " + deviceId);
|
||||
const response = await this.baseApis.uploadKeySignatures({
|
||||
[userId]: {
|
||||
[deviceId]: device,
|
||||
[deviceId]: device!,
|
||||
},
|
||||
});
|
||||
const { failures } = response || {};
|
||||
@ -2265,7 +2268,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
return deviceObj;
|
||||
}
|
||||
|
||||
public findVerificationRequestDMInProgress(roomId: string): VerificationRequest {
|
||||
public findVerificationRequestDMInProgress(roomId: string): VerificationRequest | undefined {
|
||||
return this.inRoomVerificationRequests.findRequestInProgress(roomId);
|
||||
}
|
||||
|
||||
@ -2321,9 +2324,9 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
method: string,
|
||||
userId: string,
|
||||
deviceId: string,
|
||||
transactionId: string = null,
|
||||
transactionId: string | null = null,
|
||||
): VerificationBase<any, any> {
|
||||
let request: Request;
|
||||
let request: Request | undefined;
|
||||
if (transactionId) {
|
||||
request = this.toDeviceVerificationRequests.getRequestBySenderAndTxnId(userId, transactionId);
|
||||
if (!request) {
|
||||
@ -2467,7 +2470,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
public getEventEncryptionInfo(event: MatrixEvent): IEncryptedEventInfo {
|
||||
const ret: Partial<IEncryptedEventInfo> = {};
|
||||
|
||||
ret.senderKey = event.getSenderKey();
|
||||
ret.senderKey = event.getSenderKey() ?? undefined;
|
||||
ret.algorithm = event.getWireContent().algorithm;
|
||||
|
||||
if (!ret.senderKey || !ret.algorithm) {
|
||||
@ -2488,7 +2491,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
// was sent from. In the case of Megolm, it's actually the Curve25519
|
||||
// identity key of the device which set up the Megolm session.
|
||||
|
||||
ret.sender = this.deviceList.getDeviceByIdentityKey(ret.algorithm, ret.senderKey);
|
||||
ret.sender = this.deviceList.getDeviceByIdentityKey(ret.algorithm, ret.senderKey) ?? undefined;
|
||||
|
||||
// so far so good, but now we need to check that the sender of this event
|
||||
// hadn't advertised someone else's Curve25519 key as their own. We do that
|
||||
@ -2655,7 +2658,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
if (!promise) {
|
||||
promise = trackMembers();
|
||||
this.roomDeviceTrackingState[roomId] = promise.catch(err => {
|
||||
this.roomDeviceTrackingState[roomId] = null;
|
||||
delete this.roomDeviceTrackingState[roomId];
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
@ -2716,9 +2719,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
this.cryptoStore.getAllEndToEndInboundGroupSessions(txn, (s) => {
|
||||
if (s === null) return;
|
||||
|
||||
const sess = this.olmDevice.exportInboundGroupSession(
|
||||
s.senderKey, s.sessionId, s.sessionData,
|
||||
);
|
||||
const sess = this.olmDevice.exportInboundGroupSession(s.senderKey, s.sessionId, s.sessionData!);
|
||||
delete sess.first_known_index;
|
||||
sess.algorithm = olmlib.MEGOLM_ALGORITHM;
|
||||
exportedSessions.push(sess);
|
||||
@ -2803,7 +2804,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
throw new Error("Cannot send encrypted messages in unknown rooms");
|
||||
}
|
||||
|
||||
const roomId = event.getRoomId();
|
||||
const roomId = event.getRoomId()!;
|
||||
|
||||
const alg = this.roomEncryptors.get(roomId);
|
||||
if (!alg) {
|
||||
@ -2885,7 +2886,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
};
|
||||
} else {
|
||||
const content = event.getWireContent();
|
||||
const alg = this.getRoomDecryptor(event.getRoomId(), content.algorithm);
|
||||
const alg = this.getRoomDecryptor(event.getRoomId()!, content.algorithm);
|
||||
return alg.decryptEvent(event);
|
||||
}
|
||||
}
|
||||
@ -2970,7 +2971,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
* @param {module:models/event.MatrixEvent} event encryption event
|
||||
*/
|
||||
public async onCryptoEvent(event: MatrixEvent): Promise<void> {
|
||||
const roomId = event.getRoomId();
|
||||
const roomId = event.getRoomId()!;
|
||||
const content = event.getContent<IRoomEncryption>();
|
||||
|
||||
try {
|
||||
@ -2978,8 +2979,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
// finished processing the sync, in onSyncCompleted.
|
||||
await this.setRoomEncryption(roomId, content, true);
|
||||
} catch (e) {
|
||||
logger.error("Error configuring encryption in room " + roomId +
|
||||
":", e);
|
||||
logger.error(`Error configuring encryption in room ${roomId}`, e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3013,7 +3013,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
* @param {Object} syncData the data from the 'MatrixClient.sync' event
|
||||
*/
|
||||
public async onSyncCompleted(syncData: ISyncStateData): Promise<void> {
|
||||
this.deviceList.setSyncToken(syncData.nextSyncToken);
|
||||
this.deviceList.setSyncToken(syncData.nextSyncToken ?? null);
|
||||
this.deviceList.saveIfDirty();
|
||||
|
||||
// we always track our own device list (for key backups etc)
|
||||
@ -3075,7 +3075,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
* @returns {string[]} List of user IDs
|
||||
*/
|
||||
private async getTrackedE2eUsers(): Promise<string[]> {
|
||||
const e2eUserIds = [];
|
||||
const e2eUserIds: string[] = [];
|
||||
for (const room of this.getTrackedE2eRooms()) {
|
||||
const members = await room.getEncryptionTargetMembers();
|
||||
for (const member of members) {
|
||||
@ -3295,7 +3295,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
if (!ToDeviceChannel.validateEvent(event, this.baseApis)) {
|
||||
return;
|
||||
}
|
||||
const createRequest = (event: MatrixEvent) => {
|
||||
const createRequest = (event: MatrixEvent): VerificationRequest | undefined => {
|
||||
if (!ToDeviceChannel.canCreateRequest(ToDeviceChannel.getEventType(event))) {
|
||||
return;
|
||||
}
|
||||
@ -3304,7 +3304,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
if (!deviceId) {
|
||||
return;
|
||||
}
|
||||
const userId = event.getSender();
|
||||
const userId = event.getSender()!;
|
||||
const channel = new ToDeviceChannel(
|
||||
this.baseApis,
|
||||
userId,
|
||||
@ -3337,10 +3337,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
return;
|
||||
}
|
||||
const createRequest = (event: MatrixEvent) => {
|
||||
const channel = new InRoomChannel(
|
||||
this.baseApis,
|
||||
event.getRoomId(),
|
||||
);
|
||||
const channel = new InRoomChannel(this.baseApis, event.getRoomId()!);
|
||||
return new VerificationRequest(
|
||||
channel, this.verificationMethods, this.baseApis);
|
||||
};
|
||||
@ -3350,15 +3347,15 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
private async handleVerificationEvent(
|
||||
event: MatrixEvent,
|
||||
requestsMap: IRequestsMap,
|
||||
createRequest: (event: MatrixEvent) => VerificationRequest,
|
||||
createRequest: (event: MatrixEvent) => VerificationRequest | undefined,
|
||||
isLiveEvent = true,
|
||||
): Promise<void> {
|
||||
// Wait for event to get its final ID with pendingEventOrdering: "chronological", since DM channels depend on it.
|
||||
if (event.isSending() && event.status != EventStatus.SENT) {
|
||||
let eventIdListener;
|
||||
let statusListener;
|
||||
let eventIdListener: () => void;
|
||||
let statusListener: () => void;
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
eventIdListener = resolve;
|
||||
statusListener = () => {
|
||||
if (event.status == EventStatus.CANCELLED) {
|
||||
@ -3372,11 +3369,11 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
logger.error("error while waiting for the verification event to be sent: ", err);
|
||||
return;
|
||||
} finally {
|
||||
event.removeListener(MatrixEventEvent.LocalEventIdReplaced, eventIdListener);
|
||||
event.removeListener(MatrixEventEvent.Status, statusListener);
|
||||
event.removeListener(MatrixEventEvent.LocalEventIdReplaced, eventIdListener!);
|
||||
event.removeListener(MatrixEventEvent.Status, statusListener!);
|
||||
}
|
||||
}
|
||||
let request = requestsMap.getRequest(event);
|
||||
let request: VerificationRequest | undefined = requestsMap.getRequest(event);
|
||||
let isNewRequest = false;
|
||||
if (!request) {
|
||||
request = createRequest(event);
|
||||
@ -3539,13 +3536,14 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
// this way we don't start device queries after sync on behalf of this room which we won't use
|
||||
// the result of anyway, as we'll need to do a query again once all the members are fetched
|
||||
// by calling _trackRoomDevices
|
||||
if (this.roomDeviceTrackingState[roomId]) {
|
||||
if (roomId in this.roomDeviceTrackingState) {
|
||||
if (member.membership == 'join') {
|
||||
logger.log('Join event for ' + member.userId + ' in ' + roomId);
|
||||
// make sure we are tracking the deviceList for this user
|
||||
this.deviceList.startTrackingDeviceList(member.userId);
|
||||
} else if (member.membership == 'invite' &&
|
||||
this.clientStore.getRoom(roomId).shouldEncryptForInvitedMembers()) {
|
||||
this.clientStore.getRoom(roomId)?.shouldEncryptForInvitedMembers()
|
||||
) {
|
||||
logger.log('Invite event for ' + member.userId + ' in ' + roomId);
|
||||
this.deviceList.startTrackingDeviceList(member.userId);
|
||||
}
|
||||
@ -3635,7 +3633,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
logger.debug(`room key request for unencrypted room ${roomId}`);
|
||||
return;
|
||||
}
|
||||
const encryptor = this.roomEncryptors.get(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}`);
|
||||
@ -3643,7 +3641,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
}
|
||||
|
||||
try {
|
||||
await encryptor.reshareKeyWithDevice(body.sender_key, body.session_id, userId, device);
|
||||
await encryptor.reshareKeyWithDevice!(body.sender_key, body.session_id, userId, device);
|
||||
} catch (e) {
|
||||
logger.warn(
|
||||
"Failed to re-share keys for session " + body.session_id +
|
||||
@ -3676,7 +3674,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
return;
|
||||
}
|
||||
|
||||
const decryptor = this.roomDecryptors.get(roomId).get(alg);
|
||||
const decryptor = this.roomDecryptors.get(roomId)!.get(alg);
|
||||
if (!decryptor) {
|
||||
logger.log(`room key request for unknown alg ${alg} in room ${roomId}`);
|
||||
return;
|
||||
@ -3741,11 +3739,10 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
* @raises {module:crypto.algorithms.DecryptionError} if the algorithm is
|
||||
* unknown
|
||||
*/
|
||||
public getRoomDecryptor(roomId: string, algorithm: string): DecryptionAlgorithm {
|
||||
public getRoomDecryptor(roomId: string | null, algorithm: string): DecryptionAlgorithm {
|
||||
let decryptors: Map<string, DecryptionAlgorithm> | undefined;
|
||||
let alg: DecryptionAlgorithm | undefined;
|
||||
|
||||
roomId = roomId || null;
|
||||
if (roomId) {
|
||||
decryptors = this.roomDecryptors.get(roomId);
|
||||
if (!decryptors) {
|
||||
@ -3771,7 +3768,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
crypto: this,
|
||||
olmDevice: this.olmDevice,
|
||||
baseApis: this.baseApis,
|
||||
roomId: roomId,
|
||||
roomId: roomId ?? undefined,
|
||||
});
|
||||
|
||||
if (decryptors) {
|
||||
@ -3865,7 +3862,7 @@ export class IncomingRoomKeyRequest {
|
||||
constructor(event: MatrixEvent) {
|
||||
const content = event.getContent();
|
||||
|
||||
this.userId = event.getSender();
|
||||
this.userId = event.getSender()!;
|
||||
this.deviceId = content.requesting_device_id;
|
||||
this.requestId = content.request_id;
|
||||
this.requestBody = content.body || {};
|
||||
@ -3890,7 +3887,7 @@ class IncomingRoomKeyRequestCancellation {
|
||||
constructor(event: MatrixEvent) {
|
||||
const content = event.getContent();
|
||||
|
||||
this.userId = event.getSender();
|
||||
this.userId = event.getSender()!;
|
||||
this.deviceId = content.requesting_device_id;
|
||||
this.requestId = content.request_id;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import * as bs58 from 'bs58';
|
||||
// (which are also base58 encoded, but bitcoin's involve a lot more hashing)
|
||||
const OLM_RECOVERY_KEY_PREFIX = [0x8B, 0x01];
|
||||
|
||||
export function encodeRecoveryKey(key: ArrayLike<number>): string {
|
||||
export function encodeRecoveryKey(key: ArrayLike<number>): string | undefined {
|
||||
const buf = Buffer.alloc(OLM_RECOVERY_KEY_PREFIX.length + key.length + 1);
|
||||
buf.set(OLM_RECOVERY_KEY_PREFIX, 0);
|
||||
buf.set(key, OLM_RECOVERY_KEY_PREFIX.length);
|
||||
@ -32,7 +32,7 @@ export function encodeRecoveryKey(key: ArrayLike<number>): string {
|
||||
buf[buf.length - 1] = parity;
|
||||
const base58key = bs58.encode(buf);
|
||||
|
||||
return base58key.match(/.{1,4}/g).join(" ");
|
||||
return base58key.match(/.{1,4}/g)?.join(" ");
|
||||
}
|
||||
|
||||
export function decodeRecoveryKey(recoveryKey: string): Uint8Array {
|
||||
|
@ -678,7 +678,7 @@ export class Backend implements CryptoStore {
|
||||
senderCurve25519Key, sessionId, session: sessionData,
|
||||
});
|
||||
addReq.onerror = (ev) => {
|
||||
if (addReq.error.name === 'ConstraintError') {
|
||||
if (addReq.error?.name === 'ConstraintError') {
|
||||
// This stops the error from triggering the txn's onerror
|
||||
ev.stopPropagation();
|
||||
// ...and this stops it from aborting the transaction
|
||||
|
@ -34,7 +34,7 @@ import { ListenerMap, TypedEventEmitter } from "../../models/typed-event-emitter
|
||||
const timeoutException = new Error("Verification timed out");
|
||||
|
||||
export class SwitchStartEventError extends Error {
|
||||
constructor(public readonly startEvent: MatrixEvent) {
|
||||
constructor(public readonly startEvent: MatrixEvent | null) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@ -96,7 +96,7 @@ export class VerificationBase<
|
||||
public readonly baseApis: MatrixClient,
|
||||
public readonly userId: string,
|
||||
public readonly deviceId: string,
|
||||
public startEvent: MatrixEvent,
|
||||
public startEvent: MatrixEvent | null,
|
||||
public readonly request: VerificationRequest,
|
||||
) {
|
||||
super();
|
||||
|
@ -23,7 +23,7 @@ limitations under the License.
|
||||
import { MatrixEvent } from "../../models/event";
|
||||
import { EventType } from '../../@types/event';
|
||||
|
||||
export function newVerificationError(code: string, reason: string, extraData: Record<string, any>): MatrixEvent {
|
||||
export function newVerificationError(code: string, reason: string, extraData?: Record<string, any>): MatrixEvent {
|
||||
const content = Object.assign({}, { code, reason }, extraData);
|
||||
return new MatrixEvent({
|
||||
type: EventType.KeyVerificationCancel,
|
||||
|
@ -147,7 +147,7 @@ interface IQrData {
|
||||
prefix: string;
|
||||
version: number;
|
||||
mode: Mode;
|
||||
transactionId: string;
|
||||
transactionId?: string;
|
||||
firstKeyB64: string;
|
||||
secondKeyB64: string;
|
||||
secretB64: string;
|
||||
@ -250,7 +250,7 @@ export class QRCodeData {
|
||||
): IQrData {
|
||||
const myUserId = client.getUserId()!;
|
||||
const transactionId = request.channel.transactionId;
|
||||
const qrData = {
|
||||
const qrData: IQrData = {
|
||||
prefix: BINARY_PREFIX,
|
||||
version: CODE_VERSION,
|
||||
mode,
|
||||
|
@ -233,10 +233,10 @@ type EventHandlerMap = {
|
||||
* @extends {module:crypto/verification/Base}
|
||||
*/
|
||||
export class SAS extends Base<SasEvent, EventHandlerMap> {
|
||||
private waitingForAccept: boolean;
|
||||
public ourSASPubKey: string;
|
||||
public theirSASPubKey: string;
|
||||
public sasEvent: ISasEvent;
|
||||
private waitingForAccept?: boolean;
|
||||
public ourSASPubKey?: string;
|
||||
public theirSASPubKey?: string;
|
||||
public sasEvent?: ISasEvent;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
public static get NAME(): string {
|
||||
@ -279,7 +279,7 @@ export class SAS extends Base<SasEvent, EventHandlerMap> {
|
||||
return false;
|
||||
}
|
||||
const content = event.getContent();
|
||||
return content && content.method === SAS.NAME && this.waitingForAccept;
|
||||
return content?.method === SAS.NAME && !!this.waitingForAccept;
|
||||
}
|
||||
|
||||
private async sendStart(): Promise<Record<string, any>> {
|
||||
@ -400,7 +400,7 @@ export class SAS extends Base<SasEvent, EventHandlerMap> {
|
||||
private async doRespondVerification(): Promise<void> {
|
||||
// as m.related_to is not included in the encrypted content in e2e rooms,
|
||||
// we need to make sure it is added
|
||||
let content = this.channel.completedContentFromEvent(this.startEvent);
|
||||
let content = this.channel.completedContentFromEvent(this.startEvent!);
|
||||
|
||||
// Note: we intersect using our pre-made lists, rather than the sets,
|
||||
// so that the result will be in our order of preference. Then
|
||||
|
@ -19,10 +19,10 @@ import { VerificationRequest } from "./VerificationRequest";
|
||||
|
||||
export interface IVerificationChannel {
|
||||
request?: VerificationRequest;
|
||||
readonly userId: string;
|
||||
readonly userId?: string;
|
||||
readonly roomId?: string;
|
||||
readonly deviceId?: string;
|
||||
readonly transactionId: string;
|
||||
readonly transactionId?: string;
|
||||
readonly receiveStartFromOtherDevices?: boolean;
|
||||
getTimestamp(event: MatrixEvent): number;
|
||||
send(type: string, uncompletedContent: Record<string, any>): Promise<void>;
|
||||
|
@ -37,7 +37,7 @@ const M_RELATES_TO = "m.relates_to";
|
||||
* Uses the event id of the initial m.key.verification.request event as a transaction id.
|
||||
*/
|
||||
export class InRoomChannel implements IVerificationChannel {
|
||||
private requestEventId: string = null;
|
||||
private requestEventId?: string;
|
||||
|
||||
/**
|
||||
* @param {MatrixClient} client the matrix client, to send messages with and get current user & device from.
|
||||
@ -47,7 +47,7 @@ export class InRoomChannel implements IVerificationChannel {
|
||||
constructor(
|
||||
private readonly client: MatrixClient,
|
||||
public readonly roomId: string,
|
||||
public userId: string = null,
|
||||
public userId?: string,
|
||||
) {
|
||||
}
|
||||
|
||||
@ -56,11 +56,11 @@ export class InRoomChannel implements IVerificationChannel {
|
||||
}
|
||||
|
||||
/** The transaction id generated/used by this verification channel */
|
||||
public get transactionId(): string {
|
||||
public get transactionId(): string | undefined {
|
||||
return this.requestEventId;
|
||||
}
|
||||
|
||||
public static getOtherPartyUserId(event: MatrixEvent, client: MatrixClient): string {
|
||||
public static getOtherPartyUserId(event: MatrixEvent, client: MatrixClient): string | undefined {
|
||||
const type = InRoomChannel.getEventType(event);
|
||||
if (type !== REQUEST_TYPE) {
|
||||
return;
|
||||
@ -103,12 +103,12 @@ export class InRoomChannel implements IVerificationChannel {
|
||||
* @param {MatrixEvent} event the event
|
||||
* @returns {string} the transaction id
|
||||
*/
|
||||
public static getTransactionId(event: MatrixEvent): string {
|
||||
public static getTransactionId(event: MatrixEvent): string | undefined {
|
||||
if (InRoomChannel.getEventType(event) === REQUEST_TYPE) {
|
||||
return event.getId();
|
||||
} else {
|
||||
const relation = event.getRelation();
|
||||
if (relation && relation.rel_type === M_REFERENCE) {
|
||||
if (relation?.rel_type === M_REFERENCE) {
|
||||
return relation.event_id;
|
||||
}
|
||||
}
|
||||
@ -184,10 +184,10 @@ export class InRoomChannel implements IVerificationChannel {
|
||||
* @param {boolean} isLiveEvent whether this is an even received through sync or not
|
||||
* @returns {Promise} a promise that resolves when any requests as an answer to the passed-in event are sent.
|
||||
*/
|
||||
public handleEvent(event: MatrixEvent, request: VerificationRequest, isLiveEvent = false): Promise<void> {
|
||||
public async handleEvent(event: MatrixEvent, request: VerificationRequest, isLiveEvent = false): Promise<void> {
|
||||
// prevent processing the same event multiple times, as under
|
||||
// some circumstances Room.timeline can get emitted twice for the same event
|
||||
if (request.hasEventId(event.getId())) {
|
||||
if (request.hasEventId(event.getId()!)) {
|
||||
return;
|
||||
}
|
||||
const type = InRoomChannel.getEventType(event);
|
||||
@ -198,7 +198,7 @@ export class InRoomChannel implements IVerificationChannel {
|
||||
return;
|
||||
}
|
||||
// set userId if not set already
|
||||
if (this.userId === null) {
|
||||
if (!this.userId) {
|
||||
const userId = InRoomChannel.getOtherPartyUserId(event, this.client);
|
||||
if (userId) {
|
||||
this.userId = userId;
|
||||
@ -207,14 +207,13 @@ export class InRoomChannel implements IVerificationChannel {
|
||||
// ignore events not sent by us or the other party
|
||||
const ownUserId = this.client.getUserId();
|
||||
const sender = event.getSender();
|
||||
if (this.userId !== null) {
|
||||
if (this.userId) {
|
||||
if (sender !== ownUserId && sender !== this.userId) {
|
||||
logger.log(`InRoomChannel: ignoring verification event from ` +
|
||||
`non-participating sender ${sender}`);
|
||||
logger.log(`InRoomChannel: ignoring verification event from non-participating sender ${sender}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.requestEventId === null) {
|
||||
if (!this.requestEventId) {
|
||||
this.requestEventId = InRoomChannel.getTransactionId(event);
|
||||
}
|
||||
|
||||
@ -236,7 +235,7 @@ export class InRoomChannel implements IVerificationChannel {
|
||||
// ensure m.related_to is included in e2ee rooms
|
||||
// as the field is excluded from encryption
|
||||
const content = Object.assign({}, event.getContent());
|
||||
content[M_RELATES_TO] = event.getRelation();
|
||||
content[M_RELATES_TO] = event.getRelation()!;
|
||||
return content;
|
||||
}
|
||||
|
||||
@ -307,17 +306,17 @@ export class InRoomChannel implements IVerificationChannel {
|
||||
export class InRoomRequests implements IRequestsMap {
|
||||
private requestsByRoomId = new Map<string, Map<string, VerificationRequest>>();
|
||||
|
||||
public getRequest(event: MatrixEvent): VerificationRequest {
|
||||
const roomId = event.getRoomId();
|
||||
const txnId = InRoomChannel.getTransactionId(event);
|
||||
public getRequest(event: MatrixEvent): VerificationRequest | undefined {
|
||||
const roomId = event.getRoomId()!;
|
||||
const txnId = InRoomChannel.getTransactionId(event)!;
|
||||
return this.getRequestByTxnId(roomId, txnId);
|
||||
}
|
||||
|
||||
public getRequestByChannel(channel: InRoomChannel): VerificationRequest {
|
||||
return this.getRequestByTxnId(channel.roomId, channel.transactionId);
|
||||
public getRequestByChannel(channel: InRoomChannel): VerificationRequest | undefined {
|
||||
return this.getRequestByTxnId(channel.roomId, channel.transactionId!);
|
||||
}
|
||||
|
||||
private getRequestByTxnId(roomId: string, txnId: string): VerificationRequest {
|
||||
private getRequestByTxnId(roomId: string, txnId: string): VerificationRequest | undefined {
|
||||
const requestsByTxnId = this.requestsByRoomId.get(roomId);
|
||||
if (requestsByTxnId) {
|
||||
return requestsByTxnId.get(txnId);
|
||||
@ -325,11 +324,11 @@ export class InRoomRequests implements IRequestsMap {
|
||||
}
|
||||
|
||||
public setRequest(event: MatrixEvent, request: VerificationRequest): void {
|
||||
this.doSetRequest(event.getRoomId(), InRoomChannel.getTransactionId(event), request);
|
||||
this.doSetRequest(event.getRoomId()!, InRoomChannel.getTransactionId(event)!, request);
|
||||
}
|
||||
|
||||
public setRequestByChannel(channel: IVerificationChannel, request: VerificationRequest): void {
|
||||
this.doSetRequest(channel.roomId, channel.transactionId, request);
|
||||
this.doSetRequest(channel.roomId!, channel.transactionId!, request);
|
||||
}
|
||||
|
||||
private doSetRequest(roomId: string, txnId: string, request: VerificationRequest): void {
|
||||
@ -342,17 +341,17 @@ export class InRoomRequests implements IRequestsMap {
|
||||
}
|
||||
|
||||
public removeRequest(event: MatrixEvent): void {
|
||||
const roomId = event.getRoomId();
|
||||
const roomId = event.getRoomId()!;
|
||||
const requestsByTxnId = this.requestsByRoomId.get(roomId);
|
||||
if (requestsByTxnId) {
|
||||
requestsByTxnId.delete(InRoomChannel.getTransactionId(event));
|
||||
requestsByTxnId.delete(InRoomChannel.getTransactionId(event)!);
|
||||
if (requestsByTxnId.size === 0) {
|
||||
this.requestsByRoomId.delete(roomId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public findRequestInProgress(roomId: string): VerificationRequest {
|
||||
public findRequestInProgress(roomId: string): VerificationRequest | undefined {
|
||||
const requestsByTxnId = this.requestsByRoomId.get(roomId);
|
||||
if (requestsByTxnId) {
|
||||
for (const request of requestsByTxnId.values()) {
|
||||
|
@ -46,8 +46,8 @@ export class ToDeviceChannel implements IVerificationChannel {
|
||||
private readonly client: MatrixClient,
|
||||
public readonly userId: string,
|
||||
private readonly devices: string[],
|
||||
public transactionId: string = null,
|
||||
public deviceId: string = null,
|
||||
public transactionId?: string,
|
||||
public deviceId?: string,
|
||||
) {}
|
||||
|
||||
public isToDevices(devices: string[]): boolean {
|
||||
@ -173,13 +173,11 @@ export class ToDeviceChannel implements IVerificationChannel {
|
||||
return this.sendToDevices(CANCEL_TYPE, cancelContent, [deviceId]);
|
||||
}
|
||||
}
|
||||
const wasStarted = request.phase === PHASE_STARTED ||
|
||||
request.phase === PHASE_READY;
|
||||
const wasStarted = request.phase === PHASE_STARTED || request.phase === PHASE_READY;
|
||||
|
||||
await request.handleEvent(event.getType(), event, isLiveEvent, false, false);
|
||||
|
||||
const isStarted = request.phase === PHASE_STARTED ||
|
||||
request.phase === PHASE_READY;
|
||||
const isStarted = request.phase === PHASE_STARTED || request.phase === PHASE_READY;
|
||||
|
||||
const isAcceptingEvent = type === START_TYPE || type === READY_TYPE;
|
||||
// the request has picked a ready or start event, tell the other devices about it
|
||||
@ -256,16 +254,16 @@ export class ToDeviceChannel implements IVerificationChannel {
|
||||
if (type === REQUEST_TYPE || (type === CANCEL_TYPE && !this.deviceId)) {
|
||||
result = await this.sendToDevices(type, content, this.devices);
|
||||
} else {
|
||||
result = await this.sendToDevices(type, content, [this.deviceId]);
|
||||
result = await this.sendToDevices(type, content, [this.deviceId!]);
|
||||
}
|
||||
// the VerificationRequest state machine requires remote echos of the event
|
||||
// the client sends itself, so we fake this for to_device messages
|
||||
const remoteEchoEvent = new MatrixEvent({
|
||||
sender: this.client.getUserId(),
|
||||
sender: this.client.getUserId()!,
|
||||
content,
|
||||
type,
|
||||
});
|
||||
await this.request.handleEvent(
|
||||
await this.request!.handleEvent(
|
||||
type,
|
||||
remoteEchoEvent,
|
||||
/*isLiveEvent=*/true,
|
||||
@ -298,18 +296,18 @@ export class ToDeviceChannel implements IVerificationChannel {
|
||||
export class ToDeviceRequests implements IRequestsMap {
|
||||
private requestsByUserId = new Map<string, Map<string, Request>>();
|
||||
|
||||
public getRequest(event: MatrixEvent): Request {
|
||||
public getRequest(event: MatrixEvent): Request | undefined {
|
||||
return this.getRequestBySenderAndTxnId(
|
||||
event.getSender(),
|
||||
event.getSender()!,
|
||||
ToDeviceChannel.getTransactionId(event),
|
||||
);
|
||||
}
|
||||
|
||||
public getRequestByChannel(channel: ToDeviceChannel): Request {
|
||||
return this.getRequestBySenderAndTxnId(channel.userId, channel.transactionId);
|
||||
public getRequestByChannel(channel: ToDeviceChannel): Request | undefined {
|
||||
return this.getRequestBySenderAndTxnId(channel.userId, channel.transactionId!);
|
||||
}
|
||||
|
||||
public getRequestBySenderAndTxnId(sender: string, txnId: string): Request {
|
||||
public getRequestBySenderAndTxnId(sender: string, txnId: string): Request | undefined {
|
||||
const requestsByTxnId = this.requestsByUserId.get(sender);
|
||||
if (requestsByTxnId) {
|
||||
return requestsByTxnId.get(txnId);
|
||||
@ -317,11 +315,11 @@ export class ToDeviceRequests implements IRequestsMap {
|
||||
}
|
||||
|
||||
public setRequest(event: MatrixEvent, request: Request): void {
|
||||
this.setRequestBySenderAndTxnId(event.getSender(), ToDeviceChannel.getTransactionId(event), request);
|
||||
this.setRequestBySenderAndTxnId(event.getSender()!, ToDeviceChannel.getTransactionId(event), request);
|
||||
}
|
||||
|
||||
public setRequestByChannel(channel: ToDeviceChannel, request: Request): void {
|
||||
this.setRequestBySenderAndTxnId(channel.userId, channel.transactionId, request);
|
||||
this.setRequestBySenderAndTxnId(channel.userId, channel.transactionId!, request);
|
||||
}
|
||||
|
||||
public setRequestBySenderAndTxnId(sender: string, txnId: string, request: Request): void {
|
||||
@ -334,7 +332,7 @@ export class ToDeviceRequests implements IRequestsMap {
|
||||
}
|
||||
|
||||
public removeRequest(event: MatrixEvent): void {
|
||||
const userId = event.getSender();
|
||||
const userId = event.getSender()!;
|
||||
const requestsByTxnId = this.requestsByUserId.get(userId);
|
||||
if (requestsByTxnId) {
|
||||
requestsByTxnId.delete(ToDeviceChannel.getTransactionId(event));
|
||||
@ -344,7 +342,7 @@ export class ToDeviceRequests implements IRequestsMap {
|
||||
}
|
||||
}
|
||||
|
||||
public findRequestInProgress(userId: string, devices: string[]): Request {
|
||||
public findRequestInProgress(userId: string, devices: string[]): Request | undefined {
|
||||
const requestsByTxnId = this.requestsByUserId.get(userId);
|
||||
if (requestsByTxnId) {
|
||||
for (const request of requestsByTxnId.values()) {
|
||||
|
@ -112,8 +112,8 @@ export class VerificationRequest<
|
||||
private requestReceivedAt: number | null = null;
|
||||
|
||||
private commonMethods: VerificationMethod[] = [];
|
||||
private _phase: Phase;
|
||||
public _cancellingUserId: string; // Used in tests only
|
||||
private _phase!: Phase;
|
||||
public _cancellingUserId?: string; // Used in tests only
|
||||
private _verifier?: VerificationBase<any, any>;
|
||||
|
||||
constructor(
|
||||
@ -357,7 +357,7 @@ export class VerificationRequest<
|
||||
|
||||
/** The user id of the other party in this request */
|
||||
public get otherUserId(): string {
|
||||
return this.channel.userId;
|
||||
return this.channel.userId!;
|
||||
}
|
||||
|
||||
public get isSelfVerification(): boolean {
|
||||
@ -372,7 +372,7 @@ export class VerificationRequest<
|
||||
const myCancel = this.eventsByUs.get(CANCEL_TYPE);
|
||||
const theirCancel = this.eventsByThem.get(CANCEL_TYPE);
|
||||
|
||||
if (myCancel && (!theirCancel || myCancel.getId() < theirCancel.getId())) {
|
||||
if (myCancel && (!theirCancel || myCancel.getId()! < theirCancel.getId()!)) {
|
||||
return myCancel.getSender();
|
||||
}
|
||||
if (theirCancel) {
|
||||
@ -405,8 +405,8 @@ export class VerificationRequest<
|
||||
this.eventsByThem.get(REQUEST_TYPE) ||
|
||||
this.eventsByThem.get(READY_TYPE) ||
|
||||
this.eventsByThem.get(START_TYPE);
|
||||
const theirFirstContent = theirFirstEvent.getContent();
|
||||
const fromDevice = theirFirstContent.from_device;
|
||||
const theirFirstContent = theirFirstEvent?.getContent();
|
||||
const fromDevice = theirFirstContent?.from_device;
|
||||
return {
|
||||
userId: this.otherUserId,
|
||||
deviceId: fromDevice,
|
||||
@ -559,7 +559,9 @@ export class VerificationRequest<
|
||||
const ourStartEvent = this.eventsByUs.get(START_TYPE);
|
||||
// any party can send .start after a .ready or unsent
|
||||
if (theirStartEvent && ourStartEvent) {
|
||||
startEvent = theirStartEvent.getSender() < ourStartEvent.getSender() ? theirStartEvent : ourStartEvent;
|
||||
startEvent = theirStartEvent.getSender()! < ourStartEvent.getSender()!
|
||||
? theirStartEvent
|
||||
: ourStartEvent;
|
||||
} else {
|
||||
startEvent = theirStartEvent ? theirStartEvent : ourStartEvent;
|
||||
}
|
||||
@ -595,7 +597,7 @@ export class VerificationRequest<
|
||||
// get common methods
|
||||
if (phase === PHASE_REQUESTED || phase === PHASE_READY) {
|
||||
if (!this.wasSentByOwnDevice(event)) {
|
||||
const content = event.getContent<{
|
||||
const content = event!.getContent<{
|
||||
methods: string[];
|
||||
}>();
|
||||
this.commonMethods =
|
||||
@ -620,7 +622,7 @@ export class VerificationRequest<
|
||||
}
|
||||
// create verifier
|
||||
if (phase === PHASE_STARTED) {
|
||||
const { method } = event.getContent();
|
||||
const { method } = event!.getContent();
|
||||
if (!this._verifier && !this.observeOnly) {
|
||||
this._verifier = this.createVerifier(method, event);
|
||||
if (!this._verifier) {
|
||||
@ -903,19 +905,19 @@ export class VerificationRequest<
|
||||
logger.warn("could not find verifier constructor for method", method);
|
||||
return;
|
||||
}
|
||||
return new VerifierCtor(this.channel, this.client, userId, deviceId, startEvent, this);
|
||||
return new VerifierCtor(this.channel, this.client, userId!, deviceId!, startEvent, this);
|
||||
}
|
||||
|
||||
private wasSentByOwnUser(event: MatrixEvent): boolean {
|
||||
return event.getSender() === this.client.getUserId();
|
||||
private wasSentByOwnUser(event?: MatrixEvent): boolean {
|
||||
return event?.getSender() === this.client.getUserId();
|
||||
}
|
||||
|
||||
// only for .request, .ready or .start
|
||||
private wasSentByOwnDevice(event: MatrixEvent): boolean {
|
||||
private wasSentByOwnDevice(event?: MatrixEvent): boolean {
|
||||
if (!this.wasSentByOwnUser(event)) {
|
||||
return false;
|
||||
}
|
||||
const content = event.getContent();
|
||||
const content = event!.getContent();
|
||||
if (!content || content.from_device !== this.client.getDeviceId()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ export class FilterComponent {
|
||||
// as sending a whole list of participants could be proven problematic in terms
|
||||
// of performance
|
||||
// This should be improved when bundled relationships solve that problem
|
||||
const relationSenders = [];
|
||||
const relationSenders: string[] = [];
|
||||
if (this.userId && bundledRelationships?.[THREAD_RELATION_TYPE.name]?.current_user_participated) {
|
||||
relationSenders.push(this.userId);
|
||||
}
|
||||
@ -131,8 +131,8 @@ export class FilterComponent {
|
||||
* @return {boolean} true if the event fields match the filter
|
||||
*/
|
||||
private checkFields(
|
||||
roomId: string,
|
||||
sender: string,
|
||||
roomId: string | undefined,
|
||||
sender: string | undefined,
|
||||
eventType: string,
|
||||
containsUrl: boolean,
|
||||
relationTypes: Array<RelationType | string>,
|
||||
|
@ -207,15 +207,15 @@ export class InteractiveAuth {
|
||||
private data: IAuthData;
|
||||
private emailSid?: string;
|
||||
private requestingEmailToken = false;
|
||||
private attemptAuthDeferred: IDeferred<IAuthData> = null;
|
||||
private chosenFlow: IFlow = null;
|
||||
private currentStage: string = null;
|
||||
private attemptAuthDeferred: IDeferred<IAuthData> | null = null;
|
||||
private chosenFlow: IFlow | null = null;
|
||||
private currentStage: string | null = null;
|
||||
|
||||
private emailAttempt = 1;
|
||||
|
||||
// if we are currently trying to submit an auth dict (which includes polling)
|
||||
// the promise the will resolve/reject when it completes
|
||||
private submitPromise: Promise<void> = null;
|
||||
private submitPromise: Promise<void> | null = null;
|
||||
|
||||
constructor(opts: IOpts) {
|
||||
this.matrixClient = opts.matrixClient;
|
||||
@ -229,7 +229,7 @@ export class InteractiveAuth {
|
||||
|
||||
if (opts.sessionId) this.data.session = opts.sessionId;
|
||||
this.clientSecret = opts.clientSecret || this.matrixClient.generateClientSecret();
|
||||
this.emailSid = opts.emailSid ?? null;
|
||||
this.emailSid = opts.emailSid;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -286,7 +286,7 @@ export class InteractiveAuth {
|
||||
client_secret: this.clientSecret,
|
||||
};
|
||||
if (await this.matrixClient.doesServerRequireIdServerParam()) {
|
||||
const idServerParsedUrl = new URL(this.matrixClient.getIdentityServerUrl());
|
||||
const idServerParsedUrl = new URL(this.matrixClient.getIdentityServerUrl()!);
|
||||
creds.id_server = idServerParsedUrl.host;
|
||||
}
|
||||
authDict = {
|
||||
@ -308,7 +308,7 @@ export class InteractiveAuth {
|
||||
*
|
||||
* @return {string} session id
|
||||
*/
|
||||
public getSessionId(): string {
|
||||
public getSessionId(): string | undefined {
|
||||
return this.data?.session;
|
||||
}
|
||||
|
||||
@ -332,7 +332,7 @@ export class InteractiveAuth {
|
||||
return this.data.params?.[loginType];
|
||||
}
|
||||
|
||||
public getChosenFlow(): IFlow {
|
||||
public getChosenFlow(): IFlow | null {
|
||||
return this.chosenFlow;
|
||||
}
|
||||
|
||||
@ -399,7 +399,7 @@ export class InteractiveAuth {
|
||||
*
|
||||
* @returns {string} The sid of the email auth session
|
||||
*/
|
||||
public getEmailSid(): string {
|
||||
public getEmailSid(): string | undefined {
|
||||
return this.emailSid;
|
||||
}
|
||||
|
||||
@ -457,7 +457,7 @@ export class InteractiveAuth {
|
||||
private async doRequest(auth: IAuthData, background = false): Promise<void> {
|
||||
try {
|
||||
const result = await this.requestCallback(auth, background);
|
||||
this.attemptAuthDeferred.resolve(result);
|
||||
this.attemptAuthDeferred!.resolve(result);
|
||||
this.attemptAuthDeferred = null;
|
||||
} catch (error) {
|
||||
// sometimes UI auth errors don't come with flows
|
||||
@ -491,12 +491,12 @@ export class InteractiveAuth {
|
||||
try {
|
||||
this.startNextAuthStage();
|
||||
} catch (e) {
|
||||
this.attemptAuthDeferred.reject(e);
|
||||
this.attemptAuthDeferred!.reject(e);
|
||||
this.attemptAuthDeferred = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.emailSid && this.chosenFlow.stages.includes(AuthType.Email)) {
|
||||
if (!this.emailSid && this.chosenFlow?.stages.includes(AuthType.Email)) {
|
||||
try {
|
||||
await this.requestEmailToken();
|
||||
// NB. promise is not resolved here - at some point, doRequest
|
||||
@ -512,7 +512,7 @@ export class InteractiveAuth {
|
||||
// to do) or it could be a network failure. Either way, pass
|
||||
// the failure up as the user can't complete auth if we can't
|
||||
// send the email, for whatever reason.
|
||||
this.attemptAuthDeferred.reject(e);
|
||||
this.attemptAuthDeferred!.reject(e);
|
||||
this.attemptAuthDeferred = null;
|
||||
}
|
||||
}
|
||||
@ -559,7 +559,7 @@ export class InteractiveAuth {
|
||||
* @return {string?} login type
|
||||
* @throws {NoAuthFlowFoundError} If no suitable authentication flow can be found
|
||||
*/
|
||||
private chooseStage(): AuthType {
|
||||
private chooseStage(): AuthType | undefined {
|
||||
if (this.chosenFlow === null) {
|
||||
this.chosenFlow = this.chooseFlow();
|
||||
}
|
||||
@ -625,7 +625,7 @@ export class InteractiveAuth {
|
||||
* @param {object} flow
|
||||
* @return {string} login type
|
||||
*/
|
||||
private firstUncompletedStage(flow: IFlow): AuthType {
|
||||
private firstUncompletedStage(flow: IFlow): AuthType | undefined {
|
||||
const completed = this.data.completed || [];
|
||||
for (let i = 0; i < flow.stages.length; ++i) {
|
||||
const stageType = flow.stages[i];
|
||||
|
@ -35,7 +35,7 @@ const DEFAULT_NAMESPACE = "matrix";
|
||||
// console methods at initialization time by a factory that looks up the console methods
|
||||
// when logging so we always get the current value of console methods.
|
||||
log.methodFactory = function(methodName, logLevel, loggerName) {
|
||||
return function(...args) {
|
||||
return function(this: PrefixedLogger, ...args) {
|
||||
/* eslint-disable @typescript-eslint/no-invalid-this */
|
||||
if (this.prefix) {
|
||||
args.unshift(this.prefix);
|
||||
|
@ -62,7 +62,7 @@ export class MSC3089Branch {
|
||||
}
|
||||
|
||||
private get roomId(): string {
|
||||
return this.indexEvent.getRoomId();
|
||||
return this.indexEvent.getRoomId()!;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -223,7 +223,7 @@ export class MSC3089Branch {
|
||||
do {
|
||||
childEvent = timelineEvents.find(e => e.replacingEventId() === parentEvent.getId());
|
||||
if (childEvent) {
|
||||
const branch = this.directory.getFile(childEvent.getId());
|
||||
const branch = this.directory.getFile(childEvent.getId()!);
|
||||
if (branch) {
|
||||
fileHistory.push(branch);
|
||||
parentEvent = childEvent;
|
||||
|
@ -73,7 +73,7 @@ export class Beacon extends TypedEventEmitter<Exclude<BeaconEvent, BeaconEvent.N
|
||||
}
|
||||
|
||||
public get beaconInfoId(): string {
|
||||
return this.rootEvent.getId();
|
||||
return this.rootEvent.getId()!;
|
||||
}
|
||||
|
||||
public get beaconInfoOwner(): string {
|
||||
|
@ -95,8 +95,8 @@ export class EventContext {
|
||||
* @param {boolean} backwards true to set the pagination token for going
|
||||
* backwards in time
|
||||
*/
|
||||
public setPaginateToken(token: string, backwards = false): void {
|
||||
this.paginateTokens[backwards ? Direction.Backward : Direction.Forward] = token;
|
||||
public setPaginateToken(token?: string, backwards = false): void {
|
||||
this.paginateTokens[backwards ? Direction.Backward : Direction.Forward] = token ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -459,7 +459,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
let lastEventWasNew = false;
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
const event = events[i];
|
||||
const eventId = event.getId();
|
||||
const eventId = event.getId()!;
|
||||
|
||||
const existingTimeline = this._eventIdToTimeline.get(eventId);
|
||||
|
||||
@ -612,7 +612,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
}
|
||||
}
|
||||
|
||||
const timeline = this._eventIdToTimeline.get(event.getId());
|
||||
const timeline = this._eventIdToTimeline.get(event.getId()!);
|
||||
if (timeline) {
|
||||
if (duplicateStrategy === DuplicateStrategy.Replace) {
|
||||
debuglog("EventTimelineSet.addLiveEvent: replacing duplicate event " + event.getId());
|
||||
@ -702,7 +702,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
);
|
||||
}
|
||||
|
||||
const eventId = event.getId();
|
||||
const eventId = event.getId()!;
|
||||
timeline.addEvent(event, {
|
||||
toStartOfTimeline,
|
||||
roomState,
|
||||
|
@ -74,7 +74,7 @@ export class EventTimeline {
|
||||
// check this to avoid overriding non-sentinel members by sentinel ones
|
||||
// when adding the event to a filtered timeline
|
||||
if (!event.sender?.events?.member) {
|
||||
event.sender = stateContext.getSentinelMember(event.getSender());
|
||||
event.sender = stateContext.getSentinelMember(event.getSender()!);
|
||||
}
|
||||
if (!event.target?.events?.member && event.getType() === EventType.RoomMember) {
|
||||
event.target = stateContext.getSentinelMember(event.getStateKey()!);
|
||||
|
@ -71,7 +71,7 @@ export interface IEvent {
|
||||
type: string;
|
||||
content: IContent;
|
||||
sender: string;
|
||||
room_id: string;
|
||||
room_id?: string;
|
||||
origin_server_ts: number;
|
||||
txn_id?: string;
|
||||
state_key?: string;
|
||||
@ -392,7 +392,7 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
|
||||
* @return {string} The event ID, e.g. <code>$143350589368169JsLZx:localhost
|
||||
* </code>
|
||||
*/
|
||||
public getId(): string {
|
||||
public getId(): string | undefined {
|
||||
return this.event.event_id;
|
||||
}
|
||||
|
||||
@ -400,7 +400,7 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
|
||||
* Get the user_id for this event.
|
||||
* @return {string} The user ID, e.g. <code>@alice:matrix.org</code>
|
||||
*/
|
||||
public getSender(): string {
|
||||
public getSender(): string | undefined {
|
||||
return this.event.sender || this.event.user_id; // v2 / v1
|
||||
}
|
||||
|
||||
@ -521,7 +521,7 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
|
||||
return !!threadDetails || (this.getThread()?.id === this.getId());
|
||||
}
|
||||
|
||||
public get replyEventId(): string {
|
||||
public get replyEventId(): string | undefined {
|
||||
// We're prefer ev.getContent() over ev.getWireContent() to make sure
|
||||
// we grab the latest edit with potentially new relations. But we also
|
||||
// can't just rely on ev.getContent() by itself because historically we
|
||||
@ -748,12 +748,14 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
|
||||
// original sending device if it wasn't us.
|
||||
const wireContent = this.getWireContent();
|
||||
const recipients = [{
|
||||
userId, deviceId: '*',
|
||||
userId,
|
||||
deviceId: '*',
|
||||
}];
|
||||
const sender = this.getSender();
|
||||
if (sender !== userId) {
|
||||
recipients.push({
|
||||
userId: sender, deviceId: wireContent.device_id,
|
||||
userId: sender!,
|
||||
deviceId: wireContent.device_id,
|
||||
});
|
||||
}
|
||||
return recipients;
|
||||
@ -1387,7 +1389,7 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
|
||||
return new Date(ts);
|
||||
}
|
||||
} else if (this._replacingEvent) {
|
||||
return this._replacingEvent.getDate();
|
||||
return this._replacingEvent.getDate() ?? undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ export const MAIN_ROOM_TIMELINE = "main";
|
||||
export function synthesizeReceipt(userId: string, event: MatrixEvent, receiptType: ReceiptType): MatrixEvent {
|
||||
return new MatrixEvent({
|
||||
content: {
|
||||
[event.getId()]: {
|
||||
[event.getId()!]: {
|
||||
[receiptType]: {
|
||||
[userId]: {
|
||||
ts: event.getTs(),
|
||||
@ -241,7 +241,7 @@ export abstract class ReadReceipt<
|
||||
* an empty list.
|
||||
*/
|
||||
public getReceiptsForEvent(event: MatrixEvent): CachedReceipt[] {
|
||||
return this.receiptCacheByEventId[event.getId()] || [];
|
||||
return this.receiptCacheByEventId[event.getId()!] || [];
|
||||
}
|
||||
|
||||
public abstract addReceipt(event: MatrixEvent, synthetic: boolean): void;
|
||||
|
@ -26,7 +26,7 @@ export class RelatedRelations {
|
||||
}
|
||||
|
||||
public getRelations(): MatrixEvent[] {
|
||||
return this.relations.reduce((c, p) => [...c, ...p.getRelations()], []);
|
||||
return this.relations.reduce<MatrixEvent[]>((c, p) => [...c, ...p.getRelations()], []);
|
||||
}
|
||||
|
||||
public on<T extends RelationsEvent>(ev: T, fn: Listener<RelationsEvent, EventHandlerMap, T>) {
|
||||
|
@ -74,7 +74,7 @@ export class RelationsContainer {
|
||||
* @param {MatrixEvent} event The event to check as relation target.
|
||||
*/
|
||||
public aggregateParentEvent(event: MatrixEvent): void {
|
||||
const relationsForEvent = this.relations.get(event.getId());
|
||||
const relationsForEvent = this.relations.get(event.getId()!);
|
||||
if (!relationsForEvent) return;
|
||||
|
||||
for (const relationsWithRelType of relationsForEvent.values()) {
|
||||
|
@ -76,7 +76,7 @@ export class Relations extends TypedEventEmitter<RelationsEvent, EventHandlerMap
|
||||
* The new relation event to be added.
|
||||
*/
|
||||
public async addEvent(event: MatrixEvent) {
|
||||
if (this.relationEventIds.has(event.getId())) {
|
||||
if (this.relationEventIds.has(event.getId()!)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ export class Relations extends TypedEventEmitter<RelationsEvent, EventHandlerMap
|
||||
}
|
||||
|
||||
this.relations.add(event);
|
||||
this.relationEventIds.add(event.getId());
|
||||
this.relationEventIds.add(event.getId()!);
|
||||
|
||||
if (this.relationType === RelationType.Annotation) {
|
||||
this.addAnnotationToAggregation(event);
|
||||
@ -206,7 +206,7 @@ export class Relations extends TypedEventEmitter<RelationsEvent, EventHandlerMap
|
||||
return bEvents.size - aEvents.size;
|
||||
});
|
||||
|
||||
const sender = event.getSender();
|
||||
const sender = event.getSender()!;
|
||||
let eventsFromSender = this.annotationsBySender[sender];
|
||||
if (!eventsFromSender) {
|
||||
eventsFromSender = this.annotationsBySender[sender] = new Set();
|
||||
@ -231,7 +231,7 @@ export class Relations extends TypedEventEmitter<RelationsEvent, EventHandlerMap
|
||||
});
|
||||
}
|
||||
|
||||
const sender = event.getSender();
|
||||
const sender = event.getSender()!;
|
||||
const eventsFromSender = this.annotationsBySender[sender];
|
||||
if (eventsFromSender) {
|
||||
eventsFromSender.delete(event);
|
||||
|
@ -18,6 +18,8 @@ limitations under the License.
|
||||
* @module models/room
|
||||
*/
|
||||
|
||||
import { Optional } from "matrix-events-sdk";
|
||||
|
||||
import {
|
||||
EventTimelineSet,
|
||||
DuplicateStrategy,
|
||||
@ -210,7 +212,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
// read by megolm via getter; boolean value - null indicates "use global value"
|
||||
private blacklistUnverifiedDevices?: boolean;
|
||||
private selfMembership?: string;
|
||||
private summaryHeroes: string[] = null;
|
||||
private summaryHeroes: string[] | null = null;
|
||||
// flags to stop logspam about missing m.room.create events
|
||||
private getTypeWarning = false;
|
||||
private getVersionWarning = false;
|
||||
@ -238,25 +240,25 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
/**
|
||||
* The room summary.
|
||||
*/
|
||||
public summary: RoomSummary = null;
|
||||
public summary: RoomSummary | null = null;
|
||||
// legacy fields
|
||||
/**
|
||||
* The live event timeline for this room, with the oldest event at index 0.
|
||||
* Present for backwards compatibility - prefer getLiveTimeline().getEvents()
|
||||
*/
|
||||
public timeline: MatrixEvent[];
|
||||
public timeline!: MatrixEvent[];
|
||||
/**
|
||||
* oldState The state of the room at the time of the oldest
|
||||
* event in the live timeline. Present for backwards compatibility -
|
||||
* prefer getLiveTimeline().getState(EventTimeline.BACKWARDS).
|
||||
*/
|
||||
public oldState: RoomState;
|
||||
public oldState!: RoomState;
|
||||
/**
|
||||
* currentState The state of the room at the time of the
|
||||
* newest event in the timeline. Present for backwards compatibility -
|
||||
* prefer getLiveTimeline().getState(EventTimeline.FORWARDS).
|
||||
*/
|
||||
public currentState: RoomState;
|
||||
public currentState!: RoomState;
|
||||
public readonly relations = new RelationsContainer(this.client, this);
|
||||
|
||||
/**
|
||||
@ -592,7 +594,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
* @throws If <code>opts.pendingEventOrdering</code> was not 'detached'
|
||||
*/
|
||||
public getPendingEvents(): MatrixEvent[] {
|
||||
if (this.opts.pendingEventOrdering !== PendingEventOrdering.Detached) {
|
||||
if (!this.pendingEventList) {
|
||||
throw new Error(
|
||||
"Cannot call getPendingEvents with pendingEventOrdering == " +
|
||||
this.opts.pendingEventOrdering);
|
||||
@ -608,7 +610,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
* @return {boolean} True if an element was removed.
|
||||
*/
|
||||
public removePendingEvent(eventId: string): boolean {
|
||||
if (this.opts.pendingEventOrdering !== PendingEventOrdering.Detached) {
|
||||
if (!this.pendingEventList) {
|
||||
throw new Error(
|
||||
"Cannot call removePendingEvent with pendingEventOrdering == " +
|
||||
this.opts.pendingEventOrdering);
|
||||
@ -634,11 +636,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
* @return {boolean}
|
||||
*/
|
||||
public hasPendingEvent(eventId: string): boolean {
|
||||
if (this.opts.pendingEventOrdering !== PendingEventOrdering.Detached) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.pendingEventList.some(event => event.getId() === eventId);
|
||||
return this.pendingEventList?.some(event => event.getId() === eventId) ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -648,11 +646,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
* @return {MatrixEvent}
|
||||
*/
|
||||
public getPendingEvent(eventId: string): MatrixEvent | null {
|
||||
if (this.opts.pendingEventOrdering !== PendingEventOrdering.Detached) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.pendingEventList.find(event => event.getId() === eventId) ?? null;
|
||||
return this.pendingEventList?.find(event => event.getId() === eventId) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -693,17 +687,16 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
* @return {string} user id of the inviter
|
||||
*/
|
||||
public getDMInviter(): string | undefined {
|
||||
if (this.myUserId) {
|
||||
const me = this.getMember(this.myUserId);
|
||||
if (me) {
|
||||
return me.getDMInviter();
|
||||
}
|
||||
const me = this.getMember(this.myUserId);
|
||||
if (me) {
|
||||
return me.getDMInviter();
|
||||
}
|
||||
|
||||
if (this.selfMembership === "invite") {
|
||||
// fall back to summary information
|
||||
const memberCount = this.getInvitedAndJoinedMemberCount();
|
||||
if (memberCount == 2 && this.summaryHeroes.length) {
|
||||
return this.summaryHeroes[0];
|
||||
if (memberCount === 2) {
|
||||
return this.summaryHeroes?.[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -720,11 +713,8 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
return inviterId;
|
||||
}
|
||||
}
|
||||
// remember, we're assuming this room is a DM,
|
||||
// so returning the first member we find should be fine
|
||||
const hasHeroes = Array.isArray(this.summaryHeroes) &&
|
||||
this.summaryHeroes.length;
|
||||
if (hasHeroes) {
|
||||
// Remember, we're assuming this room is a DM, so returning the first member we find should be fine
|
||||
if (Array.isArray(this.summaryHeroes) && this.summaryHeroes.length) {
|
||||
return this.summaryHeroes[0];
|
||||
}
|
||||
const members = this.currentState.getMembers();
|
||||
@ -743,10 +733,9 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
if (memberCount > 2) {
|
||||
return;
|
||||
}
|
||||
const hasHeroes = Array.isArray(this.summaryHeroes) &&
|
||||
this.summaryHeroes.length;
|
||||
const hasHeroes = Array.isArray(this.summaryHeroes) && this.summaryHeroes.length;
|
||||
if (hasHeroes) {
|
||||
const availableMember = this.summaryHeroes.map((userId) => {
|
||||
const availableMember = this.summaryHeroes!.map((userId) => {
|
||||
return this.getMember(userId);
|
||||
}).find((member) => !!member);
|
||||
if (availableMember) {
|
||||
@ -767,7 +756,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
// if all else fails, try falling back to a user,
|
||||
// and create a one-off member for it
|
||||
if (hasHeroes) {
|
||||
const availableUser = this.summaryHeroes.map((userId) => {
|
||||
const availableUser = this.summaryHeroes!.map((userId) => {
|
||||
return this.client.getUser(userId);
|
||||
}).find((user) => !!user);
|
||||
if (availableUser) {
|
||||
@ -934,7 +923,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
// Get the main TimelineSet
|
||||
const timelineSet = this.getUnfilteredTimelineSet();
|
||||
|
||||
let newTimeline: EventTimeline;
|
||||
let newTimeline: Optional<EventTimeline>;
|
||||
// If there isn't any event in the timeline, let's go fetch the latest
|
||||
// event and construct a timeline from it.
|
||||
//
|
||||
@ -965,7 +954,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
// we reset everything. The `timelineSet` we pass in needs to be empty
|
||||
// in order for this function to call `/context` and generate a new
|
||||
// timeline.
|
||||
newTimeline = await this.client.getEventTimeline(timelineSet, mostRecentEventInTimeline.getId());
|
||||
newTimeline = await this.client.getEventTimeline(timelineSet, mostRecentEventInTimeline.getId()!);
|
||||
}
|
||||
|
||||
// If a racing `/sync` beat us to creating a new timeline, use that
|
||||
@ -982,11 +971,11 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
// of using the `/context` historical token (ex. `t12-13_0_0_0_0_0_0_0_0`)
|
||||
// so that it matches the next response from `/sync` and we can properly
|
||||
// continue the timeline.
|
||||
newTimeline.setPaginationToken(forwardPaginationToken, EventTimeline.FORWARDS);
|
||||
newTimeline!.setPaginationToken(forwardPaginationToken, EventTimeline.FORWARDS);
|
||||
|
||||
// Set our new fresh timeline as the live timeline to continue syncing
|
||||
// forwards and back paginating from.
|
||||
timelineSet.setLiveTimeline(newTimeline);
|
||||
timelineSet.setLiveTimeline(newTimeline!);
|
||||
// Fixup `this.oldstate` so that `scrollback` has the pagination tokens
|
||||
// available
|
||||
this.fixUpLegacyTimelineFields();
|
||||
@ -1020,7 +1009,8 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
public resetLiveTimeline(backPaginationToken: string | null, forwardPaginationToken: string | null): void {
|
||||
for (let i = 0; i < this.timelineSets.length; i++) {
|
||||
this.timelineSets[i].resetLiveTimeline(
|
||||
backPaginationToken, forwardPaginationToken,
|
||||
backPaginationToken ?? undefined,
|
||||
forwardPaginationToken ?? undefined,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1130,7 +1120,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
* @return {?module:models/event-timeline~EventTimeline} timeline containing
|
||||
* the given event, or null if unknown
|
||||
*/
|
||||
public getTimelineForEvent(eventId: string): EventTimeline {
|
||||
public getTimelineForEvent(eventId: string): EventTimeline | null {
|
||||
const event = this.findEventById(eventId);
|
||||
const thread = this.findThreadForEvent(event);
|
||||
if (thread) {
|
||||
@ -1712,8 +1702,8 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
this.currentState,
|
||||
toStartOfTimeline,
|
||||
);
|
||||
if (!this.getThread(rootEvent.getId())) {
|
||||
this.createThread(rootEvent.getId(), rootEvent, [], toStartOfTimeline);
|
||||
if (!this.getThread(rootEvent.getId()!)) {
|
||||
this.createThread(rootEvent.getId()!, rootEvent, [], toStartOfTimeline);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1757,14 +1747,14 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
* is only meant as a short term patch
|
||||
*/
|
||||
const threadAMetadata = eventA
|
||||
.getServerAggregatedRelation<IThreadBundledRelationship>(THREAD_RELATION_TYPE.name);
|
||||
.getServerAggregatedRelation<IThreadBundledRelationship>(THREAD_RELATION_TYPE.name)!;
|
||||
const threadBMetadata = eventB
|
||||
.getServerAggregatedRelation<IThreadBundledRelationship>(THREAD_RELATION_TYPE.name);
|
||||
.getServerAggregatedRelation<IThreadBundledRelationship>(THREAD_RELATION_TYPE.name)!;
|
||||
return threadAMetadata.latest_event.origin_server_ts -
|
||||
threadBMetadata.latest_event.origin_server_ts;
|
||||
});
|
||||
|
||||
let latestMyThreadsRootEvent: MatrixEvent;
|
||||
let latestMyThreadsRootEvent: MatrixEvent | undefined;
|
||||
const roomState = this.getLiveTimeline().getState(EventTimeline.FORWARDS);
|
||||
for (const rootEvent of threadRoots) {
|
||||
this.threadsTimelineSets[0].addLiveEvent(rootEvent, {
|
||||
@ -1870,7 +1860,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
}
|
||||
|
||||
// A thread root is always shown in both timelines
|
||||
if (event.isThreadRoot || roots?.has(event.getId())) {
|
||||
if (event.isThreadRoot || roots?.has(event.getId()!)) {
|
||||
return {
|
||||
shouldLiveInRoom: true,
|
||||
shouldLiveInThread: true,
|
||||
@ -1887,7 +1877,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
};
|
||||
}
|
||||
|
||||
const parentEventId = event.getAssociatedId();
|
||||
const parentEventId = event.getAssociatedId()!;
|
||||
const parentEvent = this.findEventById(parentEventId) ?? events?.find(e => e.getId() === parentEventId);
|
||||
|
||||
// Treat relations and redactions as extensions of their parents so evaluate parentEvent instead
|
||||
@ -1896,7 +1886,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
}
|
||||
|
||||
// Edge case where we know the event is a relation but don't have the parentEvent
|
||||
if (roots?.has(event.relationEventId)) {
|
||||
if (roots?.has(event.relationEventId!)) {
|
||||
return {
|
||||
shouldLiveInRoom: true,
|
||||
shouldLiveInThread: true,
|
||||
@ -1940,10 +1930,10 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
const eventsByThread: { [threadId: string]: MatrixEvent[] } = {};
|
||||
for (const event of events) {
|
||||
const { threadId, shouldLiveInThread } = this.eventShouldLiveIn(event);
|
||||
if (shouldLiveInThread && !eventsByThread[threadId]) {
|
||||
eventsByThread[threadId] = [];
|
||||
if (shouldLiveInThread && !eventsByThread[threadId!]) {
|
||||
eventsByThread[threadId!] = [];
|
||||
}
|
||||
eventsByThread[threadId]?.push(event);
|
||||
eventsByThread[threadId!]?.push(event);
|
||||
}
|
||||
|
||||
Object.entries(eventsByThread).map(([threadId, threadEvents]) => (
|
||||
@ -1958,7 +1948,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
toStartOfTimeline: boolean,
|
||||
): Thread {
|
||||
if (rootEvent) {
|
||||
const relatedEvents = this.relations.getAllChildEventsForEvent(rootEvent.getId());
|
||||
const relatedEvents = this.relations.getAllChildEventsForEvent(rootEvent.getId()!);
|
||||
if (relatedEvents?.length) {
|
||||
// Include all relations of the root event, given it'll be visible in both timelines,
|
||||
// except `m.replace` as that will already be applied atop the event using `MatrixEvent::makeReplaced`
|
||||
@ -2270,14 +2260,14 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
* @private
|
||||
*/
|
||||
public handleRemoteEcho(remoteEvent: MatrixEvent, localEvent: MatrixEvent): void {
|
||||
const oldEventId = localEvent.getId();
|
||||
const newEventId = remoteEvent.getId();
|
||||
const oldEventId = localEvent.getId()!;
|
||||
const newEventId = remoteEvent.getId()!;
|
||||
const oldStatus = localEvent.status;
|
||||
|
||||
logger.debug(`Got remote echo for event ${oldEventId} -> ${newEventId} old status ${oldStatus}`);
|
||||
|
||||
// no longer pending
|
||||
delete this.txnToEvent[remoteEvent.getUnsigned().transaction_id];
|
||||
delete this.txnToEvent[remoteEvent.getUnsigned().transaction_id!];
|
||||
|
||||
// if it's in the pending list, remove it
|
||||
if (this.pendingEventList) {
|
||||
@ -2289,7 +2279,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
localEvent.handleRemoteEcho(remoteEvent.event);
|
||||
|
||||
const { shouldLiveInRoom, threadId } = this.eventShouldLiveIn(remoteEvent);
|
||||
const thread = this.getThread(threadId);
|
||||
const thread = threadId ? this.getThread(threadId) : null;
|
||||
thread?.timelineSet.handleRemoteEcho(localEvent, oldEventId, newEventId);
|
||||
|
||||
if (shouldLiveInRoom) {
|
||||
@ -2345,7 +2335,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
remoteEvent.setUnsigned(unsigned);
|
||||
// the remote event is _already_ in the timeline, so we need to remove it so
|
||||
// we can convert the local event into the final event.
|
||||
this.removeEvent(remoteEvent.getId());
|
||||
this.removeEvent(remoteEvent.getId()!);
|
||||
this.handleRemoteEcho(remoteEvent, event);
|
||||
}
|
||||
return;
|
||||
@ -2353,17 +2343,15 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
}
|
||||
|
||||
const oldStatus = event.status;
|
||||
const oldEventId = event.getId();
|
||||
const oldEventId = event.getId()!;
|
||||
|
||||
if (!oldStatus) {
|
||||
throw new Error("updatePendingEventStatus called on an event which is " +
|
||||
"not a local echo.");
|
||||
throw new Error("updatePendingEventStatus called on an event which is not a local echo.");
|
||||
}
|
||||
|
||||
const allowed = ALLOWED_TRANSITIONS[oldStatus];
|
||||
if (!allowed || allowed.indexOf(newStatus) < 0) {
|
||||
throw new Error("Invalid EventStatus transition " + oldStatus + "->" +
|
||||
newStatus);
|
||||
if (!allowed?.includes(newStatus)) {
|
||||
throw new Error(`Invalid EventStatus transition ${oldStatus}->${newStatus}`);
|
||||
}
|
||||
|
||||
event.setStatus(newStatus);
|
||||
@ -2964,7 +2952,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
}).map((m) => m.name);
|
||||
}
|
||||
|
||||
let oldName: string;
|
||||
let oldName: string | undefined;
|
||||
if (leftNames.length) {
|
||||
oldName = this.roomNameGenerator({
|
||||
type: RoomNameType.Generated,
|
||||
@ -3060,8 +3048,8 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
throw new Error("expected a visibility change event");
|
||||
}
|
||||
const relation = event.getRelation();
|
||||
const originalEventId = relation.event_id;
|
||||
const visibilityEventsOnOriginalEvent = this.visibilityEvents.get(originalEventId);
|
||||
const originalEventId = relation?.event_id;
|
||||
const visibilityEventsOnOriginalEvent = this.visibilityEvents.get(originalEventId!);
|
||||
if (!visibilityEventsOnOriginalEvent) {
|
||||
// No visibility changes on the original event.
|
||||
// In particular, this change event was not recorded,
|
||||
@ -3079,13 +3067,13 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
|
||||
// If we removed the latest visibility change event, propagate changes.
|
||||
if (index === visibilityEventsOnOriginalEvent.length) {
|
||||
const originalEvent = this.findEventById(originalEventId);
|
||||
const originalEvent = this.findEventById(originalEventId!);
|
||||
if (!originalEvent) {
|
||||
return;
|
||||
}
|
||||
if (index === 0) {
|
||||
// We have just removed the only visibility change event.
|
||||
this.visibilityEvents.delete(originalEventId);
|
||||
this.visibilityEvents.delete(originalEventId!);
|
||||
originalEvent.applyVisibilityEvent();
|
||||
} else {
|
||||
const newEvent = visibilityEventsOnOriginalEvent[visibilityEventsOnOriginalEvent.length - 1];
|
||||
@ -3110,7 +3098,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
* change event.
|
||||
*/
|
||||
private applyPendingVisibilityEvents(event: MatrixEvent): void {
|
||||
const visibilityEvents = this.visibilityEvents.get(event.getId());
|
||||
const visibilityEvents = this.visibilityEvents.get(event.getId()!);
|
||||
if (!visibilityEvents || visibilityEvents.length == 0) {
|
||||
// No pending visibility change in store.
|
||||
return;
|
||||
|
@ -222,7 +222,7 @@ export class Thread extends ReadReceipt<EmittedEvents, EventHandlerMap> {
|
||||
}
|
||||
|
||||
private addEventToTimeline(event: MatrixEvent, toStartOfTimeline: boolean): void {
|
||||
if (!this.findEventById(event.getId())) {
|
||||
if (!this.findEventById(event.getId()!)) {
|
||||
this.timelineSet.addEventToTimeline(
|
||||
event,
|
||||
this.liveTimeline,
|
||||
@ -305,7 +305,7 @@ export class Thread extends ReadReceipt<EmittedEvents, EventHandlerMap> {
|
||||
this._currentUserParticipated = !!bundledRelationship.current_user_participated;
|
||||
|
||||
const event = new MatrixEvent({
|
||||
room_id: this.rootEvent.getRoomId(),
|
||||
room_id: this.room.roomId,
|
||||
...bundledRelationship.latest_event,
|
||||
});
|
||||
this.setEventMetadata(event);
|
||||
@ -322,7 +322,7 @@ export class Thread extends ReadReceipt<EmittedEvents, EventHandlerMap> {
|
||||
private async fetchEditsWhereNeeded(...events: MatrixEvent[]): Promise<unknown> {
|
||||
return Promise.all(events.filter(e => e.isEncrypted()).map((event: MatrixEvent) => {
|
||||
if (event.isRelation()) return; // skip - relations don't get edits
|
||||
return this.client.relations(this.roomId, event.getId(), RelationType.Replace, event.getType(), {
|
||||
return this.client.relations(this.roomId, event.getId()!, RelationType.Replace, event.getType(), {
|
||||
limit: 1,
|
||||
}).then(relations => {
|
||||
if (relations.events.length) {
|
||||
|
@ -302,7 +302,7 @@ export class PushProcessor {
|
||||
// Note that this should not be the current state of the room but the state at
|
||||
// the point the event is in the DAG. Unfortunately the js-sdk does not store
|
||||
// this.
|
||||
return room.currentState.mayTriggerNotifOfType(notifLevelKey, ev.getSender());
|
||||
return room.currentState.mayTriggerNotifOfType(notifLevelKey, ev.getSender()!);
|
||||
}
|
||||
|
||||
private eventFulfillsRoomMemberCountCondition(cond: IRoomMemberCountCondition, ev: MatrixEvent): boolean {
|
||||
|
@ -208,7 +208,7 @@ export class MSC3906Rendezvous {
|
||||
await this.send({
|
||||
type: PayloadType.Finish,
|
||||
outcome: Outcome.Verified,
|
||||
verifying_device_id: this.client.getDeviceId(),
|
||||
verifying_device_id: this.client.getDeviceId()!,
|
||||
verifying_device_key: this.client.getDeviceEd25519Key()!,
|
||||
master_key: masterPublicKey,
|
||||
});
|
||||
|
@ -418,7 +418,7 @@ export class SlidingSyncSdk {
|
||||
// this room, then timeline_limit: 50).
|
||||
const knownEvents = new Set<string>();
|
||||
room.getLiveTimeline().getEvents().forEach((e) => {
|
||||
knownEvents.add(e.getId());
|
||||
knownEvents.add(e.getId()!);
|
||||
});
|
||||
// all unknown events BEFORE a known event must be scrollback e.g:
|
||||
// D E <-- what we know
|
||||
@ -433,7 +433,7 @@ export class SlidingSyncSdk {
|
||||
let seenKnownEvent = false;
|
||||
for (let i = timelineEvents.length-1; i >= 0; i--) {
|
||||
const recvEvent = timelineEvents[i];
|
||||
if (knownEvents.has(recvEvent.getId())) {
|
||||
if (knownEvents.has(recvEvent.getId()!)) {
|
||||
seenKnownEvent = true;
|
||||
continue; // don't include this event, it's a dupe
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import { IMinimalEvent, IRooms, ISyncResponse } from "../sync-accumulator";
|
||||
import { IStartClientOpts } from "../client";
|
||||
import { IStateEventWithRoomId } from "../@types/search";
|
||||
import { IndexedToDeviceBatch, ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage";
|
||||
import { EventEmitterEvents } from "../models/typed-event-emitter";
|
||||
|
||||
export interface ISavedSync {
|
||||
nextBatch: string;
|
||||
@ -39,7 +40,7 @@ export interface IStore {
|
||||
|
||||
// XXX: The indexeddb store exposes a non-standard emitter for the "degraded" event
|
||||
// for when it falls back to being a memory store due to errors.
|
||||
on?: (event: string, handler: (...args: any[]) => void) => void;
|
||||
on?: (event: EventEmitterEvents | "degraded", handler: (...args: any[]) => void) => void;
|
||||
|
||||
/** @return {Promise<boolean>} whether or not the database was newly created in this session. */
|
||||
isNewlyCreated(): Promise<boolean>;
|
||||
@ -231,7 +232,7 @@ export interface IStore {
|
||||
|
||||
clearOutOfBandMembers(roomId: string): Promise<void>;
|
||||
|
||||
getClientOptions(): Promise<IStartClientOpts>;
|
||||
getClientOptions(): Promise<IStartClientOpts | undefined>;
|
||||
|
||||
storeClientOptions(options: IStartClientOpts): Promise<void>;
|
||||
|
||||
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import { ISavedSync } from "./index";
|
||||
import { IEvent, IStartClientOpts, IStateEventWithRoomId, ISyncResponse } from "../matrix";
|
||||
import { IEvent, IStateEventWithRoomId, IStoredClientOpts, ISyncResponse } from "../matrix";
|
||||
import { IndexedToDeviceBatch, ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage";
|
||||
|
||||
export interface IIndexedDBBackend {
|
||||
@ -30,8 +30,8 @@ export interface IIndexedDBBackend {
|
||||
setOutOfBandMembers(roomId: string, membershipEvents: IStateEventWithRoomId[]): Promise<void>;
|
||||
clearOutOfBandMembers(roomId: string): Promise<void>;
|
||||
getUserPresenceEvents(): Promise<UserTuple[]>;
|
||||
getClientOptions(): Promise<IStartClientOpts>;
|
||||
storeClientOptions(options: IStartClientOpts): Promise<void>;
|
||||
getClientOptions(): Promise<IStoredClientOpts | undefined>;
|
||||
storeClientOptions(options: IStoredClientOpts): Promise<void>;
|
||||
saveToDeviceBatches(batches: ToDeviceBatchWithTxnId[]): Promise<void>;
|
||||
getOldestToDeviceBatch(): Promise<IndexedToDeviceBatch | null>;
|
||||
removeToDeviceBatch(id: number): Promise<void>;
|
||||
|
@ -18,7 +18,7 @@ import { IMinimalEvent, ISyncData, ISyncResponse, SyncAccumulator } from "../syn
|
||||
import * as utils from "../utils";
|
||||
import * as IndexedDBHelpers from "../indexeddb-helpers";
|
||||
import { logger } from '../logger';
|
||||
import { IStartClientOpts, IStateEventWithRoomId } from "../matrix";
|
||||
import { IStateEventWithRoomId, IStoredClientOpts } from "../matrix";
|
||||
import { ISavedSync } from "./index";
|
||||
import { IIndexedDBBackend, UserTuple } from "./indexeddb-backend";
|
||||
import { IndexedToDeviceBatch, ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage";
|
||||
@ -538,7 +538,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
});
|
||||
}
|
||||
|
||||
public getClientOptions(): Promise<IStartClientOpts> {
|
||||
public getClientOptions(): Promise<IStoredClientOpts | undefined> {
|
||||
return Promise.resolve().then(() => {
|
||||
const txn = this.db!.transaction(["client_options"], "readonly");
|
||||
const store = txn.objectStore("client_options");
|
||||
@ -548,7 +548,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
});
|
||||
}
|
||||
|
||||
public async storeClientOptions(options: IStartClientOpts): Promise<void> {
|
||||
public async storeClientOptions(options: IStoredClientOpts): Promise<void> {
|
||||
const txn = this.db!.transaction(["client_options"], "readwrite");
|
||||
const store = txn.objectStore("client_options");
|
||||
store.put({
|
||||
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||
import { logger } from "../logger";
|
||||
import { defer, IDeferred } from "../utils";
|
||||
import { ISavedSync } from "./index";
|
||||
import { IStartClientOpts } from "../client";
|
||||
import { IStoredClientOpts } from "../client";
|
||||
import { IStateEventWithRoomId, ISyncResponse } from "../matrix";
|
||||
import { IIndexedDBBackend, UserTuple } from "./indexeddb-backend";
|
||||
import { IndexedToDeviceBatch, ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage";
|
||||
@ -118,11 +118,11 @@ export class RemoteIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
return this.doCmd('clearOutOfBandMembers', [roomId]);
|
||||
}
|
||||
|
||||
public getClientOptions(): Promise<IStartClientOpts> {
|
||||
public getClientOptions(): Promise<IStoredClientOpts | undefined> {
|
||||
return this.doCmd('getClientOptions');
|
||||
}
|
||||
|
||||
public storeClientOptions(options: IStartClientOpts): Promise<void> {
|
||||
public storeClientOptions(options: IStoredClientOpts): Promise<void> {
|
||||
return this.doCmd('storeClientOptions', [options]);
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ import { ISyncResponse } from "../sync-accumulator";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter";
|
||||
import { IStateEventWithRoomId } from "../@types/search";
|
||||
import { IndexedToDeviceBatch, ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage";
|
||||
import { IStoredClientOpts } from "../client";
|
||||
|
||||
/**
|
||||
* This is an internal module. See {@link IndexedDBStore} for the public class.
|
||||
@ -269,11 +270,11 @@ export class IndexedDBStore extends MemoryStore {
|
||||
return this.backend.clearOutOfBandMembers(roomId);
|
||||
}, "clearOutOfBandMembers");
|
||||
|
||||
public getClientOptions = this.degradable((): Promise<object> => {
|
||||
public getClientOptions = this.degradable((): Promise<IStoredClientOpts | undefined> => {
|
||||
return this.backend.getClientOptions();
|
||||
}, "getClientOptions");
|
||||
|
||||
public storeClientOptions = this.degradable((options: object): Promise<void> => {
|
||||
public storeClientOptions = this.degradable((options: IStoredClientOpts): Promise<void> => {
|
||||
super.storeClientOptions(options);
|
||||
return this.backend.storeClientOptions(options);
|
||||
}, "storeClientOptions");
|
||||
|
@ -31,6 +31,7 @@ import { RoomSummary } from "../models/room-summary";
|
||||
import { ISyncResponse } from "../sync-accumulator";
|
||||
import { IStateEventWithRoomId } from "../@types/search";
|
||||
import { IndexedToDeviceBatch, ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage";
|
||||
import { IStoredClientOpts } from "../client";
|
||||
|
||||
function isValidFilterId(filterId?: string | number | null): boolean {
|
||||
const isValidStr = typeof filterId === "string" &&
|
||||
@ -64,7 +65,7 @@ export class MemoryStore implements IStore {
|
||||
protected readonly localStorage?: Storage;
|
||||
private oobMembers: Record<string, IStateEventWithRoomId[]> = {}; // roomId: [member events]
|
||||
private pendingEvents: { [roomId: string]: Partial<IEvent>[] } = {};
|
||||
private clientOptions = {};
|
||||
private clientOptions?: IStoredClientOpts;
|
||||
private pendingToDeviceBatches: IndexedToDeviceBatch[] = [];
|
||||
private nextToDeviceBatchId = 0;
|
||||
|
||||
@ -169,7 +170,7 @@ export class MemoryStore implements IStore {
|
||||
*/
|
||||
public getRoomSummaries(): RoomSummary[] {
|
||||
return Object.values(this.rooms).map(function(room) {
|
||||
return room.summary;
|
||||
return room.summary!;
|
||||
});
|
||||
}
|
||||
|
||||
@ -412,11 +413,11 @@ export class MemoryStore implements IStore {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public getClientOptions(): Promise<object> {
|
||||
public getClientOptions(): Promise<IStoredClientOpts | undefined> {
|
||||
return Promise.resolve(this.clientOptions);
|
||||
}
|
||||
|
||||
public storeClientOptions(options: object): Promise<void> {
|
||||
public storeClientOptions(options: IStoredClientOpts): Promise<void> {
|
||||
this.clientOptions = Object.assign({}, options);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import { RoomSummary } from "../models/room-summary";
|
||||
import { ISyncResponse } from "../sync-accumulator";
|
||||
import { IStateEventWithRoomId } from "../@types/search";
|
||||
import { IndexedToDeviceBatch, ToDeviceBatch } from "../models/ToDeviceMessage";
|
||||
import { IStoredClientOpts } from "../client";
|
||||
|
||||
/**
|
||||
* Construct a stub store. This does no-ops on most store methods.
|
||||
@ -256,11 +257,11 @@ export class StubStore implements IStore {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public getClientOptions(): Promise<object> {
|
||||
return Promise.resolve({});
|
||||
public getClientOptions(): Promise<IStoredClientOpts | undefined> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
public storeClientOptions(options: object): Promise<void> {
|
||||
public storeClientOptions(options: IStoredClientOpts): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
13
src/sync.ts
13
src/sync.ts
@ -1081,11 +1081,11 @@ export class SyncApi {
|
||||
if (Array.isArray(data.presence?.events)) {
|
||||
data.presence!.events.map(client.getEventMapper()).forEach(
|
||||
function(presenceEvent) {
|
||||
let user = client.store.getUser(presenceEvent.getSender());
|
||||
let user = client.store.getUser(presenceEvent.getSender()!);
|
||||
if (user) {
|
||||
user.setPresenceEvent(presenceEvent);
|
||||
} else {
|
||||
user = createNewUser(client, presenceEvent.getSender());
|
||||
user = createNewUser(client, presenceEvent.getSender()!);
|
||||
user.setPresenceEvent(presenceEvent);
|
||||
client.store.storeUser(user);
|
||||
}
|
||||
@ -1097,7 +1097,7 @@ export class SyncApi {
|
||||
if (Array.isArray(data.account_data?.events)) {
|
||||
const events = data.account_data.events.map(client.getEventMapper());
|
||||
const prevEventsMap = events.reduce((m, c) => {
|
||||
m[c.getType()] = client.store.getAccountData(c.getType());
|
||||
m[c.getType()!] = client.store.getAccountData(c.getType());
|
||||
return m;
|
||||
}, {});
|
||||
client.store.storeAccountDataEvents(events);
|
||||
@ -1111,7 +1111,7 @@ export class SyncApi {
|
||||
const rules = accountDataEvent.getContent<IPushRules>();
|
||||
client.pushRules = PushProcessor.rewriteDefaultRules(rules);
|
||||
}
|
||||
const prevEvent = prevEventsMap[accountDataEvent.getType()];
|
||||
const prevEvent = prevEventsMap[accountDataEvent.getType()!];
|
||||
client.emit(ClientEvent.AccountData, accountDataEvent, prevEvent);
|
||||
return accountDataEvent;
|
||||
},
|
||||
@ -1330,10 +1330,9 @@ export class SyncApi {
|
||||
// will stop us linking the empty timeline into the chain).
|
||||
//
|
||||
for (let i = events.length - 1; i >= 0; i--) {
|
||||
const eventId = events[i].getId();
|
||||
const eventId = events[i].getId()!;
|
||||
if (room.getTimelineForEvent(eventId)) {
|
||||
debuglog("Already have event " + eventId + " in limited " +
|
||||
"sync - not resetting");
|
||||
debuglog(`Already have event ${eventId} in limited sync - not resetting`);
|
||||
limited = false;
|
||||
|
||||
// we might still be missing some of the events before i;
|
||||
|
15
src/utils.ts
15
src/utils.ts
@ -355,7 +355,9 @@ export function globToRegexp(glob: string, extended = false): string {
|
||||
const replacements: ([RegExp, string | ((substring: string, ...args: any[]) => string) ])[] = [
|
||||
[/\\\*/g, '.*'],
|
||||
[/\?/g, '.'],
|
||||
!extended && [
|
||||
];
|
||||
if (!extended) {
|
||||
replacements.push([
|
||||
/\\\[(!|)(.*)\\]/g,
|
||||
(_match: string, neg: string, pat: string) => [
|
||||
'[',
|
||||
@ -363,8 +365,8 @@ export function globToRegexp(glob: string, extended = false): string {
|
||||
pat.replace(/\\-/, '-'),
|
||||
']',
|
||||
].join(''),
|
||||
],
|
||||
];
|
||||
]);
|
||||
}
|
||||
return replacements.reduce(
|
||||
// https://github.com/microsoft/TypeScript/issues/30134
|
||||
(pat, args) => args ? pat.replace(args[0], args[1] as any) : pat,
|
||||
@ -372,8 +374,11 @@ export function globToRegexp(glob: string, extended = false): string {
|
||||
);
|
||||
}
|
||||
|
||||
export function ensureNoTrailingSlash(url: string): string {
|
||||
if (url && url.endsWith("/")) {
|
||||
export function ensureNoTrailingSlash(url: string): string;
|
||||
export function ensureNoTrailingSlash(url: undefined): undefined;
|
||||
export function ensureNoTrailingSlash(url?: string): string | undefined;
|
||||
export function ensureNoTrailingSlash(url?: string): string | undefined {
|
||||
if (url?.endsWith("/")) {
|
||||
return url.slice(0, -1);
|
||||
} else {
|
||||
return url;
|
||||
|
@ -2134,7 +2134,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
|
||||
this.direction = CallDirection.Outbound;
|
||||
|
||||
// XXX Find a better way to do this
|
||||
this.client.callEventHandler.calls.set(this.callId, this);
|
||||
this.client.callEventHandler!.calls.set(this.callId, this);
|
||||
|
||||
// make sure we have valid turn creds. Unless something's gone wrong, it should
|
||||
// poll and keep the credentials valid so this should be instant.
|
||||
|
@ -161,7 +161,7 @@ export class CallEventHandler {
|
||||
logger.info("Current turn creds expire in " + timeUntilTurnCresExpire + " ms");
|
||||
call = createNewMatrixCall(
|
||||
this.client,
|
||||
event.getRoomId(),
|
||||
event.getRoomId()!,
|
||||
{ forceTURN: this.client.forceTURN },
|
||||
) ?? undefined;
|
||||
if (!call) {
|
||||
@ -250,7 +250,7 @@ export class CallEventHandler {
|
||||
// if not live, store the fact that the call has ended because
|
||||
// we're probably getting events backwards so
|
||||
// the hangup will come before the invite
|
||||
call = createNewMatrixCall(this.client, event.getRoomId()) ?? undefined;
|
||||
call = createNewMatrixCall(this.client, event.getRoomId()!) ?? undefined;
|
||||
if (call) {
|
||||
call.callId = content.call_id;
|
||||
call.initWithHangup(event);
|
||||
|
@ -65,14 +65,14 @@ export class MediaHandler {
|
||||
if (this.userMediaStreams.length === 0) return;
|
||||
|
||||
const callMediaStreamParams: Map<string, { audio: boolean, video: boolean }> = new Map();
|
||||
for (const call of this.client.callEventHandler.calls.values()) {
|
||||
for (const call of this.client.callEventHandler!.calls.values()) {
|
||||
callMediaStreamParams.set(call.callId, {
|
||||
audio: call.hasLocalUserMediaAudioTrack,
|
||||
video: call.hasLocalUserMediaVideoTrack,
|
||||
});
|
||||
}
|
||||
|
||||
for (const call of this.client.callEventHandler.calls.values()) {
|
||||
for (const call of this.client.callEventHandler!.calls.values()) {
|
||||
if (call.state === CallState.Ended || !callMediaStreamParams.has(call.callId)) continue;
|
||||
|
||||
const { audio, video } = callMediaStreamParams.get(call.callId)!;
|
||||
|
Reference in New Issue
Block a user