Skip to content

Commit fb1732c

Browse files
committed
fix: harden MXC image rendering and simplify HTML paths
1 parent 768a05f commit fb1732c

8 files changed

Lines changed: 333 additions & 190 deletions

File tree

src/app/components/RenderMessageContent.tsx

Lines changed: 104 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ function RenderMessageContentInternal({
8686
[CaptionPosition.Inline]: 'row',
8787
[CaptionPosition.Hidden]: 'row',
8888
} satisfies Record<CaptionPosition, React.CSSProperties['flexDirection']>;
89+
const attachmentDirection = captionPositionMap[captionPosition];
8990

9091
const renderBody = useCallback(
9192
(props: any) => (
@@ -131,6 +132,7 @@ function RenderMessageContentInternal({
131132
},
132133
[ts, clientUrlPreview, urlPreview]
133134
);
135+
const messageUrlsPreview = urlPreview ? renderUrlsPreview : undefined;
134136

135137
const renderCaption = () => {
136138
const hasCaption = content.body && content.body.trim().length > 0;
@@ -143,7 +145,7 @@ function RenderMessageContentInternal({
143145
edited={edited}
144146
content={content}
145147
renderBody={renderBody}
146-
renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined}
148+
renderUrlsPreview={messageUrlsPreview}
147149
/>
148150
);
149151
return (
@@ -162,70 +164,69 @@ function RenderMessageContentInternal({
162164
edited={edited}
163165
content={content}
164166
renderBody={renderBody}
165-
renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined}
167+
renderUrlsPreview={messageUrlsPreview}
166168
/>
167169
</Box>
168170
);
169171
}
170172
return null;
171173
};
172174

173-
const renderFile = () => (
174-
<div
175-
style={{
176-
display: 'flex',
177-
flexDirection: captionPositionMap[captionPosition],
178-
}}
179-
>
180-
<div>
181-
<MFile
182-
content={content}
183-
renderFileContent={({ body, mimeType, info, encInfo, url }) => (
184-
<FileContent
185-
body={body}
186-
mimeType={mimeType}
187-
renderAsPdfFile={() => (
188-
<ReadPdfFile
189-
body={body}
190-
mimeType={mimeType}
191-
url={url}
192-
encInfo={encInfo}
193-
renderViewer={(p) => <PdfViewer {...p} />}
194-
/>
195-
)}
196-
renderAsTextFile={() => (
197-
<ReadTextFile
198-
body={body}
199-
mimeType={mimeType}
200-
url={url}
201-
encInfo={encInfo}
202-
renderViewer={(p) => <TextViewer {...p} />}
203-
/>
204-
)}
205-
>
206-
<DownloadFile
175+
function renderCaptionedAttachment(attachment: JSX.Element): JSX.Element {
176+
return (
177+
<div
178+
style={{
179+
display: 'flex',
180+
flexDirection: attachmentDirection,
181+
}}
182+
>
183+
<div>{attachment}</div>
184+
{renderCaption()}
185+
</div>
186+
);
187+
}
188+
189+
const renderFile = () =>
190+
renderCaptionedAttachment(
191+
<MFile
192+
content={content}
193+
renderFileContent={({ body, mimeType, info, encInfo, url }) => (
194+
<FileContent
195+
body={body}
196+
mimeType={mimeType}
197+
renderAsPdfFile={() => (
198+
<ReadPdfFile
207199
body={body}
208200
mimeType={mimeType}
209201
url={url}
210202
encInfo={encInfo}
211-
info={info}
203+
renderViewer={(p) => <PdfViewer {...p} />}
212204
/>
213-
</FileContent>
214-
)}
215-
outlined={outlineAttachment}
216-
/>
217-
</div>
218-
{renderCaption()}
219-
</div>
220-
);
205+
)}
206+
renderAsTextFile={() => (
207+
<ReadTextFile
208+
body={body}
209+
mimeType={mimeType}
210+
url={url}
211+
encInfo={encInfo}
212+
renderViewer={(p) => <TextViewer {...p} />}
213+
/>
214+
)}
215+
>
216+
<DownloadFile body={body} mimeType={mimeType} url={url} encInfo={encInfo} info={info} />
217+
</FileContent>
218+
)}
219+
outlined={outlineAttachment}
220+
/>
221+
);
221222

222223
if (msgType === MsgType.Text) {
223224
return (
224225
<MText
225226
edited={edited}
226227
content={content}
227228
renderBody={renderBody}
228-
renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined}
229+
renderUrlsPreview={messageUrlsPreview}
229230
/>
230231
);
231232
}
@@ -246,7 +247,7 @@ function RenderMessageContentInternal({
246247
edited={edited}
247248
content={content}
248249
renderBody={renderBody}
249-
renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined}
250+
renderUrlsPreview={messageUrlsPreview}
250251
/>
251252
);
252253
}
@@ -257,7 +258,7 @@ function RenderMessageContentInternal({
257258
edited={edited}
258259
content={content}
259260
renderBody={renderBody}
260-
renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined}
261+
renderUrlsPreview={messageUrlsPreview}
261262
/>
262263
);
263264
}
@@ -270,101 +271,71 @@ function RenderMessageContentInternal({
270271
content.body?.toLowerCase().endsWith('.webp') ||
271272
(typeof content.url === 'string' && content.url.toLowerCase().includes('gif'));
272273

273-
return (
274-
<div
275-
style={{
276-
display: 'flex',
277-
flexDirection: captionPositionMap[captionPosition],
278-
}}
279-
>
280-
<div>
281-
<MImage
282-
content={content}
283-
renderImageContent={(imageProps) => (
284-
<ImageContent
285-
{...imageProps}
286-
autoPlay={mediaAutoLoad}
287-
renderImage={(p) => {
288-
if (isGif && !autoplayGifs && p.src) {
289-
return (
290-
<ClientSideHoverFreeze src={p.src}>
291-
<Image {...p} loading="lazy" />
292-
</ClientSideHoverFreeze>
293-
);
294-
}
295-
return <Image {...p} loading="lazy" />;
296-
}}
297-
renderViewer={(p) => <ImageViewer {...p} />}
298-
/>
299-
)}
300-
outlined={outlineAttachment}
274+
return renderCaptionedAttachment(
275+
<MImage
276+
content={content}
277+
renderImageContent={(imageProps) => (
278+
<ImageContent
279+
{...imageProps}
280+
autoPlay={mediaAutoLoad}
281+
renderImage={(p) => {
282+
if (isGif && !autoplayGifs && p.src) {
283+
return (
284+
<ClientSideHoverFreeze src={p.src}>
285+
<Image {...p} loading="lazy" />
286+
</ClientSideHoverFreeze>
287+
);
288+
}
289+
return <Image {...p} loading="lazy" />;
290+
}}
291+
renderViewer={(p) => <ImageViewer {...p} />}
301292
/>
302-
</div>
303-
{renderCaption()}
304-
</div>
293+
)}
294+
outlined={outlineAttachment}
295+
/>
305296
);
306297
}
307298

308299
if (msgType === MsgType.Video) {
309-
return (
310-
<div
311-
style={{
312-
display: 'flex',
313-
flexDirection: captionPositionMap[captionPosition],
314-
}}
315-
>
316-
<div>
317-
<MVideo
318-
content={content}
319-
renderAsFile={renderFile}
320-
renderVideoContent={({ body, info, ...videoProps }) => (
321-
<VideoContent
322-
body={body}
323-
info={info}
324-
{...videoProps}
325-
renderThumbnail={
326-
mediaAutoLoad
327-
? () => (
328-
<ThumbnailContent
329-
info={info}
330-
renderImage={(src) => (
331-
<Image alt={body} title={body} src={src} loading="lazy" />
332-
)}
333-
/>
334-
)
335-
: undefined
336-
}
337-
renderVideo={(p) => <PersistedVolumeVideo {...p} />}
338-
/>
339-
)}
340-
outlined={outlineAttachment}
300+
return renderCaptionedAttachment(
301+
<MVideo
302+
content={content}
303+
renderAsFile={renderFile}
304+
renderVideoContent={({ body, info, ...videoProps }) => (
305+
<VideoContent
306+
body={body}
307+
info={info}
308+
{...videoProps}
309+
renderThumbnail={
310+
mediaAutoLoad
311+
? () => (
312+
<ThumbnailContent
313+
info={info}
314+
renderImage={(src) => (
315+
<Image alt={body} title={body} src={src} loading="lazy" />
316+
)}
317+
/>
318+
)
319+
: undefined
320+
}
321+
renderVideo={(p) => <PersistedVolumeVideo {...p} />}
341322
/>
342-
</div>
343-
{renderCaption()}
344-
</div>
323+
)}
324+
outlined={outlineAttachment}
325+
/>
345326
);
346327
}
347328

348329
if (msgType === MsgType.Audio) {
349-
return (
350-
<div
351-
style={{
352-
display: 'flex',
353-
flexDirection: captionPositionMap[captionPosition],
354-
}}
355-
>
356-
<div>
357-
<MAudio
358-
content={content}
359-
renderAsFile={renderFile}
360-
renderAudioContent={(audioProps) => (
361-
<AudioContent {...audioProps} renderMediaControl={(p) => <MediaControl {...p} />} />
362-
)}
363-
outlined={outlineAttachment}
364-
/>
365-
</div>
366-
{renderCaption()}
367-
</div>
330+
return renderCaptionedAttachment(
331+
<MAudio
332+
content={content}
333+
renderAsFile={renderFile}
334+
renderAudioContent={(audioProps) => (
335+
<AudioContent {...audioProps} renderMediaControl={(p) => <MediaControl {...p} />} />
336+
)}
337+
outlined={outlineAttachment}
338+
/>
368339
);
369340
}
370341

src/app/components/message/RenderBody.tsx

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,25 @@ import { useRoomAbbreviationsContext } from '$hooks/useRoomAbbreviations';
99
import { splitByAbbreviations } from '$utils/abbreviations';
1010
import { MessageEmptyContent } from './content';
1111

12+
function getRenderedBodyText(text: string, highlightRegex?: RegExp): (string | JSX.Element)[] {
13+
const emojiScaledText = scaleSystemEmoji(text);
14+
15+
return highlightRegex ? highlightText(highlightRegex, emojiScaledText) : emojiScaledText;
16+
}
17+
18+
function renderLinkifiedBodyText(
19+
text: string,
20+
linkifyOpts: Opts,
21+
highlightRegex: RegExp | undefined,
22+
key?: string
23+
): JSX.Element {
24+
return (
25+
<Linkify key={key} options={linkifyOpts}>
26+
{getRenderedBodyText(text, highlightRegex)}
27+
</Linkify>
28+
);
29+
}
30+
1231
type AbbreviationTermProps = {
1332
text: string;
1433
definition: string;
@@ -92,7 +111,6 @@ export function buildAbbrReplaceTextNode(
92111
type RenderBodyProps = {
93112
body: string;
94113
customBody?: string;
95-
96114
highlightRegex?: RegExp;
97115
htmlReactParserOptions: HTMLReactParserOptions;
98116
linkifyOpts: Opts;
@@ -107,7 +125,6 @@ export function RenderBody({
107125
const abbrMap = useRoomAbbreviationsContext();
108126

109127
if (customBody) {
110-
if (customBody === '') return <MessageEmptyContent />;
111128
return parse(sanitizeCustomHtml(customBody), htmlReactParserOptions);
112129
}
113130
if (body === '') return <MessageEmptyContent />;
@@ -122,24 +139,12 @@ export function RenderBody({
122139
const definition = abbrMap.get(seg.termKey) ?? '';
123140
return <AbbreviationTerm key={seg.id} text={seg.text} definition={definition} />;
124141
}
125-
return (
126-
<Linkify key={seg.id} options={linkifyOpts}>
127-
{highlightRegex
128-
? highlightText(highlightRegex, scaleSystemEmoji(seg.text))
129-
: scaleSystemEmoji(seg.text)}
130-
</Linkify>
131-
);
142+
return renderLinkifiedBodyText(seg.text, linkifyOpts, highlightRegex, seg.id);
132143
})}
133144
</>
134145
);
135146
}
136147
}
137148

138-
return (
139-
<Linkify options={linkifyOpts}>
140-
{highlightRegex
141-
? highlightText(highlightRegex, scaleSystemEmoji(body))
142-
: scaleSystemEmoji(body)}
143-
</Linkify>
144-
);
149+
return renderLinkifiedBodyText(body, linkifyOpts, highlightRegex);
145150
}

0 commit comments

Comments
 (0)