Loading threads with server-side assistance (#9356)

* Fix bug with message context menu
* fix bug where ThreadSummary failed if no last reply is available
* Fix relations direction API
* Use same API for threads as for any other timeline
* Determine if event belongs to thread on jumping to event
* properly listen to thread deletion
* Add thread redaction tests
* Add fetchInitialEvent tests
* Paginate using default TimelinePanel behaviour
* Remove unused threads deleted code

Co-authored-by: Germain <germain@souquet.com>
Co-authored-by: Germain <germains@element.io>
This commit is contained in:
Janne Mareike Koschinski 2022-10-28 13:48:15 +02:00 committed by GitHub
parent 750ca78e98
commit d92fdc1f5b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 205 additions and 82 deletions

View file

@ -18,21 +18,27 @@ import { M_LOCATION } from "matrix-js-sdk/src/@types/location";
import {
EventStatus,
EventType,
IEvent,
MatrixClient,
MatrixEvent,
MsgType,
PendingEventOrdering,
RelationType,
Room,
} from "matrix-js-sdk/src/matrix";
import { Thread } from "matrix-js-sdk/src/models/thread";
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
import {
canCancel,
canEditContent,
canEditOwnEvent,
fetchInitialEvent,
isContentActionable,
isLocationEvent,
isVoiceMessage,
} from "../../src/utils/EventUtils";
import { getMockClientWithEventEmitter, makeBeaconInfoEvent, makePollStartEvent } from "../test-utils";
import { getMockClientWithEventEmitter, makeBeaconInfoEvent, makePollStartEvent, stubClient } from "../test-utils";
describe('EventUtils', () => {
const userId = '@user:server';
@ -336,4 +342,92 @@ describe('EventUtils', () => {
expect(canCancel(status)).toBe(false);
});
});
describe("fetchInitialEvent", () => {
const ROOM_ID = "!roomId:example.org";
let room: Room;
let client: MatrixClient;
const NORMAL_EVENT = "$normalEvent";
const THREAD_ROOT = "$threadRoot";
const THREAD_REPLY = "$threadReply";
const events: Record<string, Partial<IEvent>> = {
[NORMAL_EVENT]: {
event_id: NORMAL_EVENT,
type: EventType.RoomMessage,
content: {
"body": "Classic event",
"msgtype": MsgType.Text,
},
},
[THREAD_ROOT]: {
event_id: THREAD_ROOT,
type: EventType.RoomMessage,
content: {
"body": "Thread root",
"msgtype": "m.text",
},
unsigned: {
"m.relations": {
[RelationType.Thread]: {
latest_event: {
event_id: THREAD_REPLY,
type: EventType.RoomMessage,
content: {
"body": "Thread reply",
"msgtype": MsgType.Text,
"m.relates_to": {
event_id: "$threadRoot",
rel_type: RelationType.Thread,
},
},
},
count: 1,
current_user_participated: false,
},
},
},
},
[THREAD_REPLY]: {
event_id: THREAD_REPLY,
type: EventType.RoomMessage,
content: {
"body": "Thread reply",
"msgtype": MsgType.Text,
"m.relates_to": {
event_id: THREAD_ROOT,
rel_type: RelationType.Thread,
},
},
},
};
beforeEach(() => {
jest.clearAllMocks();
stubClient();
client = MatrixClientPeg.get();
room = new Room(ROOM_ID, client, client.getUserId(), {
pendingEventOrdering: PendingEventOrdering.Detached,
});
jest.spyOn(client, "supportsExperimentalThreads").mockReturnValue(true);
jest.spyOn(client, "getRoom").mockReturnValue(room);
jest.spyOn(client, "fetchRoomEvent").mockImplementation(async (roomId, eventId) => {
return events[eventId] ?? Promise.reject();
});
});
it("returns null for unknown events", async () => {
expect(await fetchInitialEvent(client, room.roomId, "$UNKNOWN")).toBeNull();
expect(await fetchInitialEvent(client, room.roomId, NORMAL_EVENT)).toBeInstanceOf(MatrixEvent);
});
it("creates a thread when needed", async () => {
await fetchInitialEvent(client, room.roomId, THREAD_REPLY);
expect(room.getThread(THREAD_ROOT)).toBeInstanceOf(Thread);
});
});
});