From b6c9ebbbdc95562b4c1897958cf084470bdfcbc6 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 13 Sep 2024 14:43:14 +0300 Subject: [PATCH 1/6] Add support for rendering media captions --- .../views/messages/MessageEvent.tsx | 36 +++++++++++++++++++ src/components/views/messages/TextualBody.tsx | 9 +++++ src/utils/FileUtils.ts | 4 ++- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/components/views/messages/MessageEvent.tsx b/src/components/views/messages/MessageEvent.tsx index 1a5d09e415..e9abf8c812 100644 --- a/src/components/views/messages/MessageEvent.tsx +++ b/src/components/views/messages/MessageEvent.tsx @@ -190,6 +190,32 @@ export default class MessageEvent extends React.Component implements IMe } } + const hasCaption = + [MsgType.Image, MsgType.File, MsgType.Audio, MsgType.Video].includes(msgtype as MsgType) && + content.filename && + content.filename !== content.body; + if (hasCaption) { + return ; + } + return BodyType ? ( implements IMe ) : null; } } + +const CaptionBody: React.FunctionComponent }> = ({ + WrappedBodyType, + ...props +}) => ( +
+ + +
+); diff --git a/src/components/views/messages/TextualBody.tsx b/src/components/views/messages/TextualBody.tsx index 8b7bfb9a5a..75e2255f28 100644 --- a/src/components/views/messages/TextualBody.tsx +++ b/src/components/views/messages/TextualBody.tsx @@ -556,6 +556,7 @@ export default class TextualBody extends React.Component { const content = mxEvent.getContent(); const isNotice = content.msgtype === MsgType.Notice; const isEmote = content.msgtype === MsgType.Emote; + const isCaption = [MsgType.Image, MsgType.File, MsgType.Audio, MsgType.Video].includes(content.msgtype as MsgType); const willHaveWrapper = this.props.replacingEventId || this.props.isSeeingThroughMessageHiddenForModeration || isEmote; @@ -635,6 +636,14 @@ export default class TextualBody extends React.Component { ); } + if (isCaption) { + return ( +
+ {body} + {widgets} +
+ ); + } return (
{body} diff --git a/src/utils/FileUtils.ts b/src/utils/FileUtils.ts index 194cb31d20..37fd181900 100644 --- a/src/utils/FileUtils.ts +++ b/src/utils/FileUtils.ts @@ -38,7 +38,9 @@ export function presentableTextForFile( shortened = false, ): string { let text = fallbackText; - if (content.body?.length) { + if (content.filename?.length) { + text = content.filename; + } else if (content.body?.length) { // The content body should be the name of the file including a // file extension. text = content.body; From 145477c05bdde7463f59a39e752dcad3e05829b0 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 2 Oct 2024 13:31:05 +0300 Subject: [PATCH 2/6] Run prettier --- .../views/messages/MessageEvent.tsx | 40 ++++++++++--------- src/components/views/messages/TextualBody.tsx | 4 +- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/components/views/messages/MessageEvent.tsx b/src/components/views/messages/MessageEvent.tsx index e9abf8c812..87c2f8387e 100644 --- a/src/components/views/messages/MessageEvent.tsx +++ b/src/components/views/messages/MessageEvent.tsx @@ -195,25 +195,27 @@ export default class MessageEvent extends React.Component implements IMe content.filename && content.filename !== content.body; if (hasCaption) { - return ; + return ( + + ); } return BodyType ? ( diff --git a/src/components/views/messages/TextualBody.tsx b/src/components/views/messages/TextualBody.tsx index 75e2255f28..890316fc02 100644 --- a/src/components/views/messages/TextualBody.tsx +++ b/src/components/views/messages/TextualBody.tsx @@ -556,7 +556,9 @@ export default class TextualBody extends React.Component { const content = mxEvent.getContent(); const isNotice = content.msgtype === MsgType.Notice; const isEmote = content.msgtype === MsgType.Emote; - const isCaption = [MsgType.Image, MsgType.File, MsgType.Audio, MsgType.Video].includes(content.msgtype as MsgType); + const isCaption = [MsgType.Image, MsgType.File, MsgType.Audio, MsgType.Video].includes( + content.msgtype as MsgType, + ); const willHaveWrapper = this.props.replacingEventId || this.props.isSeeingThroughMessageHiddenForModeration || isEmote; From abcbf430a777a5f8c50d25b5788f9f6f7cfb1e3c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 2 Oct 2024 14:15:18 +0300 Subject: [PATCH 3/6] Deduplicate body props --- .../views/messages/MessageEvent.tsx | 61 ++++++------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/src/components/views/messages/MessageEvent.tsx b/src/components/views/messages/MessageEvent.tsx index 87c2f8387e..e4f3cb83ec 100644 --- a/src/components/views/messages/MessageEvent.tsx +++ b/src/components/views/messages/MessageEvent.tsx @@ -194,50 +194,29 @@ export default class MessageEvent extends React.Component implements IMe [MsgType.Image, MsgType.File, MsgType.Audio, MsgType.Video].includes(msgtype as MsgType) && content.filename && content.filename !== content.body; + const bodyProps: IBodyProps = { + ref: this.body, + mxEvent: this.props.mxEvent, + highlights: this.props.highlights, + highlightLink: this.props.highlightLink, + showUrlPreview: this.props.showUrlPreview, + forExport: this.props.forExport, + maxImageHeight: this.props.maxImageHeight, + replacingEventId: this.props.replacingEventId, + editState: this.props.editState, + onHeightChanged: this.props.onHeightChanged, + onMessageAllowed: this.onTileUpdate, + permalinkCreator: this.props.permalinkCreator, + mediaEventHelper: this.mediaHelper, + getRelationsForEvent: this.props.getRelationsForEvent, + isSeeingThroughMessageHiddenForModeration: this.props.isSeeingThroughMessageHiddenForModeration, + inhibitInteraction: this.props.inhibitInteraction, + }; if (hasCaption) { - return ( - - ); + return ; } - return BodyType ? ( - - ) : null; + return BodyType ? : null; } } From 7478bdd425c692b228053643f2a0e8bc4013c133 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 2 Oct 2024 14:24:55 +0300 Subject: [PATCH 4/6] Add basic test --- .../views/messages/MessageEvent-test.tsx | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/test/components/views/messages/MessageEvent-test.tsx b/test/components/views/messages/MessageEvent-test.tsx index 16ed44f9a3..2222e93bbc 100644 --- a/test/components/views/messages/MessageEvent-test.tsx +++ b/test/components/views/messages/MessageEvent-test.tsx @@ -8,13 +8,16 @@ Please see LICENSE files in the repository root for full details. import React from "react"; import { render, RenderResult } from "@testing-library/react"; -import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; +import { MatrixClient, MatrixEvent, EventType, Room, MsgType } from "matrix-js-sdk/src/matrix"; import SettingsStore from "../../../../src/settings/SettingsStore"; import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "../../../../src/voice-broadcast"; import { mkEvent, mkRoom, stubClient } from "../../../test-utils"; import MessageEvent from "../../../../src/components/views/messages/MessageEvent"; import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks"; +import fetchMock from "fetch-mock-jest"; +import fs from "fs"; +import path from "path"; jest.mock("../../../../src/components/views/messages/UnknownBody", () => ({ __esModule: true, @@ -25,6 +28,21 @@ jest.mock("../../../../src/voice-broadcast/components/VoiceBroadcastBody", () => VoiceBroadcastBody: () =>
, })); +jest.mock("../../../../src/components/views/messages/MImageBody", () => ({ + __esModule: true, + default: () =>
, +})); + +jest.mock("../../../../src/components/views/messages/MStickerBody", () => ({ + __esModule: true, + default: () =>
, +})); + +jest.mock("../../../../src/components/views/messages/TextualBody.tsx", () => ({ + __esModule: true, + default: () =>
, +})); + describe("MessageEvent", () => { let room: Room; let client: MatrixClient; @@ -68,4 +86,42 @@ describe("MessageEvent", () => { result.getByTestId("voice-broadcast-body"); }); }); + + describe("when an image with a caption is sent", () => { + let result: RenderResult; + + beforeEach(() => { + event = mkEvent({ + event: true, + type: EventType.RoomMessage, + user: client.getUserId()!, + room: room.roomId, + content: { + body: "caption for a test image", + format: "org.matrix.custom.html", + formatted_body: "caption for a test image", + msgtype: MsgType.Image, + filename: "image.webp", + info: { + w: 40, + h: 50, + }, + url: "mxc://server/image", + }, + }); + result = renderMessageEvent(); + }); + + it("should render a TextualBody and an ImageBody", () => { + fetchMock.getOnce( + "https://server/_matrix/media/v3/download/server/image", + { + body: fs.readFileSync(path.resolve(__dirname, "..", "..", "..", "images", "animated-logo.webp")), + }, + { sendAsJson: false }, + ); + result.getByTestId("image-body"); + result.getByTestId("textual-body"); + }); + }); }); From 78fc87c7631f6bc8aa6424ab8fc5f7adcde1f80b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 2 Oct 2024 14:30:36 +0300 Subject: [PATCH 5/6] Fix import order in test --- test/components/views/messages/MessageEvent-test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/components/views/messages/MessageEvent-test.tsx b/test/components/views/messages/MessageEvent-test.tsx index 2222e93bbc..490c8cb2a2 100644 --- a/test/components/views/messages/MessageEvent-test.tsx +++ b/test/components/views/messages/MessageEvent-test.tsx @@ -9,15 +9,15 @@ Please see LICENSE files in the repository root for full details. import React from "react"; import { render, RenderResult } from "@testing-library/react"; import { MatrixClient, MatrixEvent, EventType, Room, MsgType } from "matrix-js-sdk/src/matrix"; +import fetchMock from "fetch-mock-jest"; +import fs from "fs"; +import path from "path"; import SettingsStore from "../../../../src/settings/SettingsStore"; import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "../../../../src/voice-broadcast"; import { mkEvent, mkRoom, stubClient } from "../../../test-utils"; import MessageEvent from "../../../../src/components/views/messages/MessageEvent"; import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks"; -import fetchMock from "fetch-mock-jest"; -import fs from "fs"; -import path from "path"; jest.mock("../../../../src/components/views/messages/UnknownBody", () => ({ __esModule: true, From 99572140d8a34b1a0d27a2c08916abece62119ce Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 4 Oct 2024 15:39:02 +0300 Subject: [PATCH 6/6] Fix test? --- test/components/views/messages/MessageEvent-test.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/components/views/messages/MessageEvent-test.tsx b/test/components/views/messages/MessageEvent-test.tsx index 490c8cb2a2..95481a0b12 100644 --- a/test/components/views/messages/MessageEvent-test.tsx +++ b/test/components/views/messages/MessageEvent-test.tsx @@ -33,6 +33,11 @@ jest.mock("../../../../src/components/views/messages/MImageBody", () => ({ default: () =>
, })); +jest.mock("../../../../src/components/views/messages/MImageReplyBody", () => ({ + __esModule: true, + default: () =>
, +})); + jest.mock("../../../../src/components/views/messages/MStickerBody", () => ({ __esModule: true, default: () =>
,