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
Make more of the code conform to Strict TSC (#2756)
This commit is contained in:
committed by
GitHub
parent
f70f6db926
commit
12a4d2a749
@ -201,7 +201,7 @@ describe("MatrixClient events", function() {
|
||||
});
|
||||
client!.on(RoomEvent.Timeline, function(event, room) {
|
||||
timelineFireCount++;
|
||||
expect(room.roomId).toEqual("!erufh:bar");
|
||||
expect(room?.roomId).toEqual("!erufh:bar");
|
||||
});
|
||||
client!.on(RoomEvent.Name, function(room) {
|
||||
roomNameInvokeCount++;
|
||||
|
@ -368,7 +368,7 @@ describe("MatrixClient event timelines", function() {
|
||||
expect(tl!.getEvents().length).toEqual(4);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
expect(tl!.getEvents()[i].event).toEqual(EVENTS[i]);
|
||||
expect(tl!.getEvents()[i]?.sender.name).toEqual(userName);
|
||||
expect(tl!.getEvents()[i]?.sender?.name).toEqual(userName);
|
||||
}
|
||||
expect(tl!.getPaginationToken(EventTimeline.BACKWARDS))
|
||||
.toEqual("start_token");
|
||||
@ -406,7 +406,7 @@ describe("MatrixClient event timelines", function() {
|
||||
}).then(function(tl) {
|
||||
expect(tl!.getEvents().length).toEqual(2);
|
||||
expect(tl!.getEvents()[1].event).toEqual(EVENTS[0]);
|
||||
expect(tl!.getEvents()[1]?.sender.name).toEqual(userName);
|
||||
expect(tl!.getEvents()[1]?.sender?.name).toEqual(userName);
|
||||
expect(tl!.getPaginationToken(EventTimeline.BACKWARDS))
|
||||
.toEqual("f_1_1");
|
||||
// expect(tl.getPaginationToken(EventTimeline.FORWARDS))
|
||||
@ -767,8 +767,8 @@ describe("MatrixClient event timelines", function() {
|
||||
httpBackend = testClient.httpBackend;
|
||||
await startClient(httpBackend, client);
|
||||
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
const room = client.getRoom(roomId)!;
|
||||
const timelineSet = room.getTimelineSets()[0]!;
|
||||
await expect(client.getLatestTimeline(timelineSet)).rejects.toBeTruthy();
|
||||
});
|
||||
|
||||
@ -786,7 +786,7 @@ describe("MatrixClient event timelines", function() {
|
||||
httpBackend = testClient.httpBackend;
|
||||
|
||||
return startClient(httpBackend, client).then(() => {
|
||||
const room = client.getRoom(roomId);
|
||||
const room = client.getRoom(roomId)!;
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
expect(client.getLatestTimeline(timelineSet)).rejects.toBeFalsy();
|
||||
});
|
||||
@ -849,7 +849,7 @@ describe("MatrixClient event timelines", function() {
|
||||
expect(tl!.getEvents().length).toEqual(4);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
expect(tl!.getEvents()[i].event).toEqual(EVENTS[i]);
|
||||
expect(tl!.getEvents()[i]?.sender.name).toEqual(userName);
|
||||
expect(tl!.getEvents()[i]?.sender?.name).toEqual(userName);
|
||||
}
|
||||
expect(tl!.getPaginationToken(EventTimeline.BACKWARDS))
|
||||
.toEqual("start_token");
|
||||
@ -1129,8 +1129,8 @@ describe("MatrixClient event timelines", function() {
|
||||
});
|
||||
|
||||
it("should allow fetching all threads", async function() {
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSets = await room?.createThreadsTimelineSets();
|
||||
const room = client.getRoom(roomId)!;
|
||||
const timelineSets = await room.createThreadsTimelineSets();
|
||||
expect(timelineSets).not.toBeNull();
|
||||
respondToThreads();
|
||||
respondToThreads();
|
||||
@ -1185,14 +1185,14 @@ describe("MatrixClient event timelines", function() {
|
||||
});
|
||||
|
||||
it("should allow fetching all threads", async function() {
|
||||
const room = client.getRoom(roomId);
|
||||
const room = client.getRoom(roomId)!;
|
||||
|
||||
respondToFilter();
|
||||
respondToSync();
|
||||
respondToFilter();
|
||||
respondToSync();
|
||||
|
||||
const timelineSetsPromise = room?.createThreadsTimelineSets();
|
||||
const timelineSetsPromise = room.createThreadsTimelineSets();
|
||||
expect(timelineSetsPromise).not.toBeNull();
|
||||
await flushHttp(timelineSetsPromise!);
|
||||
respondToFilter();
|
||||
@ -1218,7 +1218,7 @@ describe("MatrixClient event timelines", function() {
|
||||
const [allThreads] = timelineSets!;
|
||||
|
||||
respondToThreads().check((request) => {
|
||||
expect(request.queryParams.filter).toEqual(JSON.stringify({
|
||||
expect(request.queryParams?.filter).toEqual(JSON.stringify({
|
||||
"lazy_load_members": true,
|
||||
}));
|
||||
});
|
||||
@ -1244,7 +1244,7 @@ describe("MatrixClient event timelines", function() {
|
||||
state: [],
|
||||
next_batch: null,
|
||||
}).check((request) => {
|
||||
expect(request.queryParams.from).toEqual(RANDOM_TOKEN);
|
||||
expect(request.queryParams?.from).toEqual(RANDOM_TOKEN);
|
||||
});
|
||||
|
||||
allThreads.getLiveTimeline().setPaginationToken(RANDOM_TOKEN, Direction.Backward);
|
||||
|
@ -160,8 +160,8 @@ describe("MatrixClient room timelines", function() {
|
||||
expect(room.timeline[1].status).toEqual(EventStatus.SENDING);
|
||||
// check member
|
||||
const member = room.timeline[1].sender;
|
||||
expect(member.userId).toEqual(userId);
|
||||
expect(member.name).toEqual(userName);
|
||||
expect(member?.userId).toEqual(userId);
|
||||
expect(member?.name).toEqual(userName);
|
||||
|
||||
httpBackend!.flush("/sync", 1).then(function() {
|
||||
done();
|
||||
@ -327,11 +327,11 @@ describe("MatrixClient room timelines", function() {
|
||||
client!.scrollback(room).then(function() {
|
||||
expect(room.timeline.length).toEqual(5);
|
||||
const joinMsg = room.timeline[0];
|
||||
expect(joinMsg.sender.name).toEqual("Old Alice");
|
||||
expect(joinMsg.sender?.name).toEqual("Old Alice");
|
||||
const oldMsg = room.timeline[1];
|
||||
expect(oldMsg.sender.name).toEqual("Old Alice");
|
||||
expect(oldMsg.sender?.name).toEqual("Old Alice");
|
||||
const newMsg = room.timeline[3];
|
||||
expect(newMsg.sender.name).toEqual(userName);
|
||||
expect(newMsg.sender?.name).toEqual(userName);
|
||||
|
||||
// still have a sync to flush
|
||||
httpBackend!.flush("/sync", 1).then(() => {
|
||||
@ -468,8 +468,8 @@ describe("MatrixClient room timelines", function() {
|
||||
]).then(function() {
|
||||
const preNameEvent = room.timeline[room.timeline.length - 3];
|
||||
const postNameEvent = room.timeline[room.timeline.length - 1];
|
||||
expect(preNameEvent.sender.name).toEqual(userName);
|
||||
expect(postNameEvent.sender.name).toEqual("New Name");
|
||||
expect(preNameEvent.sender?.name).toEqual(userName);
|
||||
expect(postNameEvent.sender?.name).toEqual("New Name");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1276,9 +1276,9 @@ describe("MatrixClient syncing", () => {
|
||||
client!.on(RoomEvent.TimelineReset, (room) => {
|
||||
resetCallCount++;
|
||||
|
||||
const tl = room.getLiveTimeline();
|
||||
expect(tl.getEvents().length).toEqual(0);
|
||||
const tok = tl.getPaginationToken(EventTimeline.BACKWARDS);
|
||||
const tl = room?.getLiveTimeline();
|
||||
expect(tl?.getEvents().length).toEqual(0);
|
||||
const tok = tl?.getPaginationToken(EventTimeline.BACKWARDS);
|
||||
expect(tok).toEqual("newerTok");
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { UNREAD_THREAD_NOTIFICATIONS } from "../../src/@types/sync";
|
||||
import { Filter, IFilterDefinition } from "../../src/filter";
|
||||
import { mkEvent } from "../test-utils/test-utils";
|
||||
import { EventType } from "../../src";
|
||||
|
||||
describe("Filter", function() {
|
||||
const filterId = "f1lt3ring15g00d4ursoul";
|
||||
@ -57,4 +59,27 @@ describe("Filter", function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("filterRoomTimeline", () => {
|
||||
it("should return input if no roomTimelineFilter and roomFilter", () => {
|
||||
const events = [mkEvent({ type: EventType.Sticker, content: {}, event: true })];
|
||||
expect(new Filter(undefined).filterRoomTimeline(events)).toStrictEqual(events);
|
||||
});
|
||||
|
||||
it("should filter using components when present", () => {
|
||||
const definition: IFilterDefinition = {
|
||||
room: {
|
||||
timeline: {
|
||||
types: [EventType.Sticker],
|
||||
},
|
||||
},
|
||||
};
|
||||
const filter = Filter.fromJson(userId, filterId, definition);
|
||||
const events = [
|
||||
mkEvent({ type: EventType.Sticker, content: {}, event: true }),
|
||||
mkEvent({ type: EventType.RoomMessage, content: {}, event: true }),
|
||||
];
|
||||
expect(filter.filterRoomTimeline(events)).toStrictEqual([events[0]]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -259,7 +259,6 @@ describe("InteractiveAuth", () => {
|
||||
const requestEmailToken = jest.fn();
|
||||
|
||||
const ia = new InteractiveAuth({
|
||||
authData: null,
|
||||
matrixClient: getFakeClient(),
|
||||
stateUpdated,
|
||||
doRequest,
|
||||
|
@ -14,7 +14,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixEvent } from "../../../src/models/event";
|
||||
import { MatrixEvent, MatrixEventEvent } from "../../../src/models/event";
|
||||
import { emitPromise } from "../../test-utils/test-utils";
|
||||
import { EventType } from "../../../src";
|
||||
import { Crypto } from "../../../src/crypto";
|
||||
|
||||
describe('MatrixEvent', () => {
|
||||
it('should create copies of itself', () => {
|
||||
@ -84,4 +87,36 @@ describe('MatrixEvent', () => {
|
||||
expect(ev.getWireContent().body).toBeUndefined();
|
||||
expect(ev.getWireContent().ciphertext).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should abort decryption if fails with an error other than a DecryptionError", async () => {
|
||||
const ev = new MatrixEvent({
|
||||
type: EventType.RoomMessageEncrypted,
|
||||
content: {
|
||||
body: "Test",
|
||||
},
|
||||
event_id: "$event1:server",
|
||||
});
|
||||
await ev.attemptDecryption({
|
||||
decryptEvent: jest.fn().mockRejectedValue(new Error("Not a DecryptionError")),
|
||||
} as unknown as Crypto);
|
||||
expect(ev.isEncrypted()).toBeTruthy();
|
||||
expect(ev.isBeingDecrypted()).toBeFalsy();
|
||||
expect(ev.isDecryptionFailure()).toBeFalsy();
|
||||
});
|
||||
|
||||
describe("applyVisibilityEvent", () => {
|
||||
it("should emit VisibilityChange if a change was made", async () => {
|
||||
const ev = new MatrixEvent({
|
||||
type: "m.room.message",
|
||||
content: {
|
||||
body: "Test",
|
||||
},
|
||||
event_id: "$event1:server",
|
||||
});
|
||||
|
||||
const prom = emitPromise(ev, MatrixEventEvent.VisibilityChange);
|
||||
ev.applyVisibilityEvent({ visible: false, eventId: ev.getId(), reason: null });
|
||||
await prom;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
import * as utils from "../test-utils/test-utils";
|
||||
import { RoomMember, RoomMemberEvent } from "../../src/models/room-member";
|
||||
import { RoomState } from "../../src";
|
||||
import { EventType, RoomState } from "../../src";
|
||||
|
||||
describe("RoomMember", function() {
|
||||
const roomId = "!foo:bar";
|
||||
@ -142,8 +142,7 @@ describe("RoomMember", function() {
|
||||
expect(emitCount).toEqual(1);
|
||||
});
|
||||
|
||||
it("should not honor string power levels.",
|
||||
function() {
|
||||
it("should not honor string power levels.", function() {
|
||||
const event = utils.mkEvent({
|
||||
type: "m.room.power_levels",
|
||||
room: roomId,
|
||||
@ -169,6 +168,46 @@ describe("RoomMember", function() {
|
||||
expect(member.powerLevel).toEqual(20);
|
||||
expect(emitCount).toEqual(1);
|
||||
});
|
||||
|
||||
it("should no-op if given a non-state or unrelated event", () => {
|
||||
const fn = jest.spyOn(member, "emit");
|
||||
expect(fn).not.toHaveBeenCalledWith(RoomMemberEvent.PowerLevel);
|
||||
member.setPowerLevelEvent(utils.mkEvent({
|
||||
type: EventType.RoomPowerLevels,
|
||||
room: roomId,
|
||||
user: userA,
|
||||
content: {
|
||||
users_default: 20,
|
||||
users: {
|
||||
"@alice:bar": "5",
|
||||
},
|
||||
},
|
||||
skey: "invalid",
|
||||
event: true,
|
||||
}));
|
||||
const nonStateEv = utils.mkEvent({
|
||||
type: EventType.RoomPowerLevels,
|
||||
room: roomId,
|
||||
user: userA,
|
||||
content: {
|
||||
users_default: 20,
|
||||
users: {
|
||||
"@alice:bar": "5",
|
||||
},
|
||||
},
|
||||
event: true,
|
||||
});
|
||||
delete nonStateEv.event.state_key;
|
||||
member.setPowerLevelEvent(nonStateEv);
|
||||
member.setPowerLevelEvent(utils.mkEvent({
|
||||
type: EventType.Sticker,
|
||||
room: roomId,
|
||||
user: userA,
|
||||
content: {},
|
||||
event: true,
|
||||
}));
|
||||
expect(fn).not.toHaveBeenCalledWith(RoomMemberEvent.PowerLevel);
|
||||
});
|
||||
});
|
||||
|
||||
describe("setTypingEvent", function() {
|
||||
@ -234,6 +273,79 @@ describe("RoomMember", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("isKicked", () => {
|
||||
it("should return false if membership is not `leave`", () => {
|
||||
const member1 = new RoomMember(roomId, userA);
|
||||
member1.membership = "join";
|
||||
expect(member1.isKicked()).toBeFalsy();
|
||||
|
||||
const member2 = new RoomMember(roomId, userA);
|
||||
member2.membership = "invite";
|
||||
expect(member2.isKicked()).toBeFalsy();
|
||||
|
||||
const member3 = new RoomMember(roomId, userA);
|
||||
expect(member3.isKicked()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should return false if the membership event is unknown", () => {
|
||||
const member = new RoomMember(roomId, userA);
|
||||
member.membership = "leave";
|
||||
expect(member.isKicked()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should return false if the member left of their own accord", () => {
|
||||
const member = new RoomMember(roomId, userA);
|
||||
member.membership = "leave";
|
||||
member.events.member = utils.mkMembership({
|
||||
event: true,
|
||||
sender: userA,
|
||||
mship: "leave",
|
||||
skey: userA,
|
||||
});
|
||||
expect(member.isKicked()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should return true if the member's leave was sent by another user", () => {
|
||||
const member = new RoomMember(roomId, userA);
|
||||
member.membership = "leave";
|
||||
member.events.member = utils.mkMembership({
|
||||
event: true,
|
||||
sender: userB,
|
||||
mship: "leave",
|
||||
skey: userA,
|
||||
});
|
||||
expect(member.isKicked()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getDMInviter", () => {
|
||||
it("should return userId of the sender of the invite if is_direct=true", () => {
|
||||
const member = new RoomMember(roomId, userA);
|
||||
member.membership = "invite";
|
||||
member.events.member = utils.mkMembership({
|
||||
event: true,
|
||||
sender: userB,
|
||||
mship: "invite",
|
||||
skey: userA,
|
||||
});
|
||||
member.events.member.event.content!.is_direct = true;
|
||||
expect(member.getDMInviter()).toBe(userB);
|
||||
});
|
||||
|
||||
it("should not return userId of the sender of the invite if is_direct=false", () => {
|
||||
const member = new RoomMember(roomId, userA);
|
||||
member.membership = "invite";
|
||||
member.events.member = utils.mkMembership({
|
||||
event: true,
|
||||
sender: userB,
|
||||
mship: "invite",
|
||||
skey: userA,
|
||||
});
|
||||
member.events.member.event.content!.is_direct = false;
|
||||
expect(member.getDMInviter()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("setMembershipEvent", function() {
|
||||
const joinEvent = utils.mkMembership({
|
||||
event: true,
|
||||
|
@ -303,6 +303,7 @@ describe("RoomState", function() {
|
||||
state.setStateEvents(events, { timelineWasEmpty: true });
|
||||
expect(emitCount).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('beacon events', () => {
|
||||
it('adds new beacon info events to state and emits', () => {
|
||||
@ -390,7 +391,6 @@ describe("RoomState", function() {
|
||||
expect(emitSpy).toHaveBeenCalledWith(RoomStateEvent.BeaconLiveness, state, false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("setOutOfBandMembers", function() {
|
||||
it("should add a new member", function() {
|
||||
@ -1007,4 +1007,20 @@ describe("RoomState", function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("mayClientSendStateEvent", () => {
|
||||
it("should return false if the user isn't authenticated", () => {
|
||||
expect(state.mayClientSendStateEvent("m.room.message", {
|
||||
isGuest: jest.fn().mockReturnValue(false),
|
||||
credentials: {},
|
||||
} as unknown as MatrixClient)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should return false if the user is a guest", () => {
|
||||
expect(state.mayClientSendStateEvent("m.room.message", {
|
||||
isGuest: jest.fn().mockReturnValue(true),
|
||||
credentials: { userId: userA },
|
||||
} as unknown as MatrixClient)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -27,6 +27,7 @@ import {
|
||||
EventType,
|
||||
JoinRule,
|
||||
MatrixEvent,
|
||||
MatrixEventEvent,
|
||||
PendingEventOrdering,
|
||||
RelationType,
|
||||
RoomEvent,
|
||||
@ -40,6 +41,7 @@ import { emitPromise } from "../test-utils/test-utils";
|
||||
import { ReceiptType } from "../../src/@types/read_receipts";
|
||||
import { FeatureSupport, Thread, ThreadEvent } from "../../src/models/thread";
|
||||
import { WrappedReceipt } from "../../src/models/read-receipt";
|
||||
import { Crypto } from "../../src/crypto";
|
||||
|
||||
describe("Room", function() {
|
||||
const roomId = "!foo:bar";
|
||||
@ -337,12 +339,12 @@ describe("Room", function() {
|
||||
expect(event.getId()).toEqual(localEventId);
|
||||
expect(event.status).toEqual(EventStatus.SENDING);
|
||||
expect(emitRoom).toEqual(room);
|
||||
expect(oldEventId).toBe(null);
|
||||
expect(oldStatus).toBe(null);
|
||||
expect(oldEventId).toBeUndefined();
|
||||
expect(oldStatus).toBeUndefined();
|
||||
break;
|
||||
case 1:
|
||||
expect(event.getId()).toEqual(remoteEventId);
|
||||
expect(event.status).toBe(null);
|
||||
expect(event.status).toBeNull();
|
||||
expect(emitRoom).toEqual(room);
|
||||
expect(oldEventId).toEqual(localEventId);
|
||||
expect(oldStatus).toBe(EventStatus.SENDING);
|
||||
@ -371,7 +373,7 @@ describe("Room", function() {
|
||||
delete eventJson["event_id"];
|
||||
const localEvent = new MatrixEvent(Object.assign({ event_id: "$temp" }, eventJson));
|
||||
localEvent.status = EventStatus.SENDING;
|
||||
expect(localEvent.getTxnId()).toBeNull();
|
||||
expect(localEvent.getTxnId()).toBeUndefined();
|
||||
expect(room.timeline.length).toEqual(0);
|
||||
|
||||
// first add the local echo. This is done before the /send request is even sent.
|
||||
@ -386,7 +388,7 @@ describe("Room", function() {
|
||||
|
||||
// then /sync returns the remoteEvent, it should de-dupe based on the event ID.
|
||||
const remoteEvent = new MatrixEvent(Object.assign({ event_id: realEventId }, eventJson));
|
||||
expect(remoteEvent.getTxnId()).toBeNull();
|
||||
expect(remoteEvent.getTxnId()).toBeUndefined();
|
||||
room.addLiveEvents([remoteEvent]);
|
||||
// the duplicate strategy code should ensure we don't add a 2nd event to the live timeline
|
||||
expect(room.timeline.length).toEqual(1);
|
||||
@ -1535,6 +1537,36 @@ describe("Room", function() {
|
||||
[eventA, eventB, eventC],
|
||||
);
|
||||
});
|
||||
|
||||
it("should apply redactions eagerly in the pending event list", () => {
|
||||
const client = (new TestClient("@alice:example.com", "alicedevice")).client;
|
||||
const room = new Room(roomId, client, userA, {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
|
||||
const eventA = utils.mkMessage({
|
||||
room: roomId,
|
||||
user: userA,
|
||||
msg: "remote 1",
|
||||
event: true,
|
||||
});
|
||||
eventA.status = EventStatus.SENDING;
|
||||
const redactA = utils.mkEvent({
|
||||
room: roomId,
|
||||
user: userA,
|
||||
type: EventType.RoomRedaction,
|
||||
content: {},
|
||||
redacts: eventA.getId(),
|
||||
event: true,
|
||||
});
|
||||
redactA.status = EventStatus.SENDING;
|
||||
|
||||
room.addPendingEvent(eventA, "TXN1");
|
||||
expect(room.getPendingEvents()).toEqual([eventA]);
|
||||
room.addPendingEvent(redactA, "TXN2");
|
||||
expect(room.getPendingEvents()).toEqual([eventA, redactA]);
|
||||
expect(eventA.isRedacted()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("updatePendingEvent", function() {
|
||||
@ -1721,7 +1753,7 @@ describe("Room", function() {
|
||||
});
|
||||
room.updateMyMembership(JoinRule.Invite);
|
||||
expect(room.getMyMembership()).toEqual(JoinRule.Invite);
|
||||
expect(events[0]).toEqual({ membership: "invite", oldMembership: null });
|
||||
expect(events[0]).toEqual({ membership: "invite", oldMembership: undefined });
|
||||
events.splice(0); //clear
|
||||
room.updateMyMembership(JoinRule.Invite);
|
||||
expect(events.length).toEqual(0);
|
||||
@ -2636,4 +2668,42 @@ describe("Room", function() {
|
||||
expect(room.hasThreadUnreadNotification()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("should load pending events from from the store and decrypt if needed", async () => {
|
||||
const client = new TestClient(userA).client;
|
||||
client.crypto = {
|
||||
decryptEvent: jest.fn().mockResolvedValue({ clearEvent: { body: "enc" } }),
|
||||
} as unknown as Crypto;
|
||||
client.store.getPendingEvents = jest.fn(async roomId => [{
|
||||
event_id: "$1:server",
|
||||
type: "m.room.message",
|
||||
content: { body: "1" },
|
||||
sender: "@1:server",
|
||||
room_id: roomId,
|
||||
origin_server_ts: 1,
|
||||
txn_id: "txn1",
|
||||
}, {
|
||||
event_id: "$2:server",
|
||||
type: "m.room.encrypted",
|
||||
content: { body: "2" },
|
||||
sender: "@2:server",
|
||||
room_id: roomId,
|
||||
origin_server_ts: 2,
|
||||
txn_id: "txn2",
|
||||
}]);
|
||||
const room = new Room(roomId, client, userA, {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
await emitPromise(room, RoomEvent.LocalEchoUpdated);
|
||||
await emitPromise(client, MatrixEventEvent.Decrypted);
|
||||
await emitPromise(room, RoomEvent.LocalEchoUpdated);
|
||||
const pendingEvents = room.getPendingEvents();
|
||||
expect(pendingEvents).toHaveLength(2);
|
||||
expect(pendingEvents[1].isDecryptionFailure()).toBeFalsy();
|
||||
expect(pendingEvents[1].isBeingDecrypted()).toBeFalsy();
|
||||
expect(pendingEvents[1].isEncrypted()).toBeTruthy();
|
||||
for (const ev of pendingEvents) {
|
||||
expect(room.getPendingEvent(ev.getId())).toBe(ev);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -60,7 +60,7 @@ describe("IndexedDBStore", () => {
|
||||
expect(await store.getOutOfBandMembers(roomId)).toHaveLength(1);
|
||||
|
||||
// Simulate a broken IDB
|
||||
(store.backend as LocalIndexedDBStoreBackend)["db"].transaction = (): IDBTransaction => {
|
||||
(store.backend as LocalIndexedDBStoreBackend)["db"]!.transaction = (): IDBTransaction => {
|
||||
const err = new Error("Failed to execute 'transaction' on 'IDBDatabase': " +
|
||||
"The database connection is closing.");
|
||||
err.name = "InvalidStateError";
|
||||
|
@ -3572,7 +3572,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
* @throws Error if the event is not in QUEUED, NOT_SENT or ENCRYPTING state
|
||||
*/
|
||||
public cancelPendingEvent(event: MatrixEvent) {
|
||||
if (![EventStatus.QUEUED, EventStatus.NOT_SENT, EventStatus.ENCRYPTING].includes(event.status)) {
|
||||
if (![EventStatus.QUEUED, EventStatus.NOT_SENT, EventStatus.ENCRYPTING].includes(event.status!)) {
|
||||
throw new Error("cannot cancel an event with status " + event.status);
|
||||
}
|
||||
|
||||
@ -6800,12 +6800,12 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
* @param {boolean} options.emit Emits "event.decrypted" if set to true
|
||||
*/
|
||||
public decryptEventIfNeeded(event: MatrixEvent, options?: IDecryptOptions): Promise<void> {
|
||||
if (event.shouldAttemptDecryption()) {
|
||||
event.attemptDecryption(this.crypto, options);
|
||||
if (event.shouldAttemptDecryption() && this.isCryptoEnabled()) {
|
||||
event.attemptDecryption(this.crypto!, options);
|
||||
}
|
||||
|
||||
if (event.isBeingDecrypted()) {
|
||||
return event.getDecryptionPromise();
|
||||
return event.getDecryptionPromise()!;
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@ -9036,8 +9036,8 @@ export function fixNotificationCountOnDecryption(cli: MatrixClient, event: Matri
|
||||
// TODO: Handle mentions received while the client is offline
|
||||
// See also https://github.com/vector-im/element-web/issues/9069
|
||||
const hasReadEvent = isThreadEvent
|
||||
? room.getThread(event.threadRootId).hasUserReadEvent(cli.getUserId(), event.getId())
|
||||
: room.hasUserReadEvent(cli.getUserId(), event.getId());
|
||||
? room.getThread(event.threadRootId)?.hasUserReadEvent(cli.getUserId()!, event.getId())
|
||||
: room.hasUserReadEvent(cli.getUserId()!, event.getId());
|
||||
|
||||
if (!hasReadEvent) {
|
||||
let newCount = currentCount;
|
||||
|
@ -35,7 +35,7 @@ import * as utils from "./utils";
|
||||
*/
|
||||
export function getHttpUriForMxc(
|
||||
baseUrl: string,
|
||||
mxc: string,
|
||||
mxc?: string,
|
||||
width?: number,
|
||||
height?: number,
|
||||
resizeMethod?: string,
|
||||
|
@ -107,8 +107,8 @@ export class Filter {
|
||||
}
|
||||
|
||||
private definition: IFilterDefinition = {};
|
||||
private roomFilter: FilterComponent;
|
||||
private roomTimelineFilter: FilterComponent;
|
||||
private roomFilter?: FilterComponent;
|
||||
private roomTimelineFilter?: FilterComponent;
|
||||
|
||||
constructor(public readonly userId: string | undefined | null, public filterId?: string) {}
|
||||
|
||||
@ -116,7 +116,7 @@ export class Filter {
|
||||
* Get the ID of this filter on your homeserver (if known)
|
||||
* @return {?string} The filter ID
|
||||
*/
|
||||
getFilterId(): string | null {
|
||||
public getFilterId(): string | undefined {
|
||||
return this.filterId;
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ export class Filter {
|
||||
* Get the JSON body of the filter.
|
||||
* @return {Object} The filter definition
|
||||
*/
|
||||
getDefinition(): IFilterDefinition {
|
||||
public getDefinition(): IFilterDefinition {
|
||||
return this.definition;
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ export class Filter {
|
||||
* Set the JSON body of the filter
|
||||
* @param {Object} definition The filter definition
|
||||
*/
|
||||
setDefinition(definition: IFilterDefinition) {
|
||||
public setDefinition(definition: IFilterDefinition) {
|
||||
this.definition = definition;
|
||||
|
||||
// This is all ported from synapse's FilterCollection()
|
||||
@ -201,7 +201,7 @@ export class Filter {
|
||||
* Get the room.timeline filter component of the filter
|
||||
* @return {FilterComponent} room timeline filter component
|
||||
*/
|
||||
getRoomTimelineFilterComponent(): FilterComponent {
|
||||
public getRoomTimelineFilterComponent(): FilterComponent | undefined {
|
||||
return this.roomTimelineFilter;
|
||||
}
|
||||
|
||||
@ -211,15 +211,21 @@ export class Filter {
|
||||
* @param {MatrixEvent[]} events the list of events being filtered
|
||||
* @return {MatrixEvent[]} the list of events which match the filter
|
||||
*/
|
||||
filterRoomTimeline(events: MatrixEvent[]): MatrixEvent[] {
|
||||
return this.roomTimelineFilter.filter(this.roomFilter.filter(events));
|
||||
public filterRoomTimeline(events: MatrixEvent[]): MatrixEvent[] {
|
||||
if (this.roomFilter) {
|
||||
events = this.roomFilter.filter(events);
|
||||
}
|
||||
if (this.roomTimelineFilter) {
|
||||
events = this.roomTimelineFilter.filter(events);
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max number of events to return for each room's timeline.
|
||||
* @param {Number} limit The max number of events to return for each room.
|
||||
*/
|
||||
setTimelineLimit(limit: number) {
|
||||
public setTimelineLimit(limit: number) {
|
||||
setProp(this.definition, "room.timeline.limit", limit);
|
||||
}
|
||||
|
||||
@ -234,14 +240,14 @@ export class Filter {
|
||||
...this.definition?.room,
|
||||
timeline: {
|
||||
...this.definition?.room?.timeline,
|
||||
[UNREAD_THREAD_NOTIFICATIONS.name]: !!enabled,
|
||||
[UNREAD_THREAD_NOTIFICATIONS.name]: enabled,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
setLazyLoadMembers(enabled: boolean): void {
|
||||
setProp(this.definition, "room.state.lazy_load_members", !!enabled);
|
||||
public setLazyLoadMembers(enabled: boolean): void {
|
||||
setProp(this.definition, "room.state.lazy_load_members", enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -249,7 +255,7 @@ export class Filter {
|
||||
* @param {boolean} includeLeave True to make rooms the user has left appear
|
||||
* in responses.
|
||||
*/
|
||||
setIncludeLeaveRooms(includeLeave: boolean) {
|
||||
public setIncludeLeaveRooms(includeLeave: boolean) {
|
||||
setProp(this.definition, "room.include_leave", includeLeave);
|
||||
}
|
||||
}
|
||||
|
@ -112,11 +112,11 @@ export class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O> {
|
||||
defer.resolve(JSON.parse(xhr.responseText));
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.name === "AbortError") {
|
||||
if ((<Error>err).name === "AbortError") {
|
||||
defer.reject(err);
|
||||
return;
|
||||
}
|
||||
defer.reject(new ConnectionError("request failed", err));
|
||||
defer.reject(new ConnectionError("request failed", <Error>err));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -207,7 +207,7 @@ export class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O> {
|
||||
base: this.opts.baseUrl,
|
||||
path: MediaPrefix.R0 + "/upload",
|
||||
params: {
|
||||
access_token: this.opts.accessToken,
|
||||
access_token: this.opts.accessToken!,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ export class EventContext {
|
||||
* backwards in time
|
||||
* @return {string}
|
||||
*/
|
||||
public getPaginateToken(backwards = false): string {
|
||||
public getPaginateToken(backwards = false): string | null {
|
||||
return this.paginateTokens[backwards ? Direction.Backward : Direction.Forward];
|
||||
}
|
||||
|
||||
|
@ -75,13 +75,22 @@ export interface IAddLiveEventOptions
|
||||
type EmittedEvents = RoomEvent.Timeline | RoomEvent.TimelineReset;
|
||||
|
||||
export type EventTimelineSetHandlerMap = {
|
||||
[RoomEvent.Timeline]:
|
||||
(event: MatrixEvent, room: Room, toStartOfTimeline: boolean, removed: boolean, data: IRoomTimelineData) => void;
|
||||
[RoomEvent.TimelineReset]: (room: Room, eventTimelineSet: EventTimelineSet, resetAllTimelines: boolean) => void;
|
||||
[RoomEvent.Timeline]: (
|
||||
event: MatrixEvent,
|
||||
room: Room | undefined,
|
||||
toStartOfTimeline: boolean | undefined,
|
||||
removed: boolean,
|
||||
data: IRoomTimelineData,
|
||||
) => void;
|
||||
[RoomEvent.TimelineReset]: (
|
||||
room: Room | undefined,
|
||||
eventTimelineSet: EventTimelineSet,
|
||||
resetAllTimelines: boolean,
|
||||
) => void;
|
||||
};
|
||||
|
||||
export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTimelineSetHandlerMap> {
|
||||
public readonly relations?: RelationsContainer;
|
||||
public readonly relations: RelationsContainer;
|
||||
private readonly timelineSupport: boolean;
|
||||
private readonly displayPendingEvents: boolean;
|
||||
private liveTimeline: EventTimeline;
|
||||
@ -145,7 +154,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
|
||||
this.filter = opts.filter;
|
||||
|
||||
this.relations = this.room?.relations ?? new RelationsContainer(room?.client ?? client);
|
||||
this.relations = this.room?.relations ?? new RelationsContainer(room?.client ?? client!);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -212,7 +221,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
* @param {String} eventId the eventId being sought
|
||||
* @return {module:models/event-timeline~EventTimeline} timeline
|
||||
*/
|
||||
public eventIdToTimeline(eventId: string): EventTimeline {
|
||||
public eventIdToTimeline(eventId: string): EventTimeline | undefined {
|
||||
return this._eventIdToTimeline.get(eventId);
|
||||
}
|
||||
|
||||
@ -268,15 +277,13 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
if (forwardPaginationToken) {
|
||||
// Now set the forward pagination token on the old live timeline
|
||||
// so it can be forward-paginated.
|
||||
oldTimeline.setPaginationToken(
|
||||
forwardPaginationToken, EventTimeline.FORWARDS,
|
||||
);
|
||||
oldTimeline.setPaginationToken(forwardPaginationToken, EventTimeline.FORWARDS);
|
||||
}
|
||||
|
||||
// make sure we set the pagination token before firing timelineReset,
|
||||
// otherwise clients which start back-paginating will fail, and then get
|
||||
// stuck without realising that they *can* back-paginate.
|
||||
newTimeline.setPaginationToken(backPaginationToken, EventTimeline.BACKWARDS);
|
||||
newTimeline.setPaginationToken(backPaginationToken ?? null, EventTimeline.BACKWARDS);
|
||||
|
||||
// Now we can swap the live timeline to the new one.
|
||||
this.liveTimeline = newTimeline;
|
||||
@ -352,7 +359,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
events: MatrixEvent[],
|
||||
toStartOfTimeline: boolean,
|
||||
timeline: EventTimeline,
|
||||
paginationToken: string,
|
||||
paginationToken?: string,
|
||||
): void {
|
||||
if (!timeline) {
|
||||
throw new Error(
|
||||
@ -544,7 +551,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
);
|
||||
return;
|
||||
}
|
||||
timeline.setPaginationToken(paginationToken, direction);
|
||||
timeline.setPaginationToken(paginationToken ?? null, direction);
|
||||
}
|
||||
}
|
||||
|
||||
@ -579,7 +586,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
roomState?: RoomState,
|
||||
): void {
|
||||
let duplicateStrategy = duplicateStrategyOrOpts as DuplicateStrategy || DuplicateStrategy.Ignore;
|
||||
let timelineWasEmpty: boolean;
|
||||
let timelineWasEmpty: boolean | undefined;
|
||||
if (typeof (duplicateStrategyOrOpts) === 'object') {
|
||||
({
|
||||
duplicateStrategy = DuplicateStrategy.Ignore,
|
||||
@ -681,7 +688,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
roomState?: RoomState,
|
||||
): void {
|
||||
let toStartOfTimeline = !!toStartOfTimelineOrOpts;
|
||||
let timelineWasEmpty: boolean;
|
||||
let timelineWasEmpty: boolean | undefined;
|
||||
if (typeof (toStartOfTimelineOrOpts) === 'object') {
|
||||
({ toStartOfTimeline, fromCache = false, roomState, timelineWasEmpty } = toStartOfTimelineOrOpts);
|
||||
} else if (toStartOfTimelineOrOpts !== undefined) {
|
||||
@ -794,10 +801,9 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
}
|
||||
|
||||
if (timeline1 === timeline2) {
|
||||
// both events are in the same timeline - figure out their
|
||||
// relative indices
|
||||
let idx1: number;
|
||||
let idx2: number;
|
||||
// both events are in the same timeline - figure out their relative indices
|
||||
let idx1: number | undefined = undefined;
|
||||
let idx2: number | undefined = undefined;
|
||||
const events = timeline1.getEvents();
|
||||
for (let idx = 0; idx < events.length &&
|
||||
(idx1 === undefined || idx2 === undefined); idx++) {
|
||||
@ -809,7 +815,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
idx2 = idx;
|
||||
}
|
||||
}
|
||||
return idx1 - idx2;
|
||||
return idx1! - idx2!;
|
||||
}
|
||||
|
||||
// the events are in different timelines. Iterate through the
|
||||
|
@ -195,15 +195,15 @@ export type MatrixEventHandlerMap = {
|
||||
[MatrixEventEvent.BeforeRedaction]: (event: MatrixEvent, redactionEvent: MatrixEvent) => void;
|
||||
[MatrixEventEvent.VisibilityChange]: (event: MatrixEvent, visible: boolean) => void;
|
||||
[MatrixEventEvent.LocalEventIdReplaced]: (event: MatrixEvent) => void;
|
||||
[MatrixEventEvent.Status]: (event: MatrixEvent, status: EventStatus) => void;
|
||||
[MatrixEventEvent.Status]: (event: MatrixEvent, status: EventStatus | null) => void;
|
||||
[MatrixEventEvent.Replaced]: (event: MatrixEvent) => void;
|
||||
[MatrixEventEvent.RelationsCreated]: (relationType: string, eventType: string) => void;
|
||||
} & ThreadEventHandlerMap;
|
||||
|
||||
export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHandlerMap> {
|
||||
private pushActions: IActionsObject = null;
|
||||
private _replacingEvent: MatrixEvent = null;
|
||||
private _localRedactionEvent: MatrixEvent = null;
|
||||
private pushActions: IActionsObject | null = null;
|
||||
private _replacingEvent: MatrixEvent | null = null;
|
||||
private _localRedactionEvent: MatrixEvent | null = null;
|
||||
private _isCancelled = false;
|
||||
private clearEvent?: IClearEvent;
|
||||
|
||||
@ -222,12 +222,12 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
/* curve25519 key which we believe belongs to the sender of the event. See
|
||||
* getSenderKey()
|
||||
*/
|
||||
private senderCurve25519Key: string = null;
|
||||
private senderCurve25519Key: string | null = null;
|
||||
|
||||
/* ed25519 key which the sender of this event (for olm) or the creator of
|
||||
* the megolm session (for megolm) claims to own. See getClaimedEd25519Key()
|
||||
*/
|
||||
private claimedEd25519Key: string = null;
|
||||
private claimedEd25519Key: string | null = null;
|
||||
|
||||
/* curve25519 keys of devices involved in telling us about the
|
||||
* senderCurve25519Key and claimedEd25519Key.
|
||||
@ -237,12 +237,12 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
|
||||
/* where the decryption key is untrusted
|
||||
*/
|
||||
private untrusted: boolean = null;
|
||||
private untrusted: boolean | null = null;
|
||||
|
||||
/* if we have a process decrypting this event, a Promise which resolves
|
||||
* when it is finished. Normally null.
|
||||
*/
|
||||
private _decryptionPromise: Promise<void> = null;
|
||||
private decryptionPromise: Promise<void> | null = null;
|
||||
|
||||
/* flag to indicate if we should retry decrypting this event after the
|
||||
* first attempt (eg, we have received new data which means that a second
|
||||
@ -253,14 +253,14 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
/* The txnId with which this event was sent if it was during this session,
|
||||
* allows for a unique ID which does not change when the event comes back down sync.
|
||||
*/
|
||||
private txnId: string = null;
|
||||
private txnId?: string;
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
* A reference to the thread this event belongs to
|
||||
*/
|
||||
private thread: Thread = null;
|
||||
private threadId: string;
|
||||
private thread?: Thread;
|
||||
private threadId?: string;
|
||||
|
||||
/* Set an approximate timestamp for the event relative the local clock.
|
||||
* This will inherently be approximate because it doesn't take into account
|
||||
@ -271,17 +271,17 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
public localTimestamp: number;
|
||||
|
||||
// XXX: these should be read-only
|
||||
public sender: RoomMember = null;
|
||||
public target: RoomMember = null;
|
||||
public status: EventStatus = null;
|
||||
public error: MatrixError = null;
|
||||
public sender: RoomMember | null = null;
|
||||
public target: RoomMember | null = null;
|
||||
public status: EventStatus | null = null;
|
||||
public error: MatrixError | null = null;
|
||||
public forwardLooking = true; // only state events may be backwards looking
|
||||
|
||||
/* If the event is a `m.key.verification.request` (or to_device `m.key.verification.start`) event,
|
||||
* `Crypto` will set this the `VerificationRequest` for the event
|
||||
* so it can be easily accessed from the timeline.
|
||||
*/
|
||||
public verificationRequest: VerificationRequest = null;
|
||||
public verificationRequest?: VerificationRequest;
|
||||
|
||||
private readonly reEmitter: TypedReEmitter<EmittedEvents, MatrixEventHandlerMap>;
|
||||
|
||||
@ -332,7 +332,7 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
event.content["m.relates_to"][prop] = internaliseString(event.content["m.relates_to"][prop]);
|
||||
});
|
||||
|
||||
this.txnId = event.txn_id || null;
|
||||
this.txnId = event.txn_id;
|
||||
this.localTimestamp = Date.now() - (this.getAge() ?? 0);
|
||||
this.reEmitter = new TypedReEmitter(this);
|
||||
}
|
||||
@ -639,11 +639,11 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
* @return {boolean} True if this event is currently being decrypted, else false.
|
||||
*/
|
||||
public isBeingDecrypted(): boolean {
|
||||
return this._decryptionPromise != null;
|
||||
return this.decryptionPromise != null;
|
||||
}
|
||||
|
||||
public getDecryptionPromise(): Promise<void> {
|
||||
return this._decryptionPromise;
|
||||
public getDecryptionPromise(): Promise<void> | null {
|
||||
return this.decryptionPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -713,16 +713,16 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
// attempts going at the same time, so just set a flag that says we have
|
||||
// new info.
|
||||
//
|
||||
if (this._decryptionPromise) {
|
||||
if (this.decryptionPromise) {
|
||||
logger.log(
|
||||
`Event ${this.getId()} already being decrypted; queueing a retry`,
|
||||
);
|
||||
this.retryDecryption = true;
|
||||
return this._decryptionPromise;
|
||||
return this.decryptionPromise;
|
||||
}
|
||||
|
||||
this._decryptionPromise = this.decryptionLoop(crypto, options);
|
||||
return this._decryptionPromise;
|
||||
this.decryptionPromise = this.decryptionLoop(crypto, options);
|
||||
return this.decryptionPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -768,9 +768,9 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
|
||||
private async decryptionLoop(crypto: Crypto, options: IDecryptOptions = {}): Promise<void> {
|
||||
// make sure that this method never runs completely synchronously.
|
||||
// (doing so would mean that we would clear _decryptionPromise *before*
|
||||
// (doing so would mean that we would clear decryptionPromise *before*
|
||||
// it is set in attemptDecryption - and hence end up with a stuck
|
||||
// `_decryptionPromise`).
|
||||
// `decryptionPromise`).
|
||||
await Promise.resolve();
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
@ -778,7 +778,7 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
this.retryDecryption = false;
|
||||
|
||||
let res: IEventDecryptionResult;
|
||||
let err: Error;
|
||||
let err: Error | undefined = undefined;
|
||||
try {
|
||||
if (!crypto) {
|
||||
res = this.badEncryptedMessage("Encryption not enabled");
|
||||
@ -789,7 +789,7 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.name !== "DecryptionError") {
|
||||
if ((<Error>e).name !== "DecryptionError") {
|
||||
// not a decryption error: log the whole exception as an error
|
||||
// (and don't bother with a retry)
|
||||
const re = options.isRetry ? 're' : '';
|
||||
@ -799,25 +799,25 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
`Error ${re}decrypting event ` +
|
||||
`(id=${this.getId()}): ${e.stack || e}`,
|
||||
);
|
||||
this._decryptionPromise = null;
|
||||
this.decryptionPromise = null;
|
||||
this.retryDecryption = false;
|
||||
return;
|
||||
}
|
||||
|
||||
err = e;
|
||||
err = e as Error;
|
||||
|
||||
// see if we have a retry queued.
|
||||
//
|
||||
// NB: make sure to keep this check in the same tick of the
|
||||
// event loop as `_decryptionPromise = null` below - otherwise we
|
||||
// event loop as `decryptionPromise = null` below - otherwise we
|
||||
// risk a race:
|
||||
//
|
||||
// * A: we check retryDecryption here and see that it is
|
||||
// false
|
||||
// * B: we get a second call to attemptDecryption, which sees
|
||||
// that _decryptionPromise is set so sets
|
||||
// that decryptionPromise is set so sets
|
||||
// retryDecryption
|
||||
// * A: we continue below, clear _decryptionPromise, and
|
||||
// * A: we continue below, clear decryptionPromise, and
|
||||
// never do the retry.
|
||||
//
|
||||
if (this.retryDecryption) {
|
||||
@ -837,13 +837,13 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
// (and set res to a 'badEncryptedMessage'). Either way, we can now set the
|
||||
// cleartext of the event and raise Event.decrypted.
|
||||
//
|
||||
// make sure we clear '_decryptionPromise' before sending the 'Event.decrypted' event,
|
||||
// make sure we clear 'decryptionPromise' before sending the 'Event.decrypted' event,
|
||||
// otherwise the app will be confused to see `isBeingDecrypted` still set when
|
||||
// there isn't an `Event.decrypted` on the way.
|
||||
//
|
||||
// see also notes on retryDecryption above.
|
||||
//
|
||||
this._decryptionPromise = null;
|
||||
this.decryptionPromise = null;
|
||||
this.retryDecryption = false;
|
||||
this.setClearData(res);
|
||||
|
||||
@ -889,10 +889,8 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
*/
|
||||
private setClearData(decryptionResult: IEventDecryptionResult): void {
|
||||
this.clearEvent = decryptionResult.clearEvent;
|
||||
this.senderCurve25519Key =
|
||||
decryptionResult.senderCurve25519Key || null;
|
||||
this.claimedEd25519Key =
|
||||
decryptionResult.claimedEd25519Key || null;
|
||||
this.senderCurve25519Key = decryptionResult.senderCurve25519Key ?? null;
|
||||
this.claimedEd25519Key = decryptionResult.claimedEd25519Key ?? null;
|
||||
this.forwardingCurve25519KeyChain =
|
||||
decryptionResult.forwardingCurve25519KeyChain || [];
|
||||
this.untrusted = decryptionResult.untrusted || false;
|
||||
@ -941,7 +939,9 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
*
|
||||
* @return {Object<string, string>}
|
||||
*/
|
||||
public getKeysClaimed(): Record<"ed25519", string> {
|
||||
public getKeysClaimed(): Partial<Record<"ed25519", string>> {
|
||||
if (!this.claimedEd25519Key) return {};
|
||||
|
||||
return {
|
||||
ed25519: this.claimedEd25519Key,
|
||||
};
|
||||
@ -992,8 +992,8 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
public isKeySourceUntrusted(): boolean {
|
||||
return this.untrusted;
|
||||
public isKeySourceUntrusted(): boolean | undefined {
|
||||
return !!this.untrusted;
|
||||
}
|
||||
|
||||
public getUnsigned(): IUnsigned {
|
||||
@ -1035,10 +1035,10 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
* by a visibility event being redacted).
|
||||
*/
|
||||
public applyVisibilityEvent(visibilityChange?: IVisibilityChange): void {
|
||||
const visible = visibilityChange ? visibilityChange.visible : true;
|
||||
const reason = visibilityChange ? visibilityChange.reason : null;
|
||||
const visible = visibilityChange?.visible ?? true;
|
||||
const reason = visibilityChange?.reason ?? null;
|
||||
let change = false;
|
||||
if (this.visibility.visible !== visibilityChange.visible) {
|
||||
if (this.visibility.visible !== visible) {
|
||||
change = true;
|
||||
} else if (!this.visibility.visible && this.visibility["reason"] !== reason) {
|
||||
change = true;
|
||||
@ -1049,7 +1049,7 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
} else {
|
||||
this.visibility = Object.freeze({
|
||||
visible: false,
|
||||
reason: reason,
|
||||
reason,
|
||||
});
|
||||
}
|
||||
this.emit(MatrixEventEvent.VisibilityChange, this, visible);
|
||||
@ -1105,7 +1105,7 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
|
||||
// If the event is encrypted prune the decrypted bits
|
||||
if (this.isEncrypted()) {
|
||||
this.clearEvent = null;
|
||||
this.clearEvent = undefined;
|
||||
}
|
||||
|
||||
const keeps = REDACT_KEEP_CONTENT_MAP[this.getType()] || {};
|
||||
@ -1217,7 +1217,7 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
*
|
||||
* @param {Object} pushActions push actions
|
||||
*/
|
||||
public setPushActions(pushActions: IActionsObject): void {
|
||||
public setPushActions(pushActions: IActionsObject | null): void {
|
||||
this.pushActions = pushActions;
|
||||
}
|
||||
|
||||
@ -1265,7 +1265,7 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
*
|
||||
* @param {String} status The new status
|
||||
*/
|
||||
public setStatus(status: EventStatus): void {
|
||||
public setStatus(status: EventStatus | null): void {
|
||||
this.status = status;
|
||||
this.emit(MatrixEventEvent.Status, this, status);
|
||||
}
|
||||
@ -1283,7 +1283,7 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
* given type
|
||||
* @return {boolean}
|
||||
*/
|
||||
public isRelation(relType: string = undefined): boolean {
|
||||
public isRelation(relType?: string): boolean {
|
||||
// Relation info is lifted out of the encrypted content when sent to
|
||||
// encrypted rooms, so we have to check `getWireContent` for this.
|
||||
const relation = this.getWireContent()?.["m.relates_to"];
|
||||
@ -1326,7 +1326,7 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
return;
|
||||
}
|
||||
if (this._replacingEvent !== newEvent) {
|
||||
this._replacingEvent = newEvent;
|
||||
this._replacingEvent = newEvent ?? null;
|
||||
this.emit(MatrixEventEvent.Replaced, this);
|
||||
this.invalidateExtensibleEvent();
|
||||
}
|
||||
@ -1339,7 +1339,7 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
*
|
||||
* @return {EventStatus}
|
||||
*/
|
||||
public getAssociatedStatus(): EventStatus | undefined {
|
||||
public getAssociatedStatus(): EventStatus | null {
|
||||
if (this._replacingEvent) {
|
||||
return this._replacingEvent.status;
|
||||
} else if (this._localRedactionEvent) {
|
||||
@ -1373,7 +1373,7 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
*
|
||||
* @return {MatrixEvent?}
|
||||
*/
|
||||
public replacingEvent(): MatrixEvent | undefined {
|
||||
public replacingEvent(): MatrixEvent | null {
|
||||
return this._replacingEvent;
|
||||
}
|
||||
|
||||
@ -1398,7 +1398,7 @@ export class MatrixEvent extends TypedEventEmitter<EmittedEvents, MatrixEventHan
|
||||
* Returns the event that wants to redact this event, but hasn't been sent yet.
|
||||
* @return {MatrixEvent} the event
|
||||
*/
|
||||
public localRedactionEvent(): MatrixEvent | undefined {
|
||||
public localRedactionEvent(): MatrixEvent | null {
|
||||
return this._localRedactionEvent;
|
||||
}
|
||||
|
||||
|
@ -160,7 +160,7 @@ export class Relations extends TypedEventEmitter<RelationsEvent, EventHandlerMap
|
||||
* @param {MatrixEvent} event The event whose status has changed
|
||||
* @param {EventStatus} status The new status
|
||||
*/
|
||||
private onEventStatus = (event: MatrixEvent, status: EventStatus) => {
|
||||
private onEventStatus = (event: MatrixEvent, status: EventStatus | null) => {
|
||||
if (!event.isSending()) {
|
||||
// Sending is done, so we don't need to listen anymore
|
||||
event.removeListener(MatrixEventEvent.Status, this.onEventStatus);
|
||||
|
@ -35,7 +35,7 @@ export enum RoomMemberEvent {
|
||||
}
|
||||
|
||||
export type RoomMemberEventHandlerMap = {
|
||||
[RoomMemberEvent.Membership]: (event: MatrixEvent, member: RoomMember, oldMembership: string | null) => void;
|
||||
[RoomMemberEvent.Membership]: (event: MatrixEvent, member: RoomMember, oldMembership?: string) => void;
|
||||
[RoomMemberEvent.Name]: (event: MatrixEvent, member: RoomMember, oldName: string | null) => void;
|
||||
[RoomMemberEvent.PowerLevel]: (event: MatrixEvent, member: RoomMember) => void;
|
||||
[RoomMemberEvent.Typing]: (event: MatrixEvent, member: RoomMember) => void;
|
||||
@ -43,8 +43,8 @@ export type RoomMemberEventHandlerMap = {
|
||||
|
||||
export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEventHandlerMap> {
|
||||
private _isOutOfBand = false;
|
||||
private _modified: number;
|
||||
public _requestedProfileInfo: boolean; // used by sync.ts
|
||||
private modified = -1;
|
||||
public requestedProfileInfo = false; // used by sync.ts
|
||||
|
||||
// XXX these should be read-only
|
||||
public typing = false;
|
||||
@ -52,14 +52,12 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
|
||||
public rawDisplayName: string;
|
||||
public powerLevel = 0;
|
||||
public powerLevelNorm = 0;
|
||||
public user?: User = null;
|
||||
public membership: string = null;
|
||||
public user?: User;
|
||||
public membership?: string;
|
||||
public disambiguate = false;
|
||||
public events: {
|
||||
member?: MatrixEvent;
|
||||
} = {
|
||||
member: null,
|
||||
};
|
||||
} = {};
|
||||
|
||||
/**
|
||||
* Construct a new room member.
|
||||
@ -119,7 +117,7 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
|
||||
* @fires module:client~MatrixClient#event:"RoomMember.membership"
|
||||
*/
|
||||
public setMembershipEvent(event: MatrixEvent, roomState?: RoomState): void {
|
||||
const displayName = event.getDirectionalContent().displayname;
|
||||
const displayName = event.getDirectionalContent().displayname ?? "";
|
||||
|
||||
if (event.getType() !== EventType.RoomMember) {
|
||||
return;
|
||||
@ -141,23 +139,14 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
|
||||
);
|
||||
}
|
||||
|
||||
this.disambiguate = shouldDisambiguate(
|
||||
this.userId,
|
||||
displayName,
|
||||
roomState,
|
||||
);
|
||||
this.disambiguate = shouldDisambiguate(this.userId, displayName, roomState);
|
||||
|
||||
const oldName = this.name;
|
||||
this.name = calculateDisplayName(
|
||||
this.userId,
|
||||
displayName,
|
||||
roomState,
|
||||
this.disambiguate,
|
||||
);
|
||||
this.name = calculateDisplayName(this.userId, displayName, this.disambiguate);
|
||||
|
||||
// not quite raw: we strip direction override chars so it can safely be inserted into
|
||||
// blocks of text without breaking the text direction
|
||||
this.rawDisplayName = utils.removeDirectionOverrideChars(event.getDirectionalContent().displayname);
|
||||
this.rawDisplayName = utils.removeDirectionOverrideChars(event.getDirectionalContent().displayname ?? "");
|
||||
if (!this.rawDisplayName || !utils.removeHiddenChars(this.rawDisplayName)) {
|
||||
this.rawDisplayName = this.userId;
|
||||
}
|
||||
@ -180,15 +169,15 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
|
||||
* @fires module:client~MatrixClient#event:"RoomMember.powerLevel"
|
||||
*/
|
||||
public setPowerLevelEvent(powerLevelEvent: MatrixEvent): void {
|
||||
if (powerLevelEvent.getType() !== "m.room.power_levels") {
|
||||
if (powerLevelEvent.getType() !== EventType.RoomPowerLevels || powerLevelEvent.getStateKey() !== "") {
|
||||
return;
|
||||
}
|
||||
|
||||
const evContent = powerLevelEvent.getDirectionalContent();
|
||||
|
||||
let maxLevel = evContent.users_default || 0;
|
||||
const users = evContent.users || {};
|
||||
Object.values(users).forEach(function(lvl: number) {
|
||||
const users: { [userId: string]: number } = evContent.users || {};
|
||||
Object.values(users).forEach((lvl: number) => {
|
||||
maxLevel = Math.max(maxLevel, lvl);
|
||||
});
|
||||
const oldPowerLevel = this.powerLevel;
|
||||
@ -244,7 +233,7 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
|
||||
* Update the last modified time to the current time.
|
||||
*/
|
||||
private updateModifiedTime() {
|
||||
this._modified = Date.now();
|
||||
this.modified = Date.now();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -254,12 +243,13 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
|
||||
* @return {number} The timestamp
|
||||
*/
|
||||
public getLastModifiedTime(): number {
|
||||
return this._modified;
|
||||
return this.modified;
|
||||
}
|
||||
|
||||
public isKicked(): boolean {
|
||||
return this.membership === "leave" &&
|
||||
this.events.member.getSender() !== this.events.member.getStateKey();
|
||||
return this.membership === "leave"
|
||||
&& this.events.member !== undefined
|
||||
&& this.events.member.getSender() !== this.events.member.getStateKey();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -267,7 +257,7 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
|
||||
* the user that invited this member
|
||||
* @return {string} user id of the inviter
|
||||
*/
|
||||
public getDMInviter(): string {
|
||||
public getDMInviter(): string | undefined {
|
||||
// when not available because that room state hasn't been loaded in,
|
||||
// we don't really know, but more likely to not be a direct chat
|
||||
if (this.events.member) {
|
||||
@ -280,7 +270,7 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
|
||||
|
||||
const memberEvent = this.events.member;
|
||||
let memberContent = memberEvent.getContent();
|
||||
let inviteSender = memberEvent.getSender();
|
||||
let inviteSender: string | undefined = memberEvent.getSender();
|
||||
|
||||
if (memberContent.membership === "join") {
|
||||
memberContent = memberEvent.getPrevContent();
|
||||
@ -335,20 +325,19 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
|
||||
* get the mxc avatar url, either from a state event, or from a lazily loaded member
|
||||
* @return {string} the mxc avatar url
|
||||
*/
|
||||
public getMxcAvatarUrl(): string | null {
|
||||
public getMxcAvatarUrl(): string | undefined {
|
||||
if (this.events.member) {
|
||||
return this.events.member.getDirectionalContent().avatar_url;
|
||||
} else if (this.user) {
|
||||
return this.user.avatarUrl;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const MXID_PATTERN = /@.+:.+/;
|
||||
const LTR_RTL_PATTERN = /[\u200E\u200F\u202A-\u202F]/;
|
||||
|
||||
function shouldDisambiguate(selfUserId: string, displayName: string, roomState?: RoomState): boolean {
|
||||
function shouldDisambiguate(selfUserId: string, displayName?: string, roomState?: RoomState): boolean {
|
||||
if (!displayName || displayName === selfUserId) return false;
|
||||
|
||||
// First check if the displayname is something we consider truthy
|
||||
@ -377,14 +366,13 @@ function shouldDisambiguate(selfUserId: string, displayName: string, roomState?:
|
||||
|
||||
function calculateDisplayName(
|
||||
selfUserId: string,
|
||||
displayName: string,
|
||||
roomState: RoomState,
|
||||
displayName: string | undefined,
|
||||
disambiguate: boolean,
|
||||
): string {
|
||||
if (disambiguate) return utils.removeDirectionOverrideChars(displayName) + " (" + selfUserId + ")";
|
||||
|
||||
if (!displayName || displayName === selfUserId) return selfUserId;
|
||||
|
||||
if (disambiguate) return utils.removeDirectionOverrideChars(displayName) + " (" + selfUserId + ")";
|
||||
|
||||
// First check if the displayname is something we consider truthy
|
||||
// after stripping it of zero width characters and padding spaces
|
||||
if (!utils.removeHiddenChars(displayName)) return selfUserId;
|
||||
|
@ -68,7 +68,7 @@ export type RoomStateEventHandlerMap = {
|
||||
[RoomStateEvent.NewMember]: (event: MatrixEvent, state: RoomState, member: RoomMember) => void;
|
||||
[RoomStateEvent.Update]: (state: RoomState) => void;
|
||||
[RoomStateEvent.BeaconLiveness]: (state: RoomState, hasLiveBeacons: boolean) => void;
|
||||
[RoomStateEvent.Marker]: (event: MatrixEvent, setStateOptions: IMarkerFoundOptions) => void;
|
||||
[RoomStateEvent.Marker]: (event: MatrixEvent, setStateOptions?: IMarkerFoundOptions) => void;
|
||||
[BeaconEvent.New]: (event: MatrixEvent, beacon: Beacon) => void;
|
||||
};
|
||||
|
||||
@ -82,17 +82,17 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
private displayNameToUserIds = new Map<string, string[]>();
|
||||
private userIdsToDisplayNames: Record<string, string> = {};
|
||||
private tokenToInvite: Record<string, MatrixEvent> = {}; // 3pid invite state_key to m.room.member invite
|
||||
private joinedMemberCount: number = null; // cache of the number of joined members
|
||||
private joinedMemberCount: number | null = null; // cache of the number of joined members
|
||||
// joined members count from summary api
|
||||
// once set, we know the server supports the summary api
|
||||
// and we should only trust that
|
||||
// we could also only trust that before OOB members
|
||||
// are loaded but doesn't seem worth the hassle atm
|
||||
private summaryJoinedMemberCount: number = null;
|
||||
private summaryJoinedMemberCount: number |null = null;
|
||||
// same for invited member count
|
||||
private invitedMemberCount: number = null;
|
||||
private summaryInvitedMemberCount: number = null;
|
||||
private modified: number;
|
||||
private invitedMemberCount: number | null = null;
|
||||
private summaryInvitedMemberCount: number | null = null;
|
||||
private modified = -1;
|
||||
|
||||
// XXX: Should be read-only
|
||||
public members: Record<string, RoomMember> = {}; // userId: RoomMember
|
||||
@ -232,7 +232,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
if (sentinel === undefined) {
|
||||
sentinel = new RoomMember(this.roomId, userId);
|
||||
const member = this.members[userId];
|
||||
if (member) {
|
||||
if (member?.events.member) {
|
||||
sentinel.setMembershipEvent(member.events.member, this);
|
||||
}
|
||||
this.sentinels[userId] = sentinel;
|
||||
@ -257,9 +257,9 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
return stateKey === undefined ? [] : null;
|
||||
}
|
||||
if (stateKey === undefined) { // return all values
|
||||
return Array.from(this.events.get(eventType).values());
|
||||
return Array.from(this.events.get(eventType)!.values());
|
||||
}
|
||||
const event = this.events.get(eventType).get(stateKey);
|
||||
const event = this.events.get(eventType)!.get(stateKey);
|
||||
return event ? event : null;
|
||||
}
|
||||
|
||||
@ -306,8 +306,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
// copy markOutOfBand flags
|
||||
this.getMembers().forEach((member) => {
|
||||
if (member.isOutOfBand()) {
|
||||
const copyMember = copy.getMember(member.userId);
|
||||
copyMember.markOutOfBand();
|
||||
copy.getMember(member.userId)?.markOutOfBand();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -324,8 +323,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
*/
|
||||
public setUnknownStateEvents(events: MatrixEvent[]): void {
|
||||
const unknownStateEvents = events.filter((event) => {
|
||||
return !this.events.has(event.getType()) ||
|
||||
!this.events.get(event.getType()).has(event.getStateKey());
|
||||
return !this.events.has(event.getType()) || !this.events.get(event.getType())!.has(event.getStateKey()!);
|
||||
});
|
||||
|
||||
this.setStateEvents(unknownStateEvents);
|
||||
@ -349,12 +347,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
|
||||
// update the core event dict
|
||||
stateEvents.forEach((event) => {
|
||||
if (event.getRoomId() !== this.roomId) {
|
||||
return;
|
||||
}
|
||||
if (!event.isState()) {
|
||||
return;
|
||||
}
|
||||
if (event.getRoomId() !== this.roomId || !event.isState()) return;
|
||||
|
||||
if (M_BEACON_INFO.matches(event.getType())) {
|
||||
this.setBeacon(event);
|
||||
@ -363,7 +356,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
const lastStateEvent = this.getStateEventMatching(event);
|
||||
this.setStateEvent(event);
|
||||
if (event.getType() === EventType.RoomMember) {
|
||||
this.updateDisplayNameCache(event.getStateKey(), event.getContent().displayname);
|
||||
this.updateDisplayNameCache(event.getStateKey()!, event.getContent().displayname ?? "");
|
||||
this.updateThirdPartyTokenCache(event);
|
||||
}
|
||||
this.emit(RoomStateEvent.Events, event, this, lastStateEvent);
|
||||
@ -375,15 +368,10 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
// the given array (e.g. disambiguating display names in one go to do both
|
||||
// clashing names rather than progressively which only catches 1 of them).
|
||||
stateEvents.forEach((event) => {
|
||||
if (event.getRoomId() !== this.roomId) {
|
||||
return;
|
||||
}
|
||||
if (!event.isState()) {
|
||||
return;
|
||||
}
|
||||
if (event.getRoomId() !== this.roomId || !event.isState()) return;
|
||||
|
||||
if (event.getType() === EventType.RoomMember) {
|
||||
const userId = event.getStateKey();
|
||||
const userId = event.getStateKey()!;
|
||||
|
||||
// leave events apparently elide the displayname or avatar_url,
|
||||
// so let's fake one up so that we don't leak user ids
|
||||
@ -456,11 +444,8 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
|
||||
events.forEach((event: MatrixEvent) => {
|
||||
const relatedToEventId = event.getRelation()?.event_id;
|
||||
// not related to a beacon we know about
|
||||
// discard
|
||||
if (!beaconByEventIdDict[relatedToEventId]) {
|
||||
return;
|
||||
}
|
||||
// not related to a beacon we know about; discard
|
||||
if (!relatedToEventId || !beaconByEventIdDict[relatedToEventId]) return;
|
||||
|
||||
matrixClient.decryptEventIfNeeded(event);
|
||||
|
||||
@ -501,7 +486,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
if (!this.events.has(event.getType())) {
|
||||
this.events.set(event.getType(), new Map());
|
||||
}
|
||||
this.events.get(event.getType()).set(event.getStateKey(), event);
|
||||
this.events.get(event.getType())!.set(event.getStateKey()!, event);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -511,7 +496,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
const beaconIdentifier = getBeaconInfoIdentifier(event);
|
||||
|
||||
if (this.beacons.has(beaconIdentifier)) {
|
||||
const beacon = this.beacons.get(beaconIdentifier);
|
||||
const beacon = this.beacons.get(beaconIdentifier)!;
|
||||
|
||||
if (event.isRedacted()) {
|
||||
if (beacon.beaconInfoId === event.getRedactionEvent()?.['redacts']) {
|
||||
@ -558,7 +543,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
}
|
||||
|
||||
private getStateEventMatching(event: MatrixEvent): MatrixEvent | null {
|
||||
return this.events.get(event.getType())?.get(event.getStateKey()) ?? null;
|
||||
return this.events.get(event.getType())?.get(event.getStateKey()!) ?? null;
|
||||
}
|
||||
|
||||
private updateMember(member: RoomMember): void {
|
||||
@ -646,7 +631,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
if (stateEvent.getType() !== EventType.RoomMember) {
|
||||
return;
|
||||
}
|
||||
const userId = stateEvent.getStateKey();
|
||||
const userId = stateEvent.getStateKey()!;
|
||||
const existingMember = this.getMember(userId);
|
||||
// never replace members received as part of the sync
|
||||
if (existingMember && !existingMember.isOutOfBand()) {
|
||||
@ -788,7 +773,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
||||
* according to the room's state.
|
||||
*/
|
||||
public mayClientSendStateEvent(stateEventType: EventType | string, cli: MatrixClient): boolean {
|
||||
if (cli.isGuest()) {
|
||||
if (cli.isGuest() || !cli.credentials.userId) {
|
||||
return false;
|
||||
}
|
||||
return this.maySendStateEvent(stateEventType, cli.credentials.userId);
|
||||
|
@ -69,7 +69,6 @@ export const KNOWN_SAFE_ROOM_VERSION = '9';
|
||||
const SAFE_ROOM_VERSIONS = ['1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
||||
|
||||
interface IOpts {
|
||||
storageToken?: string;
|
||||
pendingEventOrdering?: PendingEventOrdering;
|
||||
timelineSupport?: boolean;
|
||||
lazyLoadMembers?: boolean;
|
||||
@ -158,7 +157,7 @@ export type RoomEventHandlerMap = {
|
||||
event: MatrixEvent,
|
||||
room: Room,
|
||||
oldEventId?: string,
|
||||
oldStatus?: EventStatus,
|
||||
oldStatus?: EventStatus | null,
|
||||
) => void;
|
||||
[RoomEvent.OldStateUpdated]: (room: Room, previousRoomState: RoomState, roomState: RoomState) => void;
|
||||
[RoomEvent.CurrentStateUpdated]: (room: Room, previousRoomState: RoomState, roomState: RoomState) => void;
|
||||
@ -201,8 +200,8 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
private timelineNeedsRefresh = false;
|
||||
private readonly pendingEventList?: MatrixEvent[];
|
||||
// read by megolm via getter; boolean value - null indicates "use global value"
|
||||
private blacklistUnverifiedDevices: boolean = null;
|
||||
private selfMembership: string = null;
|
||||
private blacklistUnverifiedDevices?: boolean;
|
||||
private selfMembership?: string;
|
||||
private summaryHeroes: string[] = null;
|
||||
// flags to stop logspam about missing m.room.create events
|
||||
private getTypeWarning = false;
|
||||
@ -232,10 +231,6 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
* The room summary.
|
||||
*/
|
||||
public summary: RoomSummary = null;
|
||||
/**
|
||||
* A token which a data store can use to remember the state of the room.
|
||||
*/
|
||||
public readonly storageToken?: string;
|
||||
// legacy fields
|
||||
/**
|
||||
* The live event timeline for this room, with the oldest event at index 0.
|
||||
@ -260,7 +255,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
* @experimental
|
||||
*/
|
||||
private threads = new Map<string, Thread>();
|
||||
public lastThread: Thread;
|
||||
public lastThread?: Thread;
|
||||
|
||||
/**
|
||||
* A mapping of eventId to all visibility changes to apply
|
||||
@ -304,9 +299,6 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
* @param {MatrixClient} client Required. The client, used to lazy load members.
|
||||
* @param {string} myUserId Required. The ID of the syncing user.
|
||||
* @param {Object=} opts Configuration options
|
||||
* @param {*} opts.storageToken Optional. The token which a data store can use
|
||||
* to remember the state of the room. What this means is dependent on the store
|
||||
* implementation.
|
||||
*
|
||||
* @param {String=} opts.pendingEventOrdering Controls where pending messages
|
||||
* appear in a room's timeline. If "<b>chronological</b>", messages will appear
|
||||
@ -332,6 +324,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
opts.pendingEventOrdering = opts.pendingEventOrdering || PendingEventOrdering.Chronological;
|
||||
|
||||
this.name = roomId;
|
||||
this.normalizedName = roomId;
|
||||
|
||||
// all our per-room timeline sets. the first one is the unfiltered ones;
|
||||
// the subsequent ones are the filtered ones in no particular order.
|
||||
@ -346,13 +339,17 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
if (this.opts.pendingEventOrdering === PendingEventOrdering.Detached) {
|
||||
this.pendingEventList = [];
|
||||
this.client.store.getPendingEvents(this.roomId).then(events => {
|
||||
const mapper = this.client.getEventMapper({
|
||||
toDevice: false,
|
||||
decrypt: false,
|
||||
});
|
||||
events.forEach(async (serializedEvent: Partial<IEvent>) => {
|
||||
const event = new MatrixEvent(serializedEvent);
|
||||
if (event.getType() === EventType.RoomMessageEncrypted) {
|
||||
await event.attemptDecryption(this.client.crypto);
|
||||
const event = mapper(serializedEvent);
|
||||
if (event.getType() === EventType.RoomMessageEncrypted && this.client.isCryptoEnabled()) {
|
||||
await event.attemptDecryption(this.client.crypto!);
|
||||
}
|
||||
event.setStatus(EventStatus.NOT_SENT);
|
||||
this.addPendingEvent(event, event.getTxnId());
|
||||
this.addPendingEvent(event, event.getTxnId()!);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -361,7 +358,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
if (!this.opts.lazyLoadMembers) {
|
||||
this.membersPromise = Promise.resolve(false);
|
||||
} else {
|
||||
this.membersPromise = null;
|
||||
this.membersPromise = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@ -399,8 +396,10 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
*
|
||||
* @returns {Promise} Signals when all events have been decrypted
|
||||
*/
|
||||
public decryptCriticalEvents(): Promise<void> {
|
||||
const readReceiptEventId = this.getEventReadUpTo(this.client.getUserId(), true);
|
||||
public async decryptCriticalEvents(): Promise<void> {
|
||||
if (!this.client.isCryptoEnabled()) return;
|
||||
|
||||
const readReceiptEventId = this.getEventReadUpTo(this.client.getUserId()!, true);
|
||||
const events = this.getLiveTimeline().getEvents();
|
||||
const readReceiptTimelineIndex = events.findIndex(matrixEvent => {
|
||||
return matrixEvent.event.event_id === readReceiptEventId;
|
||||
@ -410,9 +409,9 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
.slice(readReceiptTimelineIndex)
|
||||
.filter(event => event.shouldAttemptDecryption())
|
||||
.reverse()
|
||||
.map(event => event.attemptDecryption(this.client.crypto, { isRetry: true }));
|
||||
.map(event => event.attemptDecryption(this.client.crypto!, { isRetry: true }));
|
||||
|
||||
return Promise.allSettled(decryptionPromises) as unknown as Promise<void>;
|
||||
await Promise.allSettled(decryptionPromises);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -420,16 +419,18 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
*
|
||||
* @returns {Promise} Signals when all events have been decrypted
|
||||
*/
|
||||
public decryptAllEvents(): Promise<void> {
|
||||
public async decryptAllEvents(): Promise<void> {
|
||||
if (!this.client.isCryptoEnabled()) return;
|
||||
|
||||
const decryptionPromises = this
|
||||
.getUnfilteredTimelineSet()
|
||||
.getLiveTimeline()
|
||||
.getEvents()
|
||||
.filter(event => event.shouldAttemptDecryption())
|
||||
.reverse()
|
||||
.map(event => event.attemptDecryption(this.client.crypto, { isRetry: true }));
|
||||
.map(event => event.attemptDecryption(this.client.crypto!, { isRetry: true }));
|
||||
|
||||
return Promise.allSettled(decryptionPromises) as unknown as Promise<void>;
|
||||
await Promise.allSettled(decryptionPromises);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -445,7 +446,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
* Gets the version of the room
|
||||
* @returns {string} The version of the room, or null if it could not be determined
|
||||
*/
|
||||
public getVersion(): string | null {
|
||||
public getVersion(): string {
|
||||
const createEvent = this.currentState.getStateEvents(EventType.RoomCreate, "");
|
||||
if (!createEvent) {
|
||||
if (!this.getVersionWarning) {
|
||||
@ -454,9 +455,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
}
|
||||
return '1';
|
||||
}
|
||||
const ver = createEvent.getContent()['room_version'];
|
||||
if (ver === undefined) return '1';
|
||||
return ver;
|
||||
return createEvent.getContent()['room_version'] ?? '1';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -535,7 +534,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
logger.log(`[${this.roomId}] Current version: ${currentVersion}`);
|
||||
logger.log(`[${this.roomId}] Version capability: `, versionCap);
|
||||
|
||||
const result = {
|
||||
const result: IRecommendedVersion = {
|
||||
version: currentVersion,
|
||||
needsUpgrade: false,
|
||||
urgent: false,
|
||||
@ -645,7 +644,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.pendingEventList.find(event => event.getId() === eventId);
|
||||
return this.pendingEventList.find(event => event.getId() === eventId) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -677,7 +676,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
* @return {string} the membership type (join | leave | invite) for the logged in user
|
||||
*/
|
||||
public getMyMembership(): string {
|
||||
return this.selfMembership;
|
||||
return this.selfMembership ?? "leave";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -685,7 +684,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
* try to find out who invited us
|
||||
* @return {string} user id of the inviter
|
||||
*/
|
||||
public getDMInviter(): string {
|
||||
public getDMInviter(): string | undefined {
|
||||
if (this.myUserId) {
|
||||
const me = this.getMember(this.myUserId);
|
||||
if (me) {
|
||||
@ -731,7 +730,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
return this.myUserId;
|
||||
}
|
||||
|
||||
public getAvatarFallbackMember(): RoomMember {
|
||||
public getAvatarFallbackMember(): RoomMember | undefined {
|
||||
const memberCount = this.getInvitedAndJoinedMemberCount();
|
||||
if (memberCount > 2) {
|
||||
return;
|
||||
@ -789,7 +788,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
|
||||
private async loadMembersFromServer(): Promise<IStateEventWithRoomId[]> {
|
||||
const lastSyncToken = this.client.store.getSyncToken();
|
||||
const response = await this.client.members(this.roomId, undefined, "leave", lastSyncToken);
|
||||
const response = await this.client.members(this.roomId, undefined, "leave", lastSyncToken ?? undefined);
|
||||
return response.chunk;
|
||||
}
|
||||
|
||||
@ -836,12 +835,12 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
this.currentState.setOutOfBandMembers(result.memberEvents);
|
||||
// now the members are loaded, start to track the e2e devices if needed
|
||||
if (this.client.isCryptoEnabled() && this.client.isRoomEncrypted(this.roomId)) {
|
||||
this.client.crypto.trackRoomDevices(this.roomId);
|
||||
this.client.crypto!.trackRoomDevices(this.roomId);
|
||||
}
|
||||
return result.fromServer;
|
||||
}).catch((err) => {
|
||||
// allow retries on fail
|
||||
this.membersPromise = null;
|
||||
this.membersPromise = undefined;
|
||||
this.currentState.markOutOfBandMembersFailed();
|
||||
throw err;
|
||||
});
|
||||
@ -850,7 +849,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
if (fromServer) {
|
||||
const oobMembers = this.currentState.getMembers()
|
||||
.filter((m) => m.isOutOfBand())
|
||||
.map((m) => m.events.member.event as IStateEventWithRoomId);
|
||||
.map((m) => m.events.member?.event as IStateEventWithRoomId);
|
||||
logger.log(`LL: telling store to write ${oobMembers.length}`
|
||||
+ ` members for room ${this.roomId}`);
|
||||
const store = this.client.store;
|
||||
@ -881,7 +880,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
await this.loadMembersIfNeeded();
|
||||
await this.client.store.clearOutOfBandMembers(this.roomId);
|
||||
this.currentState.clearOutOfBandMembers();
|
||||
this.membersPromise = null;
|
||||
this.membersPromise = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1328,7 +1327,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
* if the global value should be used for this room.
|
||||
*/
|
||||
public getBlacklistUnverifiedDevices(): boolean {
|
||||
return this.blacklistUnverifiedDevices;
|
||||
return !!this.blacklistUnverifiedDevices;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1457,8 +1456,8 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
public getThread(eventId: string): Thread {
|
||||
return this.threads.get(eventId);
|
||||
public getThread(eventId: string): Thread | null {
|
||||
return this.threads.get(eventId) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1584,7 +1583,6 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
* Add a timelineSet for this room with the given filter
|
||||
* @param {Filter} filter The filter to be applied to this timelineSet
|
||||
* @param {Object=} opts Configuration options
|
||||
* @param {*} opts.storageToken Optional.
|
||||
* @return {EventTimelineSet} The timelineSet
|
||||
*/
|
||||
public getOrCreateFilteredTimelineSet(
|
||||
@ -1799,7 +1797,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
|
||||
const threadRelationship = rootEvent
|
||||
.getServerAggregatedRelation<IThreadBundledRelationship>(THREAD_RELATION_TYPE.name);
|
||||
if (threadRelationship.current_user_participated) {
|
||||
if (threadRelationship?.current_user_participated) {
|
||||
this.threadsTimelineSets[1].addLiveEvent(rootEvent, {
|
||||
duplicateStrategy: DuplicateStrategy.Ignore,
|
||||
fromCache: false,
|
||||
@ -2035,7 +2033,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
const redactId = event.event.redacts;
|
||||
|
||||
// if we know about this event, redact its contents now.
|
||||
const redactedEvent = this.findEventById(redactId);
|
||||
const redactedEvent = redactId ? this.findEventById(redactId) : undefined;
|
||||
if (redactedEvent) {
|
||||
redactedEvent.makeRedacted(event);
|
||||
|
||||
@ -2043,7 +2041,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
if (redactedEvent.isState()) {
|
||||
const currentStateEvent = this.currentState.getStateEvents(
|
||||
redactedEvent.getType(),
|
||||
redactedEvent.getStateKey(),
|
||||
redactedEvent.getStateKey()!,
|
||||
);
|
||||
if (currentStateEvent.getId() === redactedEvent.getId()) {
|
||||
this.currentState.setStateEvents([redactedEvent]);
|
||||
@ -2059,7 +2057,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
// they are based on are changed.
|
||||
|
||||
// Remove any visibility change on this event.
|
||||
this.visibilityEvents.delete(redactId);
|
||||
this.visibilityEvents.delete(redactId!);
|
||||
|
||||
// If this event is a visibility change event, remove it from the
|
||||
// list of visibility changes and update any event affected by it.
|
||||
@ -2183,7 +2181,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
EventTimeline.setEventMetadata(event, this.getLiveTimeline().getState(EventTimeline.FORWARDS), false);
|
||||
|
||||
this.txnToEvent[txnId] = event;
|
||||
if (this.opts.pendingEventOrdering === PendingEventOrdering.Detached) {
|
||||
if (this.pendingEventList) {
|
||||
if (this.pendingEventList.some((e) => e.status === EventStatus.NOT_SENT)) {
|
||||
logger.warn("Setting event as NOT_SENT due to messages in the same state");
|
||||
event.setStatus(EventStatus.NOT_SENT);
|
||||
@ -2199,8 +2197,8 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
|
||||
if (event.isRedaction()) {
|
||||
const redactId = event.event.redacts;
|
||||
let redactedEvent = this.pendingEventList?.find(e => e.getId() === redactId);
|
||||
if (!redactedEvent) {
|
||||
let redactedEvent = this.pendingEventList.find(e => e.getId() === redactId);
|
||||
if (!redactedEvent && redactId) {
|
||||
redactedEvent = this.findEventById(redactId);
|
||||
}
|
||||
if (redactedEvent) {
|
||||
@ -2212,7 +2210,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
for (let i = 0; i < this.timelineSets.length; i++) {
|
||||
const timelineSet = this.timelineSets[i];
|
||||
if (timelineSet.getFilter()) {
|
||||
if (timelineSet.getFilter().filterRoomTimeline([event]).length) {
|
||||
if (timelineSet.getFilter()!.filterRoomTimeline([event]).length) {
|
||||
timelineSet.addEventToTimeline(event,
|
||||
timelineSet.getLiveTimeline(), {
|
||||
toStartOfTimeline: false,
|
||||
@ -2227,7 +2225,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
}
|
||||
}
|
||||
|
||||
this.emit(RoomEvent.LocalEchoUpdated, event, this, null, null);
|
||||
this.emit(RoomEvent.LocalEchoUpdated, event, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2348,20 +2346,19 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
|
||||
// if the message was sent, we expect an event id
|
||||
if (newStatus == EventStatus.SENT && !newEventId) {
|
||||
throw new Error("updatePendingEvent called with status=SENT, " +
|
||||
"but no new event id");
|
||||
throw new Error("updatePendingEvent called with status=SENT, but no new event id");
|
||||
}
|
||||
|
||||
// SENT races against /sync, so we have to special-case it.
|
||||
if (newStatus == EventStatus.SENT) {
|
||||
const timeline = this.getTimelineForEvent(newEventId);
|
||||
const timeline = this.getTimelineForEvent(newEventId!);
|
||||
if (timeline) {
|
||||
// we've already received the event via the event stream.
|
||||
// nothing more to do here, assuming the transaction ID was correctly matched.
|
||||
// Let's check that.
|
||||
const remoteEvent = this.findEventById(newEventId);
|
||||
const remoteTxnId = remoteEvent.getUnsigned().transaction_id;
|
||||
if (!remoteTxnId) {
|
||||
const remoteEvent = this.findEventById(newEventId!);
|
||||
const remoteTxnId = remoteEvent?.getUnsigned().transaction_id;
|
||||
if (!remoteTxnId && remoteEvent) {
|
||||
// This code path is mostly relevant for the Sliding Sync proxy.
|
||||
// The remote event did not contain a transaction ID, so we did not handle
|
||||
// the remote echo yet. Handle it now.
|
||||
@ -2395,18 +2392,18 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
|
||||
if (newStatus == EventStatus.SENT) {
|
||||
// update the event id
|
||||
event.replaceLocalEventId(newEventId);
|
||||
event.replaceLocalEventId(newEventId!);
|
||||
|
||||
const { shouldLiveInRoom, threadId } = this.eventShouldLiveIn(event);
|
||||
const thread = this.getThread(threadId);
|
||||
thread?.timelineSet.replaceEventId(oldEventId, newEventId);
|
||||
const thread = threadId ? this.getThread(threadId) : undefined;
|
||||
thread?.timelineSet.replaceEventId(oldEventId, newEventId!);
|
||||
|
||||
if (shouldLiveInRoom) {
|
||||
// if the event was already in the timeline (which will be the case if
|
||||
// opts.pendingEventOrdering==chronological), we need to update the
|
||||
// timeline map.
|
||||
for (let i = 0; i < this.timelineSets.length; i++) {
|
||||
this.timelineSets[i].replaceEventId(oldEventId, newEventId);
|
||||
this.timelineSets[i].replaceEventId(oldEventId, newEventId!);
|
||||
}
|
||||
}
|
||||
} else if (newStatus == EventStatus.CANCELLED) {
|
||||
@ -2414,7 +2411,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
if (this.pendingEventList) {
|
||||
const removedEvent = this.getPendingEvent(oldEventId);
|
||||
this.removePendingEvent(oldEventId);
|
||||
if (removedEvent.isRedaction()) {
|
||||
if (removedEvent?.isRedaction()) {
|
||||
this.revertRedactionLocalEcho(removedEvent);
|
||||
}
|
||||
}
|
||||
@ -2449,7 +2446,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
* they will go to the end of the timeline.
|
||||
*
|
||||
* @param {MatrixEvent[]} events A list of events to add.
|
||||
* @param {IAddLiveEventOptions} options addLiveEvent options
|
||||
* @param {IAddLiveEventOptions} addLiveEventOptions addLiveEvent options
|
||||
* @throws If <code>duplicateStrategy</code> is not falsey, 'replace' or 'ignore'.
|
||||
*/
|
||||
public addLiveEvents(events: MatrixEvent[], addLiveEventOptions?: IAddLiveEventOptions): void;
|
||||
@ -2462,8 +2459,8 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
duplicateStrategyOrOpts?: DuplicateStrategy | IAddLiveEventOptions,
|
||||
fromCache = false,
|
||||
): void {
|
||||
let duplicateStrategy = duplicateStrategyOrOpts as DuplicateStrategy;
|
||||
let timelineWasEmpty = false;
|
||||
let duplicateStrategy: DuplicateStrategy | undefined = duplicateStrategyOrOpts as DuplicateStrategy;
|
||||
let timelineWasEmpty: boolean | undefined = false;
|
||||
if (typeof (duplicateStrategyOrOpts) === 'object') {
|
||||
({
|
||||
duplicateStrategy,
|
||||
@ -2679,7 +2676,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
const membershipEvent = this.currentState.getStateEvents(EventType.RoomMember, this.myUserId);
|
||||
if (membershipEvent) {
|
||||
const membership = membershipEvent.getContent().membership;
|
||||
this.updateMyMembership(membership);
|
||||
this.updateMyMembership(membership!);
|
||||
|
||||
if (membership === "invite") {
|
||||
const strippedStateEvents = membershipEvent.getUnsigned().invite_room_state || [];
|
||||
@ -2919,11 +2916,9 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
|
||||
}
|
||||
|
||||
// get members that are NOT ourselves and are actually in the room.
|
||||
let otherNames: string[] = null;
|
||||
let otherNames: string[] = [];
|
||||
if (this.summaryHeroes) {
|
||||
// if we have a summary, the member state events
|
||||
// should be in the room state
|
||||
otherNames = [];
|
||||
// if we have a summary, the member state events should be in the room state
|
||||
this.summaryHeroes.forEach((userId) => {
|
||||
// filter service members
|
||||
if (excludedUserIds.includes(userId)) {
|
||||
|
@ -38,13 +38,13 @@ export type UserEventHandlerMap = {
|
||||
};
|
||||
|
||||
export class User extends TypedEventEmitter<UserEvent, UserEventHandlerMap> {
|
||||
private modified: number;
|
||||
private modified = -1;
|
||||
|
||||
// XXX these should be read-only
|
||||
public displayName: string;
|
||||
public rawDisplayName: string;
|
||||
public avatarUrl: string;
|
||||
public presenceStatusMsg: string = null;
|
||||
public displayName?: string;
|
||||
public rawDisplayName?: string;
|
||||
public avatarUrl?: string;
|
||||
public presenceStatusMsg?: string;
|
||||
public presence = "offline";
|
||||
public lastActiveAgo = 0;
|
||||
public lastPresenceTs = 0;
|
||||
@ -52,10 +52,7 @@ export class User extends TypedEventEmitter<UserEvent, UserEventHandlerMap> {
|
||||
public events: {
|
||||
presence?: MatrixEvent;
|
||||
profile?: MatrixEvent;
|
||||
} = {
|
||||
presence: null,
|
||||
profile: null,
|
||||
};
|
||||
} = {};
|
||||
|
||||
/**
|
||||
* Construct a new User. A User must have an ID and can optionally have extra
|
||||
@ -83,7 +80,6 @@ export class User extends TypedEventEmitter<UserEvent, UserEventHandlerMap> {
|
||||
super();
|
||||
this.displayName = userId;
|
||||
this.rawDisplayName = userId;
|
||||
this.avatarUrl = null;
|
||||
this.updateModifiedTime();
|
||||
}
|
||||
|
||||
@ -150,11 +146,7 @@ export class User extends TypedEventEmitter<UserEvent, UserEventHandlerMap> {
|
||||
*/
|
||||
public setDisplayName(name: string): void {
|
||||
const oldName = this.displayName;
|
||||
if (typeof name === "string") {
|
||||
this.displayName = name;
|
||||
} else {
|
||||
this.displayName = undefined;
|
||||
}
|
||||
if (name !== oldName) {
|
||||
this.updateModifiedTime();
|
||||
}
|
||||
@ -165,12 +157,8 @@ export class User extends TypedEventEmitter<UserEvent, UserEventHandlerMap> {
|
||||
* in response to this as there is no underlying MatrixEvent to emit with.
|
||||
* @param {string} name The new display name.
|
||||
*/
|
||||
public setRawDisplayName(name: string): void {
|
||||
if (typeof name === "string") {
|
||||
public setRawDisplayName(name?: string): void {
|
||||
this.rawDisplayName = name;
|
||||
} else {
|
||||
this.rawDisplayName = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -178,7 +166,7 @@ export class User extends TypedEventEmitter<UserEvent, UserEventHandlerMap> {
|
||||
* as there is no underlying MatrixEvent to emit with.
|
||||
* @param {string} url The new avatar URL.
|
||||
*/
|
||||
public setAvatarUrl(url: string): void {
|
||||
public setAvatarUrl(url?: string): void {
|
||||
const oldUrl = this.avatarUrl;
|
||||
this.avatarUrl = url;
|
||||
if (url !== oldUrl) {
|
||||
|
@ -670,8 +670,8 @@ export class SlidingSyncSdk {
|
||||
// For each invited room member we want to give them a displayname/avatar url
|
||||
// if they have one (the m.room.member invites don't contain this).
|
||||
room.getMembersWithMembership("invite").forEach(function(member) {
|
||||
if (member._requestedProfileInfo) return;
|
||||
member._requestedProfileInfo = true;
|
||||
if (member.requestedProfileInfo) return;
|
||||
member.requestedProfileInfo = true;
|
||||
// try to get a cached copy first.
|
||||
const user = client.getUser(member.userId);
|
||||
let promise: ReturnType<MatrixClient["getProfileInfo"]>;
|
||||
|
@ -195,7 +195,7 @@ export interface IStore {
|
||||
* client state to where it was at the last save, or null if there
|
||||
* is no saved sync data.
|
||||
*/
|
||||
getSavedSync(): Promise<ISavedSync>;
|
||||
getSavedSync(): Promise<ISavedSync | null>;
|
||||
|
||||
/**
|
||||
* @return {Promise} If there is a saved sync, the nextBatch token
|
||||
|
@ -23,7 +23,7 @@ export interface IIndexedDBBackend {
|
||||
syncToDatabase(userTuples: UserTuple[]): Promise<void>;
|
||||
isNewlyCreated(): Promise<boolean>;
|
||||
setSyncData(syncData: ISyncResponse): Promise<void>;
|
||||
getSavedSync(): Promise<ISavedSync>;
|
||||
getSavedSync(): Promise<ISavedSync | null>;
|
||||
getNextBatchToken(): Promise<string>;
|
||||
clearDatabase(): Promise<void>;
|
||||
getOutOfBandMembers(roomId: string): Promise<IStateEventWithRoomId[] | null>;
|
||||
|
@ -124,7 +124,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
|
||||
private readonly dbName: string;
|
||||
private readonly syncAccumulator: SyncAccumulator;
|
||||
private db: IDBDatabase = null;
|
||||
private db?: IDBDatabase;
|
||||
private disconnected = true;
|
||||
private _isNewlyCreated = false;
|
||||
private isPersisting = false;
|
||||
@ -141,8 +141,8 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
* @param {string=} dbName Optional database name. The same name must be used
|
||||
* to open the same database.
|
||||
*/
|
||||
constructor(private readonly indexedDB: IDBFactory, dbName: string) {
|
||||
this.dbName = "matrix-js-sdk:" + (dbName || "default");
|
||||
constructor(private readonly indexedDB: IDBFactory, dbName = "default") {
|
||||
this.dbName = "matrix-js-sdk:" + dbName;
|
||||
this.syncAccumulator = new SyncAccumulator();
|
||||
}
|
||||
|
||||
@ -188,7 +188,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
// add a poorly-named listener for when deleteDatabase is called
|
||||
// so we can close our db connections.
|
||||
this.db.onversionchange = () => {
|
||||
this.db.close();
|
||||
this.db?.close();
|
||||
};
|
||||
|
||||
return this.init();
|
||||
@ -229,7 +229,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
*/
|
||||
public getOutOfBandMembers(roomId: string): Promise<IStateEventWithRoomId[] | null> {
|
||||
return new Promise<IStateEventWithRoomId[] | null>((resolve, reject) => {
|
||||
const tx = this.db.transaction(["oob_membership_events"], "readonly");
|
||||
const tx = this.db!.transaction(["oob_membership_events"], "readonly");
|
||||
const store = tx.objectStore("oob_membership_events");
|
||||
const roomIndex = store.index("room");
|
||||
const range = IDBKeyRange.only(roomId);
|
||||
@ -279,7 +279,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
public async setOutOfBandMembers(roomId: string, membershipEvents: IStateEventWithRoomId[]): Promise<void> {
|
||||
logger.log(`LL: backend about to store ${membershipEvents.length}` +
|
||||
` members for ${roomId}`);
|
||||
const tx = this.db.transaction(["oob_membership_events"], "readwrite");
|
||||
const tx = this.db!.transaction(["oob_membership_events"], "readwrite");
|
||||
const store = tx.objectStore("oob_membership_events");
|
||||
membershipEvents.forEach((e) => {
|
||||
store.put(e);
|
||||
@ -306,7 +306,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
// keys in the store.
|
||||
// this should be way faster than deleting every member
|
||||
// individually for a large room.
|
||||
const readTx = this.db.transaction(
|
||||
const readTx = this.db!.transaction(
|
||||
["oob_membership_events"],
|
||||
"readonly");
|
||||
const store = readTx.objectStore("oob_membership_events");
|
||||
@ -322,7 +322,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
const [minStateKey, maxStateKey] = await Promise.all(
|
||||
[minStateKeyProm, maxStateKeyProm]);
|
||||
|
||||
const writeTx = this.db.transaction(
|
||||
const writeTx = this.db!.transaction(
|
||||
["oob_membership_events"],
|
||||
"readwrite");
|
||||
const writeStore = writeTx.objectStore("oob_membership_events");
|
||||
@ -374,7 +374,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
* client state to where it was at the last save, or null if there
|
||||
* is no saved sync data.
|
||||
*/
|
||||
public getSavedSync(copy = true): Promise<ISavedSync> {
|
||||
public getSavedSync(copy = true): Promise<ISavedSync | null> {
|
||||
const data = this.syncAccumulator.getJSON();
|
||||
if (!data.nextBatch) return Promise.resolve(null);
|
||||
if (copy) {
|
||||
@ -431,7 +431,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
): Promise<void> {
|
||||
logger.log("Persisting sync data up to", nextBatch);
|
||||
return utils.promiseTry<void>(() => {
|
||||
const txn = this.db.transaction(["sync"], "readwrite");
|
||||
const txn = this.db!.transaction(["sync"], "readwrite");
|
||||
const store = txn.objectStore("sync");
|
||||
store.put({
|
||||
clobber: "-", // constant key so will always clobber
|
||||
@ -452,7 +452,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
*/
|
||||
private persistAccountData(accountData: IMinimalEvent[]): Promise<void> {
|
||||
return utils.promiseTry<void>(() => {
|
||||
const txn = this.db.transaction(["accountData"], "readwrite");
|
||||
const txn = this.db!.transaction(["accountData"], "readwrite");
|
||||
const store = txn.objectStore("accountData");
|
||||
for (let i = 0; i < accountData.length; i++) {
|
||||
store.put(accountData[i]); // put == UPSERT
|
||||
@ -471,7 +471,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
*/
|
||||
private persistUserPresenceEvents(tuples: UserTuple[]): Promise<void> {
|
||||
return utils.promiseTry<void>(() => {
|
||||
const txn = this.db.transaction(["users"], "readwrite");
|
||||
const txn = this.db!.transaction(["users"], "readwrite");
|
||||
const store = txn.objectStore("users");
|
||||
for (const tuple of tuples) {
|
||||
store.put({
|
||||
@ -491,7 +491,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
*/
|
||||
public getUserPresenceEvents(): Promise<UserTuple[]> {
|
||||
return utils.promiseTry<UserTuple[]>(() => {
|
||||
const txn = this.db.transaction(["users"], "readonly");
|
||||
const txn = this.db!.transaction(["users"], "readonly");
|
||||
const store = txn.objectStore("users");
|
||||
return selectQuery(store, undefined, (cursor) => {
|
||||
return [cursor.value.userId, cursor.value.event];
|
||||
@ -506,7 +506,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
private loadAccountData(): Promise<IMinimalEvent[]> {
|
||||
logger.log(`LocalIndexedDBStoreBackend: loading account data...`);
|
||||
return utils.promiseTry<IMinimalEvent[]>(() => {
|
||||
const txn = this.db.transaction(["accountData"], "readonly");
|
||||
const txn = this.db!.transaction(["accountData"], "readonly");
|
||||
const store = txn.objectStore("accountData");
|
||||
return selectQuery(store, undefined, (cursor) => {
|
||||
return cursor.value;
|
||||
@ -524,7 +524,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
private loadSyncData(): Promise<ISyncData> {
|
||||
logger.log(`LocalIndexedDBStoreBackend: loading sync data...`);
|
||||
return utils.promiseTry<ISyncData>(() => {
|
||||
const txn = this.db.transaction(["sync"], "readonly");
|
||||
const txn = this.db!.transaction(["sync"], "readonly");
|
||||
const store = txn.objectStore("sync");
|
||||
return selectQuery(store, undefined, (cursor) => {
|
||||
return cursor.value;
|
||||
@ -540,7 +540,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
|
||||
public getClientOptions(): Promise<IStartClientOpts> {
|
||||
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");
|
||||
return selectQuery(store, undefined, (cursor) => {
|
||||
return cursor.value?.options;
|
||||
@ -549,7 +549,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
}
|
||||
|
||||
public async storeClientOptions(options: IStartClientOpts): Promise<void> {
|
||||
const txn = this.db.transaction(["client_options"], "readwrite");
|
||||
const txn = this.db!.transaction(["client_options"], "readwrite");
|
||||
const store = txn.objectStore("client_options");
|
||||
store.put({
|
||||
clobber: "-", // constant key so will always clobber
|
||||
@ -559,7 +559,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
}
|
||||
|
||||
public async saveToDeviceBatches(batches: ToDeviceBatchWithTxnId[]): Promise<void> {
|
||||
const txn = this.db.transaction(["to_device_queue"], "readwrite");
|
||||
const txn = this.db!.transaction(["to_device_queue"], "readwrite");
|
||||
const store = txn.objectStore("to_device_queue");
|
||||
for (const batch of batches) {
|
||||
store.add(batch);
|
||||
@ -568,7 +568,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
}
|
||||
|
||||
public async getOldestToDeviceBatch(): Promise<IndexedToDeviceBatch | null> {
|
||||
const txn = this.db.transaction(["to_device_queue"], "readonly");
|
||||
const txn = this.db!.transaction(["to_device_queue"], "readonly");
|
||||
const store = txn.objectStore("to_device_queue");
|
||||
const cursor = await reqAsCursorPromise(store.openCursor());
|
||||
if (!cursor) return null;
|
||||
@ -584,7 +584,7 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
}
|
||||
|
||||
public async removeToDeviceBatch(id: number): Promise<void> {
|
||||
const txn = this.db.transaction(["to_device_queue"], "readwrite");
|
||||
const txn = this.db!.transaction(["to_device_queue"], "readwrite");
|
||||
const store = txn.objectStore("to_device_queue");
|
||||
store.delete(id);
|
||||
await txnAsPromise(txn);
|
||||
|
@ -23,13 +23,13 @@ import { IIndexedDBBackend, UserTuple } from "./indexeddb-backend";
|
||||
import { IndexedToDeviceBatch, ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage";
|
||||
|
||||
export class RemoteIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
private worker: Worker;
|
||||
private worker?: Worker;
|
||||
private nextSeq = 0;
|
||||
// The currently in-flight requests to the actual backend
|
||||
private inFlight: Record<number, IDeferred<any>> = {}; // seq: promise
|
||||
// Once we start connecting, we keep the promise and re-use it
|
||||
// if we try to connect again
|
||||
private startPromise: Promise<void> = null;
|
||||
private startPromise?: Promise<void>;
|
||||
|
||||
/**
|
||||
* An IndexedDB store backend where the actual backend sits in a web
|
||||
@ -44,7 +44,7 @@ export class RemoteIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
*/
|
||||
constructor(
|
||||
private readonly workerFactory: () => Worker,
|
||||
private readonly dbName: string,
|
||||
private readonly dbName?: string,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -147,12 +147,12 @@ export class RemoteIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
}
|
||||
|
||||
private ensureStarted(): Promise<void> {
|
||||
if (this.startPromise === null) {
|
||||
if (!this.startPromise) {
|
||||
this.worker = this.workerFactory();
|
||||
this.worker.onmessage = this.onWorkerMessage;
|
||||
|
||||
// tell the worker the db name.
|
||||
this.startPromise = this.doCmd('_setupWorker', [this.dbName]).then(() => {
|
||||
this.startPromise = this.doCmd('setupWorker', [this.dbName]).then(() => {
|
||||
logger.log("IndexedDB worker is ready");
|
||||
});
|
||||
}
|
||||
@ -168,7 +168,7 @@ export class RemoteIndexedDBStoreBackend implements IIndexedDBBackend {
|
||||
|
||||
this.inFlight[seq] = def;
|
||||
|
||||
this.worker.postMessage({ command, seq, args });
|
||||
this.worker?.postMessage({ command, seq, args });
|
||||
|
||||
return def.promise;
|
||||
});
|
||||
|
@ -20,7 +20,7 @@ import { logger } from '../logger';
|
||||
interface ICmd {
|
||||
command: string;
|
||||
seq: number;
|
||||
args?: any[];
|
||||
args: any[];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -39,7 +39,7 @@ interface ICmd {
|
||||
*
|
||||
*/
|
||||
export class IndexedDBStoreWorker {
|
||||
private backend: LocalIndexedDBStoreBackend = null;
|
||||
private backend?: LocalIndexedDBStoreBackend;
|
||||
|
||||
/**
|
||||
* @param {function} postMessage The web worker postMessage function that
|
||||
@ -58,59 +58,59 @@ export class IndexedDBStoreWorker {
|
||||
let prom;
|
||||
|
||||
switch (msg.command) {
|
||||
case '_setupWorker':
|
||||
case 'setupWorker':
|
||||
// this is the 'indexedDB' global (where global != window
|
||||
// because it's a web worker and there is no window).
|
||||
this.backend = new LocalIndexedDBStoreBackend(indexedDB, msg.args[0]);
|
||||
prom = Promise.resolve();
|
||||
break;
|
||||
case 'connect':
|
||||
prom = this.backend.connect();
|
||||
prom = this.backend?.connect();
|
||||
break;
|
||||
case 'isNewlyCreated':
|
||||
prom = this.backend.isNewlyCreated();
|
||||
prom = this.backend?.isNewlyCreated();
|
||||
break;
|
||||
case 'clearDatabase':
|
||||
prom = this.backend.clearDatabase();
|
||||
prom = this.backend?.clearDatabase();
|
||||
break;
|
||||
case 'getSavedSync':
|
||||
prom = this.backend.getSavedSync(false);
|
||||
prom = this.backend?.getSavedSync(false);
|
||||
break;
|
||||
case 'setSyncData':
|
||||
prom = this.backend.setSyncData(msg.args[0]);
|
||||
prom = this.backend?.setSyncData(msg.args[0]);
|
||||
break;
|
||||
case 'syncToDatabase':
|
||||
prom = this.backend.syncToDatabase(msg.args[0]);
|
||||
prom = this.backend?.syncToDatabase(msg.args[0]);
|
||||
break;
|
||||
case 'getUserPresenceEvents':
|
||||
prom = this.backend.getUserPresenceEvents();
|
||||
prom = this.backend?.getUserPresenceEvents();
|
||||
break;
|
||||
case 'getNextBatchToken':
|
||||
prom = this.backend.getNextBatchToken();
|
||||
prom = this.backend?.getNextBatchToken();
|
||||
break;
|
||||
case 'getOutOfBandMembers':
|
||||
prom = this.backend.getOutOfBandMembers(msg.args[0]);
|
||||
prom = this.backend?.getOutOfBandMembers(msg.args[0]);
|
||||
break;
|
||||
case 'clearOutOfBandMembers':
|
||||
prom = this.backend.clearOutOfBandMembers(msg.args[0]);
|
||||
prom = this.backend?.clearOutOfBandMembers(msg.args[0]);
|
||||
break;
|
||||
case 'setOutOfBandMembers':
|
||||
prom = this.backend.setOutOfBandMembers(msg.args[0], msg.args[1]);
|
||||
prom = this.backend?.setOutOfBandMembers(msg.args[0], msg.args[1]);
|
||||
break;
|
||||
case 'getClientOptions':
|
||||
prom = this.backend.getClientOptions();
|
||||
prom = this.backend?.getClientOptions();
|
||||
break;
|
||||
case 'storeClientOptions':
|
||||
prom = this.backend.storeClientOptions(msg.args[0]);
|
||||
prom = this.backend?.storeClientOptions(msg.args[0]);
|
||||
break;
|
||||
case 'saveToDeviceBatches':
|
||||
prom = this.backend.saveToDeviceBatches(msg.args[0]);
|
||||
prom = this.backend?.saveToDeviceBatches(msg.args[0]);
|
||||
break;
|
||||
case 'getOldestToDeviceBatch':
|
||||
prom = this.backend.getOldestToDeviceBatch();
|
||||
prom = this.backend?.getOldestToDeviceBatch();
|
||||
break;
|
||||
case 'removeToDeviceBatch':
|
||||
prom = this.backend.removeToDeviceBatch(msg.args[0]);
|
||||
prom = this.backend?.removeToDeviceBatch(msg.args[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -155,7 +155,7 @@ export class IndexedDBStore extends MemoryStore {
|
||||
* client state to where it was at the last save, or null if there
|
||||
* is no saved sync data.
|
||||
*/
|
||||
public getSavedSync = this.degradable((): Promise<ISavedSync> => {
|
||||
public getSavedSync = this.degradable((): Promise<ISavedSync | null> => {
|
||||
return this.backend.getSavedSync();
|
||||
}, "getSavedSync");
|
||||
|
||||
@ -292,16 +292,16 @@ export class IndexedDBStore extends MemoryStore {
|
||||
*/
|
||||
private degradable<A extends Array<any>, R = void>(
|
||||
func: DegradableFn<A, R>,
|
||||
fallback?: string,
|
||||
fallback?: keyof MemoryStore,
|
||||
): DegradableFn<A, R> {
|
||||
const fallbackFn = super[fallback];
|
||||
const fallbackFn = fallback ? super[fallback] as Function : null;
|
||||
|
||||
return async (...args) => {
|
||||
try {
|
||||
return await func.call(this, ...args);
|
||||
} catch (e) {
|
||||
logger.error("IndexedDBStore failure, degrading to MemoryStore", e);
|
||||
this.emitter.emit("degraded", e);
|
||||
this.emitter.emit("degraded", e as Error);
|
||||
try {
|
||||
// We try to delete IndexedDB after degrading since this store is only a
|
||||
// cache (the app will still function correctly without the data).
|
||||
|
@ -32,7 +32,7 @@ import { ISyncResponse } from "../sync-accumulator";
|
||||
import { IStateEventWithRoomId } from "../@types/search";
|
||||
import { IndexedToDeviceBatch, ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage";
|
||||
|
||||
function isValidFilterId(filterId: string): boolean {
|
||||
function isValidFilterId(filterId?: string | number | null): boolean {
|
||||
const isValidStr = typeof filterId === "string" &&
|
||||
!!filterId &&
|
||||
filterId !== "undefined" && // exclude these as we've serialized undefined in localStorage before
|
||||
@ -55,13 +55,13 @@ export interface IOpts {
|
||||
export class MemoryStore implements IStore {
|
||||
private rooms: Record<string, Room> = {}; // roomId: Room
|
||||
private users: Record<string, User> = {}; // userId: User
|
||||
private syncToken: string = null;
|
||||
private syncToken: string | null = null;
|
||||
// userId: {
|
||||
// filterId: Filter
|
||||
// }
|
||||
private filters: Record<string, Record<string, Filter>> = {};
|
||||
public accountData: Record<string, MatrixEvent> = {}; // type : content
|
||||
protected readonly localStorage: Storage;
|
||||
protected readonly localStorage?: Storage;
|
||||
private oobMembers: Record<string, IStateEventWithRoomId[]> = {}; // roomId: [member events]
|
||||
private pendingEvents: { [roomId: string]: Partial<IEvent>[] } = {};
|
||||
private clientOptions = {};
|
||||
@ -115,7 +115,7 @@ export class MemoryStore implements IStore {
|
||||
* @param {RoomState} state
|
||||
* @param {RoomMember} member
|
||||
*/
|
||||
private onRoomMember = (event: MatrixEvent, state: RoomState, member: RoomMember) => {
|
||||
private onRoomMember = (event: MatrixEvent | null, state: RoomState, member: RoomMember) => {
|
||||
if (member.membership === "invite") {
|
||||
// We do NOT add invited members because people love to typo user IDs
|
||||
// which would then show up in these lists (!)
|
||||
@ -126,9 +126,7 @@ export class MemoryStore implements IStore {
|
||||
if (member.name) {
|
||||
user.setDisplayName(member.name);
|
||||
if (member.events.member) {
|
||||
user.setRawDisplayName(
|
||||
member.events.member.getDirectionalContent().displayname,
|
||||
);
|
||||
user.setRawDisplayName(member.events.member.getDirectionalContent().displayname);
|
||||
}
|
||||
}
|
||||
if (member.events.member && member.events.member.getContent().avatar_url) {
|
||||
@ -227,9 +225,7 @@ export class MemoryStore implements IStore {
|
||||
* @param {Filter} filter
|
||||
*/
|
||||
public storeFilter(filter: Filter): void {
|
||||
if (!filter?.userId) {
|
||||
return;
|
||||
}
|
||||
if (!filter?.userId || !filter?.filterId) return;
|
||||
if (!this.filters[filter.userId]) {
|
||||
this.filters[filter.userId] = {};
|
||||
}
|
||||
@ -352,7 +348,7 @@ export class MemoryStore implements IStore {
|
||||
* client state to where it was at the last save, or null if there
|
||||
* is no saved sync data.
|
||||
*/
|
||||
public getSavedSync(): Promise<ISavedSync> {
|
||||
public getSavedSync(): Promise<ISavedSync | null> {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
|
@ -1615,8 +1615,8 @@ export class SyncApi {
|
||||
// For each invited room member we want to give them a displayname/avatar url
|
||||
// if they have one (the m.room.member invites don't contain this).
|
||||
room.getMembersWithMembership("invite").forEach(function(member) {
|
||||
if (member._requestedProfileInfo) return;
|
||||
member._requestedProfileInfo = true;
|
||||
if (member.requestedProfileInfo) return;
|
||||
member.requestedProfileInfo = true;
|
||||
// try to get a cached copy first.
|
||||
const user = client.getUser(member.userId);
|
||||
let promise;
|
||||
|
@ -2210,7 +2210,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
|
||||
this.opponentPartyId = msg.party_id || null;
|
||||
}
|
||||
this.opponentCaps = msg.capabilities || {} as CallCapabilities;
|
||||
this.opponentMember = ev.sender;
|
||||
this.opponentMember = ev.sender!; // XXX: we should use ev.getSender() and just store the userId
|
||||
}
|
||||
|
||||
private async addBufferedIceCandidates(): Promise<void> {
|
||||
|
Reference in New Issue
Block a user