Render captions to m.file, m.image, m.video, and m.audio (#2059)
authornexy7574 <me@nexy7574.co.uk>
Mon, 6 Jan 2025 01:44:22 +0000 (01:44 +0000)
committerGitHub <noreply@github.com>
Mon, 6 Jan 2025 01:44:22 +0000 (12:44 +1100)
* 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>
src/app/components/RenderMessageContent.tsx
src/app/components/message/MsgTypeRenderers.tsx
src/app/features/room/msgContent.ts
src/types/matrix/common.ts

index 1ce37e5ca3432e23327746d5e55b7ef1b3bfdcff..55b0ffc2dda8d4258f58a3ba180ad3f21b1cbd3c 100644 (file)
@@ -29,6 +29,7 @@ import { ImageViewer } from './image-viewer';
 import { PdfViewer } from './Pdf-viewer';
 import { TextViewer } from './text-viewer';
 import { testMatrixTo } from '../plugins/matrix-to';
+import {IImageContent} from "../../types/matrix/common";
 
 type RenderMessageContentProps = {
   displayName: string;
@@ -67,38 +68,63 @@ export function RenderMessageContent({
       </UrlPreviewHolder>
     );
   };
-
-  const renderFile = () => (
-    <MFile
-      content={getContent()}
-      renderFileContent={({ body, mimeType, info, encInfo, url }) => (
-        <FileContent
-          body={body}
-          mimeType={mimeType}
-          renderAsPdfFile={() => (
-            <ReadPdfFile
-              body={body}
-              mimeType={mimeType}
-              url={url}
-              encInfo={encInfo}
-              renderViewer={(p) => <PdfViewer {...p} />}
+  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}
             />
           )}
-          renderAsTextFile={() => (
-            <ReadTextFile
+          renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined}
+        />
+      )
+    }
+    return null;
+  }
+
+  const renderFile = () => (
+    <>
+      <MFile
+        content={getContent()}
+        renderFileContent={({ body, mimeType, info, encInfo, url }) => (
+            <FileContent
               body={body}
               mimeType={mimeType}
-              url={url}
-              encInfo={encInfo}
-              renderViewer={(p) => <TextViewer {...p} />}
-            />
-          )}
-        >
-          <DownloadFile body={body} mimeType={mimeType} url={url} encInfo={encInfo} info={info} />
-        </FileContent>
-      )}
-      outlined={outlineAttachment}
-    />
+              renderAsPdfFile={() => (
+                <ReadPdfFile
+                  body={body}
+                  mimeType={mimeType}
+                  url={url}
+                  encInfo={encInfo}
+                  renderViewer={(p) => <PdfViewer {...p} />}
+                />
+              )}
+              renderAsTextFile={() => (
+                <ReadTextFile
+                  body={body}
+                  mimeType={mimeType}
+                  url={url}
+                  encInfo={encInfo}
+                  renderViewer={(p) => <TextViewer {...p} />}
+                />
+              )}
+            >
+              <DownloadFile body={body} mimeType={mimeType} url={url} encInfo={encInfo} info={info} />
+            </FileContent>
+
+        )}
+        outlined={outlineAttachment}
+      />
+      {renderCaption()}
+    </>
   );
 
   if (msgType === MsgType.Text) {
@@ -158,36 +184,40 @@ export function RenderMessageContent({
 
   if (msgType === MsgType.Image) {
     return (
-      <MImage
-        content={getContent()}
-        renderImageContent={(props) => (
-          <ImageContent
-            {...props}
-            autoPlay={mediaAutoLoad}
-            renderImage={(p) => <Image {...p} loading="lazy" />}
-            renderViewer={(p) => <ImageViewer {...p} />}
-          />
-        )}
-        outlined={outlineAttachment}
-      />
+      <>
+        <MImage
+          content={getContent()}
+          renderImageContent={(props) => (
+              <ImageContent
+                  {...props}
+                  autoPlay={mediaAutoLoad}
+                  renderImage={(p) => <Image {...p} loading="lazy" />}
+                  renderViewer={(p) => <ImageViewer {...p} />}
+              />
+          )}
+          outlined={outlineAttachment}
+        />
+        {renderCaption()}
+      </>
     );
   }
 
   if (msgType === MsgType.Video) {
     return (
-      <MVideo
-        content={getContent()}
-        renderAsFile={renderFile}
-        renderVideoContent={({ body, info, mimeType, url, encInfo }) => (
-          <VideoContent
-            body={body}
-            info={info}
-            mimeType={mimeType}
-            url={url}
-            encInfo={encInfo}
-            renderThumbnail={
-              mediaAutoLoad
-                ? () => (
+      <>
+        <MVideo
+          content={getContent()}
+          renderAsFile={renderFile}
+          renderVideoContent={({ body, info, mimeType, url, encInfo }) => (
+            <VideoContent
+              body={body}
+              info={info}
+              mimeType={mimeType}
+              url={url}
+              encInfo={encInfo}
+              renderThumbnail={
+                mediaAutoLoad
+                  ? () => (
                     <ThumbnailContent
                       info={info}
                       renderImage={(src) => (
@@ -195,26 +225,33 @@ export function RenderMessageContent({
                       )}
                     />
                   )
-                : undefined
-            }
-            renderVideo={(p) => <Video {...p} />}
-          />
-        )}
-        outlined={outlineAttachment}
-      />
+                  : undefined
+              }
+              renderVideo={(p) => <Video {...p} />}
+            />
+          )}
+          outlined={outlineAttachment}
+        />
+        {renderCaption()}
+      </>
+
     );
   }
 
   if (msgType === MsgType.Audio) {
     return (
-      <MAudio
-        content={getContent()}
-        renderAsFile={renderFile}
-        renderAudioContent={(props) => (
-          <AudioContent {...props} renderMediaControl={(p) => <MediaControl {...p} />} />
-        )}
-        outlined={outlineAttachment}
-      />
+      <>
+        <MAudio
+          content={getContent()}
+          renderAsFile={renderFile}
+          renderAudioContent={(props) => (
+            <AudioContent {...props} renderMediaControl={(p) => <MediaControl {...p} />} />
+          )}
+          outlined={outlineAttachment}
+        />
+        {renderCaption()}
+      </>
+
     );
   }
 
index f7cbc4811a536bdf418d3b71d00bfde8c7637976..6138d0d735148b4582cc02cdacea1be178aae1c4 100644 (file)
@@ -172,6 +172,7 @@ export function MNotice({ edited, content, renderBody, renderUrlsPreview }: MNot
 
 type RenderImageContentProps = {
   body: string;
+  filename?: string;
   info?: IImageInfo & IThumbnailContent;
   mimeType?: string;
   url: string;
@@ -282,7 +283,7 @@ export function MAudio({ content, renderAsFile, renderAudioContent, outlined }:
   return (
     <Attachment outlined={outlined}>
       <AttachmentHeader>
-        <FileHeader body={content.body ?? 'Audio'} mimeType={safeMimeType} />
+        <FileHeader body={content.filename ?? content.body ?? 'Audio'} mimeType={safeMimeType} />
       </AttachmentHeader>
       <AttachmentBox>
         <AttachmentContent>
@@ -322,14 +323,14 @@ export function MFile({ content, renderFileContent, outlined }: MFileProps) {
     <Attachment outlined={outlined}>
       <AttachmentHeader>
         <FileHeader
-          body={content.body ?? 'Unnamed File'}
+          body={content.filename ?? content.body ?? 'Unnamed File'}
           mimeType={fileInfo?.mimetype ?? FALLBACK_MIMETYPE}
         />
       </AttachmentHeader>
       <AttachmentBox>
         <AttachmentContent>
           {renderFileContent({
-            body: content.body ?? 'File',
+            body: content.filename ?? content.body ?? 'File',
             info: fileInfo ?? {},
             mimeType: fileInfo?.mimetype ?? FALLBACK_MIMETYPE,
             url: mxcUrl,
index 103e8dcdf929bb84add9a87c58f046f54d69004a..60781ef0abaea72fc379830cbb54213e5b1df3b7 100644 (file)
@@ -42,7 +42,7 @@ const generateThumbnailContent = async (
 export const getImageMsgContent = async (
   mx: MatrixClient,
   item: TUploadItem,
-  mxc: string
+  mxc: string,
 ): Promise<IContent> => {
   const { file, originalFile, encInfo } = item;
   const [imgError, imgEl] = await to(loadImageElement(getImageFileUrl(originalFile)));
@@ -50,6 +50,7 @@ export const getImageMsgContent = async (
 
   const content: IContent = {
     msgtype: MsgType.Image,
+    filename: file.name,
     body: file.name,
   };
   if (imgEl) {
@@ -74,7 +75,7 @@ export const getImageMsgContent = async (
 export const getVideoMsgContent = async (
   mx: MatrixClient,
   item: TUploadItem,
-  mxc: string
+  mxc: string,
 ): Promise<IContent> => {
   const { file, originalFile, encInfo } = item;
 
@@ -83,6 +84,7 @@ export const getVideoMsgContent = async (
 
   const content: IContent = {
     msgtype: MsgType.Video,
+    filename: file.name,
     body: file.name,
   };
   if (videoEl) {
@@ -122,6 +124,7 @@ export const getAudioMsgContent = (item: TUploadItem, mxc: string): IContent =>
   const { file, encInfo } = item;
   const content: IContent = {
     msgtype: MsgType.Audio,
+    filename: file.name,
     body: file.name,
     info: {
       mimetype: file.type,
index cc20d453c516f2e62fd2913c87e3ce0e617a96d8..f2f12a6acdca7fe64d772102034e67edfe13c4cd 100644 (file)
@@ -43,6 +43,7 @@ export type IThumbnailContent = {
 export type IImageContent = {
   msgtype: MsgType.Image;
   body?: string;
+  filename?: string;
   url?: string;
   info?: IImageInfo & IThumbnailContent;
   file?: IEncryptedFile;
@@ -51,6 +52,7 @@ export type IImageContent = {
 export type IVideoContent = {
   msgtype: MsgType.Video;
   body?: string;
+  filename?: string;
   url?: string;
   info?: IVideoInfo & IThumbnailContent;
   file?: IEncryptedFile;
@@ -59,6 +61,7 @@ export type IVideoContent = {
 export type IAudioContent = {
   msgtype: MsgType.Audio;
   body?: string;
+  filename?: string;
   url?: string;
   info?: IAudioInfo;
   file?: IEncryptedFile;
@@ -67,6 +70,7 @@ export type IAudioContent = {
 export type IFileContent = {
   msgtype: MsgType.File;
   body?: string;
+  filename?: string;
   url?: string;
   info?: IFileInfo & IThumbnailContent;
   file?: IEncryptedFile;