import React from 'react'; import { MsgType } from 'matrix-js-sdk'; import { HTMLReactParserOptions } from 'html-react-parser'; import { Opts } from 'linkifyjs'; import { config } from 'folds'; import { AudioContent, DownloadFile, FileContent, ImageContent, MAudio, MBadEncrypted, MEmote, MFile, MImage, MLocation, MNotice, MText, MVideo, ReadPdfFile, ReadTextFile, RenderBody, ThumbnailContent, UnsupportedContent, VerificationRequestContent, VideoContent, } from './message'; import { UrlPreviewCard, UrlPreviewHolder } from './url-preview'; import { Image, MediaControl, Video } from './media'; import { ImageViewer } from './image-viewer'; import { PdfViewer } from './Pdf-viewer'; import { TextViewer } from './text-viewer'; import { testMatrixTo } from '../plugins/matrix-to'; import { IAudioContent, IFileContent, IImageContent } from '../../types/matrix/common'; // Audio is frequently sent as m.file (bridges/other clients, or when the browser // reported a non-audio/* mime on upload). Detect that so we can play it inline // like m.audio instead of showing only a download button. const AUDIO_EXT_MIME: Record = { mp3: 'audio/mpeg', m4a: 'audio/mp4', aac: 'audio/aac', oga: 'audio/ogg', ogg: 'audio/ogg', opus: 'audio/ogg', wav: 'audio/wav', flac: 'audio/flac', weba: 'audio/webm', }; const resolveInlineAudioMime = (content: IFileContent): string | undefined => { const mime = content.info?.mimetype; if (typeof mime === 'string' && mime.startsWith('audio')) return mime; const name = content.filename ?? content.body ?? ''; const ext = name.split('.').pop()?.toLowerCase(); return ext ? AUDIO_EXT_MIME[ext] : undefined; }; type RenderMessageContentProps = { displayName: string; msgType: string; ts: number; edited?: boolean; onEditHistoryClick?: () => void; getContent: () => T; mediaAutoLoad?: boolean; urlPreview?: boolean; highlightRegex?: RegExp; htmlReactParserOptions: HTMLReactParserOptions; linkifyOpts: Opts; outlineAttachment?: boolean; eventId?: string; }; export function RenderMessageContent({ displayName, msgType, ts, edited, onEditHistoryClick, getContent, mediaAutoLoad, urlPreview, highlightRegex, htmlReactParserOptions, linkifyOpts, outlineAttachment, eventId, }: RenderMessageContentProps) { const renderUrlsPreview = (urls: string[]) => { // Cap previews per message so a link-dump doesn't spawn dozens of preview // fetches + iframes at once. const filteredUrls = urls.filter((url) => !testMatrixTo(url)).slice(0, 6); if (filteredUrls.length === 0) return undefined; return ( {filteredUrls.map((url) => ( ))} ); }; const renderCaption = () => { const content: IImageContent = getContent(); if (content.filename && content.filename !== content.body) { return ( ( )} renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined} /> ); } return null; }; const renderFile = () => ( <> ( ( } /> )} renderAsTextFile={() => ( } /> )} > )} outlined={outlineAttachment} /> {renderCaption()} ); if (msgType === MsgType.Text) { return ( ( )} renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined} eventId={eventId} /> ); } if (msgType === MsgType.Emote) { return ( ( )} renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined} eventId={eventId} /> ); } if (msgType === MsgType.Notice) { return ( ( )} renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined} eventId={eventId} /> ); } if (msgType === MsgType.Image) { return ( <> ( } renderViewer={(p) => } /> )} outlined={outlineAttachment} /> {renderCaption()} ); } if (msgType === MsgType.Video) { return ( <> ( ( ( {body} )} /> ) : undefined } renderVideo={(p) =>