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