diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index f15318d572..f88b34e8bc 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -214,7 +214,12 @@ export default class ThreadView extends React.Component { let thread = this.props.room.getThread(eventId); if (!thread) { - thread = this.props.room.createThread(eventId, mxEv, [mxEv], true); + const events = []; + // if the event is still being sent, don't include it in the Thread yet - otherwise the timeline panel + // will attempt to show it twice (once as a regular event, once as a pending event) and everything will + // blow up + if (mxEv.status === null) events.push(mxEv); + thread = this.props.room.createThread(eventId, mxEv, events, true); } this.updateThread(thread); diff --git a/test/components/structures/ThreadView-test.tsx b/test/components/structures/ThreadView-test.tsx index fb5440d768..857137a5f2 100644 --- a/test/components/structures/ThreadView-test.tsx +++ b/test/components/structures/ThreadView-test.tsx @@ -19,7 +19,7 @@ import userEvent from "@testing-library/user-event"; import { mocked } from "jest-mock"; import { MsgType, RelationType } from "matrix-js-sdk/src/@types/event"; import { MatrixClient, PendingEventOrdering } from "matrix-js-sdk/src/client"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { EventStatus, MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Room } from "matrix-js-sdk/src/models/room"; import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread"; import React, { useState } from "react"; @@ -35,7 +35,7 @@ import DMRoomMap from "../../../src/utils/DMRoomMap"; import ResizeNotifier from "../../../src/utils/ResizeNotifier"; import { mockPlatformPeg } from "../../test-utils/platform"; import { getRoomContext } from "../../test-utils/room"; -import { stubClient } from "../../test-utils/test-utils"; +import { mkMessage, stubClient } from "../../test-utils/test-utils"; import { mkThread } from "../../test-utils/threads"; describe("ThreadView", () => { @@ -135,6 +135,24 @@ describe("ThreadView", () => { jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(SENDER); }); + it("does not include pending root event in the timeline twice", async () => { + rootEvent = mkMessage({ + user: mockClient.getUserId()!, + event: true, + room: room.roomId, + msg: "root event message " + Math.random(), + }); + + rootEvent.status = EventStatus.SENDING; + rootEvent.setTxnId("1234"); + room.addPendingEvent(rootEvent, "1234"); + room.updatePendingEvent(rootEvent, EventStatus.SENT, rootEvent.getId()); + + const { container } = await getComponent(); + const tiles = container.getElementsByClassName("mx_EventTile"); + expect(tiles.length).toEqual(1); + }); + it("sends a message with the correct fallback", async () => { const { container } = await getComponent(); diff --git a/test/test-utils/threads.ts b/test/test-utils/threads.ts index 8c7c658cfc..0bc6ea8b0a 100644 --- a/test/test-utils/threads.ts +++ b/test/test-utils/threads.ts @@ -136,10 +136,6 @@ export const mkThread = ({ const thread = room.createThread(rootEvent.getId()!, rootEvent, events, true); - events.forEach((event) => { - thread.timeline.push(event); - }); - // So that we do not have to mock the thread loading thread.initialEventsFetched = true;