Render captions to m.file, m.image, m.video, and m.audio (#2059)
Some checks failed
Deploy to Netlify (dev) / Deploy to Netlify (push) Has been cancelled

* Add rendering image captions

* Handle sending captions for images

* Fix caption rendering on m.video and m.audio too

* Remove unused renderBody() parameter

* Fix m.file rendering body instead of filename where possible

* Add caption rendering for generic files

+ Fix video and audio not properly sending captions

* Use m.text for captions & render on demand

* Allow custom HTML in sending captions

* Don't *send* captions

* mvoe content const into renderCaption()

---------

Co-authored-by: Ajay Bura <32841439+ajbura@users.noreply.github.com>
This commit is contained in:
nexy7574 2025-01-06 01:44:22 +00:00 committed by GitHub
parent 3c5afaf33a
commit 02437a6a20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 118 additions and 73 deletions

View file

@ -29,6 +29,7 @@ import { ImageViewer } from './image-viewer';
import { PdfViewer } from './Pdf-viewer'; import { PdfViewer } from './Pdf-viewer';
import { TextViewer } from './text-viewer'; import { TextViewer } from './text-viewer';
import { testMatrixTo } from '../plugins/matrix-to'; import { testMatrixTo } from '../plugins/matrix-to';
import {IImageContent} from "../../types/matrix/common";
type RenderMessageContentProps = { type RenderMessageContentProps = {
displayName: string; displayName: string;
@ -67,8 +68,30 @@ export function RenderMessageContent({
</UrlPreviewHolder> </UrlPreviewHolder>
); );
}; };
const renderCaption = () => {
const content: IImageContent = getContent();
if(content.filename && content.filename !== content.body) {
return (
<MText
edited={edited}
content={content}
renderBody={(props) => (
<RenderBody
{...props}
highlightRegex={highlightRegex}
htmlReactParserOptions={htmlReactParserOptions}
linkifyOpts={linkifyOpts}
/>
)}
renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined}
/>
)
}
return null;
}
const renderFile = () => ( const renderFile = () => (
<>
<MFile <MFile
content={getContent()} content={getContent()}
renderFileContent={({ body, mimeType, info, encInfo, url }) => ( renderFileContent={({ body, mimeType, info, encInfo, url }) => (
@ -96,9 +119,12 @@ export function RenderMessageContent({
> >
<DownloadFile body={body} mimeType={mimeType} url={url} encInfo={encInfo} info={info} /> <DownloadFile body={body} mimeType={mimeType} url={url} encInfo={encInfo} info={info} />
</FileContent> </FileContent>
)} )}
outlined={outlineAttachment} outlined={outlineAttachment}
/> />
{renderCaption()}
</>
); );
if (msgType === MsgType.Text) { if (msgType === MsgType.Text) {
@ -158,6 +184,7 @@ export function RenderMessageContent({
if (msgType === MsgType.Image) { if (msgType === MsgType.Image) {
return ( return (
<>
<MImage <MImage
content={getContent()} content={getContent()}
renderImageContent={(props) => ( renderImageContent={(props) => (
@ -170,11 +197,14 @@ export function RenderMessageContent({
)} )}
outlined={outlineAttachment} outlined={outlineAttachment}
/> />
{renderCaption()}
</>
); );
} }
if (msgType === MsgType.Video) { if (msgType === MsgType.Video) {
return ( return (
<>
<MVideo <MVideo
content={getContent()} content={getContent()}
renderAsFile={renderFile} renderAsFile={renderFile}
@ -202,11 +232,15 @@ export function RenderMessageContent({
)} )}
outlined={outlineAttachment} outlined={outlineAttachment}
/> />
{renderCaption()}
</>
); );
} }
if (msgType === MsgType.Audio) { if (msgType === MsgType.Audio) {
return ( return (
<>
<MAudio <MAudio
content={getContent()} content={getContent()}
renderAsFile={renderFile} renderAsFile={renderFile}
@ -215,6 +249,9 @@ export function RenderMessageContent({
)} )}
outlined={outlineAttachment} outlined={outlineAttachment}
/> />
{renderCaption()}
</>
); );
} }

View file

@ -172,6 +172,7 @@ export function MNotice({ edited, content, renderBody, renderUrlsPreview }: MNot
type RenderImageContentProps = { type RenderImageContentProps = {
body: string; body: string;
filename?: string;
info?: IImageInfo & IThumbnailContent; info?: IImageInfo & IThumbnailContent;
mimeType?: string; mimeType?: string;
url: string; url: string;
@ -282,7 +283,7 @@ export function MAudio({ content, renderAsFile, renderAudioContent, outlined }:
return ( return (
<Attachment outlined={outlined}> <Attachment outlined={outlined}>
<AttachmentHeader> <AttachmentHeader>
<FileHeader body={content.body ?? 'Audio'} mimeType={safeMimeType} /> <FileHeader body={content.filename ?? content.body ?? 'Audio'} mimeType={safeMimeType} />
</AttachmentHeader> </AttachmentHeader>
<AttachmentBox> <AttachmentBox>
<AttachmentContent> <AttachmentContent>
@ -322,14 +323,14 @@ export function MFile({ content, renderFileContent, outlined }: MFileProps) {
<Attachment outlined={outlined}> <Attachment outlined={outlined}>
<AttachmentHeader> <AttachmentHeader>
<FileHeader <FileHeader
body={content.body ?? 'Unnamed File'} body={content.filename ?? content.body ?? 'Unnamed File'}
mimeType={fileInfo?.mimetype ?? FALLBACK_MIMETYPE} mimeType={fileInfo?.mimetype ?? FALLBACK_MIMETYPE}
/> />
</AttachmentHeader> </AttachmentHeader>
<AttachmentBox> <AttachmentBox>
<AttachmentContent> <AttachmentContent>
{renderFileContent({ {renderFileContent({
body: content.body ?? 'File', body: content.filename ?? content.body ?? 'File',
info: fileInfo ?? {}, info: fileInfo ?? {},
mimeType: fileInfo?.mimetype ?? FALLBACK_MIMETYPE, mimeType: fileInfo?.mimetype ?? FALLBACK_MIMETYPE,
url: mxcUrl, url: mxcUrl,

View file

@ -42,7 +42,7 @@ const generateThumbnailContent = async (
export const getImageMsgContent = async ( export const getImageMsgContent = async (
mx: MatrixClient, mx: MatrixClient,
item: TUploadItem, item: TUploadItem,
mxc: string mxc: string,
): Promise<IContent> => { ): Promise<IContent> => {
const { file, originalFile, encInfo } = item; const { file, originalFile, encInfo } = item;
const [imgError, imgEl] = await to(loadImageElement(getImageFileUrl(originalFile))); const [imgError, imgEl] = await to(loadImageElement(getImageFileUrl(originalFile)));
@ -50,6 +50,7 @@ export const getImageMsgContent = async (
const content: IContent = { const content: IContent = {
msgtype: MsgType.Image, msgtype: MsgType.Image,
filename: file.name,
body: file.name, body: file.name,
}; };
if (imgEl) { if (imgEl) {
@ -74,7 +75,7 @@ export const getImageMsgContent = async (
export const getVideoMsgContent = async ( export const getVideoMsgContent = async (
mx: MatrixClient, mx: MatrixClient,
item: TUploadItem, item: TUploadItem,
mxc: string mxc: string,
): Promise<IContent> => { ): Promise<IContent> => {
const { file, originalFile, encInfo } = item; const { file, originalFile, encInfo } = item;
@ -83,6 +84,7 @@ export const getVideoMsgContent = async (
const content: IContent = { const content: IContent = {
msgtype: MsgType.Video, msgtype: MsgType.Video,
filename: file.name,
body: file.name, body: file.name,
}; };
if (videoEl) { if (videoEl) {
@ -122,6 +124,7 @@ export const getAudioMsgContent = (item: TUploadItem, mxc: string): IContent =>
const { file, encInfo } = item; const { file, encInfo } = item;
const content: IContent = { const content: IContent = {
msgtype: MsgType.Audio, msgtype: MsgType.Audio,
filename: file.name,
body: file.name, body: file.name,
info: { info: {
mimetype: file.type, mimetype: file.type,

View file

@ -43,6 +43,7 @@ export type IThumbnailContent = {
export type IImageContent = { export type IImageContent = {
msgtype: MsgType.Image; msgtype: MsgType.Image;
body?: string; body?: string;
filename?: string;
url?: string; url?: string;
info?: IImageInfo & IThumbnailContent; info?: IImageInfo & IThumbnailContent;
file?: IEncryptedFile; file?: IEncryptedFile;
@ -51,6 +52,7 @@ export type IImageContent = {
export type IVideoContent = { export type IVideoContent = {
msgtype: MsgType.Video; msgtype: MsgType.Video;
body?: string; body?: string;
filename?: string;
url?: string; url?: string;
info?: IVideoInfo & IThumbnailContent; info?: IVideoInfo & IThumbnailContent;
file?: IEncryptedFile; file?: IEncryptedFile;
@ -59,6 +61,7 @@ export type IVideoContent = {
export type IAudioContent = { export type IAudioContent = {
msgtype: MsgType.Audio; msgtype: MsgType.Audio;
body?: string; body?: string;
filename?: string;
url?: string; url?: string;
info?: IAudioInfo; info?: IAudioInfo;
file?: IEncryptedFile; file?: IEncryptedFile;
@ -67,6 +70,7 @@ export type IAudioContent = {
export type IFileContent = { export type IFileContent = {
msgtype: MsgType.File; msgtype: MsgType.File;
body?: string; body?: string;
filename?: string;
url?: string; url?: string;
info?: IFileInfo & IThumbnailContent; info?: IFileInfo & IThumbnailContent;
file?: IEncryptedFile; file?: IEncryptedFile;