import React, { MouseEventHandler, createContext, useContext } 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, RenderImageContentProps, RenderVideoContentProps, StreamMediaCaption, StreamMediaImage, StreamMediaVideo, ThumbnailContent, UnsupportedContent, 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 { IImageContent } from '../../types/matrix/common'; import { logMedia } from './message/attachment/streamMediaDebug'; // Threads the StreamLayout's mediaMode info from Message.tsx down to the // image / video rendering branches below. Non-null only for media messages // in the timeline; pin-menu / message-search leave it null and fall back // to the legacy MImage / MVideo Attachment chrome. export type StreamMediaContextValue = { own: boolean; username: string; senderId: string; onUsernameClick: MouseEventHandler; onUsernameContextMenu: MouseEventHandler; }; export const StreamMediaContext = createContext(null); export const useStreamMediaContext = (): StreamMediaContextValue | null => useContext(StreamMediaContext); type RenderMessageContentProps = { displayName: string; msgType: string; ts: number; edited?: boolean; getContent: () => T; mediaAutoLoad?: boolean; urlPreview?: boolean; highlightRegex?: RegExp; htmlReactParserOptions: HTMLReactParserOptions; linkifyOpts: Opts; outlineAttachment?: boolean; }; export function RenderMessageContent({ displayName, msgType, ts, edited, getContent, mediaAutoLoad, urlPreview, highlightRegex, htmlReactParserOptions, linkifyOpts, outlineAttachment, }: RenderMessageContentProps) { const streamMedia = useStreamMediaContext(); const renderUrlsPreview = (urls: string[]) => { const filteredUrls = urls.filter((url) => !testMatrixTo(url)); if (filteredUrls.length === 0) return undefined; return ( {filteredUrls.map((url) => ( ))} ); }; const renderCaption = () => { const content: IImageContent = getContent(); if (content.filename && content.filename !== content.body) { const captionNode = ( ( )} renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined} /> ); if (streamMedia) { return
{captionNode}
; } return captionNode; } return null; }; const renderFile = () => ( <> ( ( } /> )} renderAsTextFile={() => ( } /> )} > )} outlined={outlineAttachment} /> {renderCaption()} ); if (msgType === MsgType.Text) { return ( ( )} renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined} /> ); } if (msgType === MsgType.Emote) { return ( ( )} renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined} /> ); } if (msgType === MsgType.Notice) { return ( ( )} renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined} /> ); } if (msgType === MsgType.Image) { logMedia('RenderMessageContent', { msgType, streamMediaPresent: !!streamMedia, branch: streamMedia ? 'StreamMediaImage' : 'MImage(legacy)', }); const renderImageInside = (props: RenderImageContentProps) => ( } renderViewer={(p) => } /> ); return ( <> {streamMedia ? ( ) : ( )} {renderCaption()} ); } if (msgType === MsgType.Video) { logMedia('RenderMessageContent', { msgType, streamMediaPresent: !!streamMedia, branch: streamMedia ? 'StreamMediaVideo' : 'MVideo(legacy)', }); const renderVideoInside = ({ body, info, ...props }: RenderVideoContentProps) => ( ( ( {body} )} /> ) : undefined } renderVideo={(p) =>