Skip to content

Commit 063d69e

Browse files
authored
Fix mark as unread button (#3393)
* Fix mark as unread button * Revert to prefer the last event from the main timeline * Refactor room test * Fix type * Improve docs * Insert events to the end of the timeline * Improve test doc
1 parent 614f446 commit 063d69e

File tree

3 files changed

+89
-51
lines changed

3 files changed

+89
-51
lines changed

spec/test-utils/thread.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,34 @@ type MakeThreadProps = {
115115
ts?: number;
116116
};
117117

118+
type MakeThreadResult = {
119+
/**
120+
* Thread model
121+
*/
122+
thread: Thread;
123+
/**
124+
* Thread root event
125+
*/
126+
rootEvent: MatrixEvent;
127+
/**
128+
* Events added to the thread
129+
*/
130+
events: MatrixEvent[];
131+
};
132+
133+
/**
134+
* Starts a new thread in a room by creating a message as thread root.
135+
* Also creates a Thread model and adds it to the room.
136+
* Does not insert the messages into a timeline.
137+
*/
118138
export const mkThread = ({
119139
room,
120140
client,
121141
authorId,
122142
participantUserIds,
123143
length = 2,
124144
ts = 1,
125-
}: MakeThreadProps): { thread: Thread; rootEvent: MatrixEvent; events: MatrixEvent[] } => {
145+
}: MakeThreadProps): MakeThreadResult => {
126146
const { rootEvent, events } = makeThreadEvents({
127147
roomId: room.roomId,
128148
authorId,

spec/unit/room.spec.ts

Lines changed: 67 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import { TestClient } from "../TestClient";
5151
import { ReceiptType, WrappedReceipt } from "../../src/@types/read_receipts";
5252
import { FeatureSupport, Thread, THREAD_RELATION_TYPE, ThreadEvent } from "../../src/models/thread";
5353
import { Crypto } from "../../src/crypto";
54-
import { mkThread } from "../test-utils/thread";
54+
import * as threadUtils from "../test-utils/thread";
5555
import { getMockClientWithEventEmitter, mockClientMethodsUser } from "../test-utils/client";
5656
import { logger } from "../../src/logger";
5757
import { IMessageOpts } from "../test-utils/test-utils";
@@ -168,30 +168,45 @@ describe("Room", function () {
168168
room.client,
169169
);
170170

171-
const addRoomMainAndThreadMessages = (
172-
room: Room,
173-
tsMain?: number,
174-
tsThread?: number,
175-
): { mainEvent?: MatrixEvent; threadEvent?: MatrixEvent } => {
176-
const result: { mainEvent?: MatrixEvent; threadEvent?: MatrixEvent } = {};
177-
178-
if (tsMain) {
179-
result.mainEvent = mkMessage({ ts: tsMain });
180-
room.addLiveEvents([result.mainEvent]);
181-
}
171+
/**
172+
* @see threadUtils.mkThread
173+
*/
174+
const mkThread = (
175+
opts: Partial<Parameters<typeof threadUtils.mkThread>[0]>,
176+
): ReturnType<typeof threadUtils.mkThread> => {
177+
return threadUtils.mkThread({
178+
room,
179+
client: new TestClient().client,
180+
authorId: "@bob:example.org",
181+
participantUserIds: ["@bob:example.org"],
182+
...opts,
183+
});
184+
};
182185

183-
if (tsThread) {
184-
const { rootEvent, thread } = mkThread({
185-
room,
186-
client: new TestClient().client,
187-
authorId: "@bob:example.org",
188-
participantUserIds: ["@bob:example.org"],
189-
});
190-
result.threadEvent = mkThreadResponse(rootEvent, { ts: tsThread });
191-
thread.liveTimeline.addEvent(result.threadEvent, { toStartOfTimeline: true });
192-
}
186+
/**
187+
* Creates a message and adds it to the end of the main live timeline.
188+
*
189+
* @param room - Room to add the message to
190+
* @param timestamp - Timestamp of the message
191+
* @return The message event
192+
*/
193+
const mkMessageInRoom = (room: Room, timestamp: number) => {
194+
const message = mkMessage({ ts: timestamp });
195+
room.addLiveEvents([message]);
196+
return message;
197+
};
193198

194-
return result;
199+
/**
200+
* Creates a message in a thread and adds it to the end of the thread live timeline.
201+
*
202+
* @param thread - Thread to add the message to
203+
* @param timestamp - Timestamp of the message
204+
* @returns The thread message event
205+
*/
206+
const mkMessageInThread = (thread: Thread, timestamp: number) => {
207+
const message = mkThreadResponse(thread.rootEvent!, { ts: timestamp });
208+
thread.liveTimeline.addEvent(message, { toStartOfTimeline: false });
209+
return message;
195210
};
196211

197212
const addRoomThreads = (
@@ -202,24 +217,14 @@ describe("Room", function () {
202217
const result: { thread1?: Thread; thread2?: Thread } = {};
203218

204219
if (thread1EventTs !== null) {
205-
const { rootEvent: thread1RootEvent, thread: thread1 } = mkThread({
206-
room,
207-
client: new TestClient().client,
208-
authorId: "@bob:example.org",
209-
participantUserIds: ["@bob:example.org"],
210-
});
220+
const { rootEvent: thread1RootEvent, thread: thread1 } = mkThread({ room });
211221
const thread1Event = mkThreadResponse(thread1RootEvent, { ts: thread1EventTs });
212222
thread1.liveTimeline.addEvent(thread1Event, { toStartOfTimeline: true });
213223
result.thread1 = thread1;
214224
}
215225

216226
if (thread2EventTs !== null) {
217-
const { rootEvent: thread2RootEvent, thread: thread2 } = mkThread({
218-
room,
219-
client: new TestClient().client,
220-
authorId: "@bob:example.org",
221-
participantUserIds: ["@bob:example.org"],
222-
});
227+
const { rootEvent: thread2RootEvent, thread: thread2 } = mkThread({ room });
223228
const thread2Event = mkThreadResponse(thread2RootEvent, { ts: thread2EventTs });
224229
thread2.liveTimeline.addEvent(thread2Event, { toStartOfTimeline: true });
225230
result.thread2 = thread2;
@@ -2504,12 +2509,7 @@ describe("Room", function () {
25042509
});
25052510

25062511
it("returns the same model when creating a thread twice", () => {
2507-
const { thread, rootEvent } = mkThread({
2508-
room,
2509-
client: new TestClient().client,
2510-
authorId: "@bob:example.org",
2511-
participantUserIds: ["@bob:example.org"],
2512-
});
2512+
const { thread, rootEvent } = mkThread({ room });
25132513

25142514
expect(thread).toBeInstanceOf(Thread);
25152515

@@ -3534,32 +3534,50 @@ describe("Room", function () {
35343534
});
35353535

35363536
describe("getLastLiveEvent", () => {
3537-
let lastEventInMainTimeline: MatrixEvent;
3538-
let lastEventInThread: MatrixEvent;
3539-
35403537
it("when there are no events, it should return undefined", () => {
35413538
expect(room.getLastLiveEvent()).toBeUndefined();
35423539
});
35433540

35443541
it("when there is only an event in the main timeline and there are no threads, it should return the last event from the main timeline", () => {
3545-
lastEventInMainTimeline = addRoomMainAndThreadMessages(room, 23).mainEvent!;
3546-
room.addLiveEvents([lastEventInMainTimeline]);
3542+
const lastEventInMainTimeline = mkMessageInRoom(room, 23);
35473543
expect(room.getLastLiveEvent()).toBe(lastEventInMainTimeline);
35483544
});
35493545

3546+
/**
3547+
* This should normally not happen. The test exists only for the sake of completeness.
3548+
* No event is added to the room's live timeline here.
3549+
*/
35503550
it("when there is no event in the room live timeline but in a thread, it should return the last event from the thread", () => {
3551-
lastEventInThread = addRoomMainAndThreadMessages(room, undefined, 42).threadEvent!;
3551+
const { thread } = mkThread({ room, length: 0 });
3552+
const lastEventInThread = mkMessageInThread(thread, 42);
35523553
expect(room.getLastLiveEvent()).toBe(lastEventInThread);
35533554
});
35543555

35553556
describe("when there are events in both, the main timeline and threads", () => {
35563557
it("and the last event is in a thread, it should return the last event from the thread", () => {
3557-
lastEventInThread = addRoomMainAndThreadMessages(room, 23, 42).threadEvent!;
3558+
mkMessageInRoom(room, 23);
3559+
const { thread } = mkThread({ room, length: 0 });
3560+
const lastEventInThread = mkMessageInThread(thread, 42);
35583561
expect(room.getLastLiveEvent()).toBe(lastEventInThread);
35593562
});
35603563

35613564
it("and the last event is in the main timeline, it should return the last event from the main timeline", () => {
3562-
lastEventInMainTimeline = addRoomMainAndThreadMessages(room, 42, 23).mainEvent!;
3565+
const lastEventInMainTimeline = mkMessageInRoom(room, 42);
3566+
const { thread } = mkThread({ room, length: 0 });
3567+
mkMessageInThread(thread, 23);
3568+
expect(room.getLastLiveEvent()).toBe(lastEventInMainTimeline);
3569+
});
3570+
3571+
it("and both events have the same timestamp, it should return the last event from the thread", () => {
3572+
mkMessageInRoom(room, 23);
3573+
const { thread } = mkThread({ room, length: 0 });
3574+
const lastEventInThread = mkMessageInThread(thread, 23);
3575+
expect(room.getLastLiveEvent()).toBe(lastEventInThread);
3576+
});
3577+
3578+
it("and there is a thread without any messages, it should return the last event from the main timeline", () => {
3579+
const lastEventInMainTimeline = mkMessageInRoom(room, 23);
3580+
mkThread({ room, length: 0 });
35633581
expect(room.getLastLiveEvent()).toBe(lastEventInMainTimeline);
35643582
});
35653583
});

src/models/room.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
809809

810810
const lastThreadEvent = lastThread.events[lastThread.events.length - 1];
811811

812-
return (lastRoomEvent?.getTs() ?? 0) > (lastThreadEvent.getTs() ?? 0) ? lastRoomEvent : lastThreadEvent;
812+
return (lastRoomEvent?.getTs() ?? 0) > (lastThreadEvent?.getTs() ?? 0) ? lastRoomEvent : lastThreadEvent;
813813
}
814814

815815
/**

0 commit comments

Comments
 (0)