From f9fdcd1fa38c883a07ef1b519217a1927f40547d Mon Sep 17 00:00:00 2001 From: Tulir Asokan <tulir@maunium.net> Date: Tue, 12 Jul 2022 15:34:57 +0300 Subject: Add support for rendering captions in media messages --- src/components/views/messages/IBodyProps.ts | 2 ++ src/components/views/messages/MessageEvent.tsx | 15 +++++++++++++++ src/components/views/messages/TextualBody.tsx | 11 +++++++++++ src/utils/FileUtils.ts | 4 +++- 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/components/views/messages/IBodyProps.ts b/src/components/views/messages/IBodyProps.ts index fcc204dae3..95cb2eb37d 100644 --- a/src/components/views/messages/IBodyProps.ts +++ b/src/components/views/messages/IBodyProps.ts @@ -59,4 +59,6 @@ export interface IBodyProps { // Set to `true` to disable interactions (e.g. video controls) and to remove controls from the tab order. // This may be useful when displaying a preview of the event. inhibitInteraction?: boolean; + + OrigBodyType?: React.ComponentType<Partial<IBodyProps>>; } diff --git a/src/components/views/messages/MessageEvent.tsx b/src/components/views/messages/MessageEvent.tsx index db0016de7b..dd7a864300 100644 --- a/src/components/views/messages/MessageEvent.tsx +++ b/src/components/views/messages/MessageEvent.tsx @@ -183,6 +183,15 @@ export default class MessageEvent extends React.Component<IProps> implements IMe } } + // @ts-ignore + const hasCaption = [MsgType.Image, MsgType.File, MsgType.Audio, MsgType.Video].includes(msgtype) + && content.filename && content.filename !== content.body; + let OrigBodyType; + if (hasCaption) { + OrigBodyType = BodyType + BodyType = CaptionBody + } + if (SettingsStore.getValue("feature_mjolnir")) { const key = `mx_mjolnir_render_${this.props.mxEvent.getRoomId()}__${this.props.mxEvent.getId()}`; const allowRender = localStorage.getItem(key) === "true"; @@ -216,7 +225,13 @@ export default class MessageEvent extends React.Component<IProps> implements IMe getRelationsForEvent={this.props.getRelationsForEvent} isSeeingThroughMessageHiddenForModeration={this.props.isSeeingThroughMessageHiddenForModeration} inhibitInteraction={this.props.inhibitInteraction} + OrigBodyType={OrigBodyType} /> ) : null; } } + +const CaptionBody: React.FunctionComponent<IBodyProps & {OrigBodyType: React.ComponentType<Partial<IBodyProps>>}> = ({OrigBodyType, ...props}) => (<div className="mx_EventTile_content"> + <OrigBodyType {...props}/> + <TextualBody {...{...props, ref: undefined}}/> +</div>) diff --git a/src/components/views/messages/TextualBody.tsx b/src/components/views/messages/TextualBody.tsx index f0b5c70f17..e28ed3fad7 100644 --- a/src/components/views/messages/TextualBody.tsx +++ b/src/components/views/messages/TextualBody.tsx @@ -575,11 +575,14 @@ export default class TextualBody extends React.Component<IBodyProps, IState> { const content = mxEvent.getContent(); let isNotice = false; let isEmote = false; + let isCaption = false; // only strip reply if this is the original replying event, edits thereafter do not have the fallback const stripReply = !mxEvent.replacingEvent() && !!getParentEventId(mxEvent); isEmote = content.msgtype === MsgType.Emote; isNotice = content.msgtype === MsgType.Notice; + // @ts-ignore + isCaption = [MsgType.Image, MsgType.File, MsgType.Audio, MsgType.Video].includes(content.msgtype); let body = HtmlUtils.bodyToHtml(content, this.props.highlights, { disableBigEmoji: isEmote || !SettingsStore.getValue<boolean>("TextualBody.enableBigEmoji"), // Part of Replies fallback support @@ -651,6 +654,14 @@ export default class TextualBody extends React.Component<IBodyProps, IState> { </div> ); } + if (isCaption) { + return ( + <div className="mx_MTextBody mx_EventTile_caption" onClick={this.onBodyLinkClick}> + { body } + { widgets } + </div> + ); + } return ( <div className="mx_MTextBody mx_EventTile_content" onClick={this.onBodyLinkClick}> {body} diff --git a/src/utils/FileUtils.ts b/src/utils/FileUtils.ts index 75511756f5..a8e3b9cceb 100644 --- a/src/utils/FileUtils.ts +++ b/src/utils/FileUtils.ts @@ -46,7 +46,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; -- 2.45.0