Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 52fc8ff

Browse files
authored
Fix html export not including images (#9260)
* Fix html export not including images * Respect showPlaceholder * Add tests
1 parent 638175b commit 52fc8ff

File tree

3 files changed

+68
-23
lines changed

3 files changed

+68
-23
lines changed

src/components/views/messages/MImageBody.tsx

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
391391
// By doing this, the image "pops" into the timeline, but is still restricted
392392
// by the same width and height logic below.
393393
if (!this.state.loadedImageDimensions) {
394-
let imageElement;
394+
let imageElement: JSX.Element;
395395
if (!this.state.showImage) {
396396
imageElement = <HiddenImagePlaceholder />;
397397
} else {
@@ -425,7 +425,13 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
425425
let gifLabel: JSX.Element;
426426

427427
if (!this.props.forExport && !this.state.imgLoaded) {
428-
placeholder = this.getPlaceholder(maxWidth, maxHeight);
428+
const classes = classNames('mx_MImageBody_placeholder', {
429+
'mx_MImageBody_placeholder--blurhash': this.props.mxEvent.getContent().info?.[BLURHASH_FIELD],
430+
});
431+
432+
placeholder = <div className={classes}>
433+
{ this.getPlaceholder(maxWidth, maxHeight) }
434+
</div>;
429435
}
430436

431437
let showPlaceholder = Boolean(placeholder);
@@ -463,29 +469,26 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
463469
banner = this.getBanner(content);
464470
}
465471

466-
const classes = classNames({
467-
'mx_MImageBody_placeholder': true,
468-
'mx_MImageBody_placeholder--blurhash': this.props.mxEvent.getContent().info?.[BLURHASH_FIELD],
469-
});
470-
471472
// many SVGs don't have an intrinsic size if used in <img> elements.
472473
// due to this we have to set our desired width directly.
473474
// this way if the image is forced to shrink, the height adapts appropriately.
474475
const sizing = infoSvg ? { maxHeight, maxWidth, width: maxWidth } : { maxHeight, maxWidth };
475476

477+
if (!this.props.forExport) {
478+
placeholder = <SwitchTransition mode="out-in">
479+
<CSSTransition
480+
classNames="mx_rtg--fade"
481+
key={`img-${showPlaceholder}`}
482+
timeout={300}
483+
>
484+
{ showPlaceholder ? placeholder : <></> /* Transition always expects a child */ }
485+
</CSSTransition>
486+
</SwitchTransition>;
487+
}
488+
476489
const thumbnail = (
477490
<div className="mx_MImageBody_thumbnail_container" style={{ maxHeight, maxWidth, aspectRatio: `${infoWidth}/${infoHeight}` }}>
478-
<SwitchTransition mode="out-in">
479-
<CSSTransition
480-
classNames="mx_rtg--fade"
481-
key={`img-${showPlaceholder}`}
482-
timeout={300}
483-
>
484-
{ showPlaceholder ? <div className={classes}>
485-
{ placeholder }
486-
</div> : <></> /* Transition always expects a child */ }
487-
</CSSTransition>
488-
</SwitchTransition>
491+
{ placeholder }
489492

490493
<div style={sizing}>
491494
{ img }
@@ -494,7 +497,9 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
494497
</div>
495498

496499
{ /* HACK: This div fills out space while the image loads, to prevent scroll jumps */ }
497-
{ !this.state.imgLoaded && <div style={{ height: maxHeight, width: maxWidth }} /> }
500+
{ !this.props.forExport && !this.state.imgLoaded && (
501+
<div style={{ height: maxHeight, width: maxWidth }} />
502+
) }
498503

499504
{ this.state.hover && this.getTooltip() }
500505
</div>
@@ -559,9 +564,12 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
559564
);
560565
}
561566

562-
const contentUrl = this.state.contentUrl;
567+
let contentUrl = this.state.contentUrl;
563568
let thumbUrl: string;
564-
if (this.props.forExport || (this.state.isAnimated && SettingsStore.getValue("autoplayGifs"))) {
569+
if (this.props.forExport) {
570+
contentUrl = this.props.mxEvent.getContent().url ?? this.props.mxEvent.getContent().file?.url;
571+
thumbUrl = contentUrl;
572+
} else if (this.state.isAnimated && SettingsStore.getValue("autoplayGifs")) {
565573
thumbUrl = contentUrl;
566574
} else {
567575
thumbUrl = this.state.thumbUrl ?? this.state.contentUrl;

src/utils/exportUtils/HtmlExport.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ export default class HTMLExporter extends Exporter {
316316
}
317317

318318
if (filePath) {
319-
const mxc = mxEv.getContent().url || mxEv.getContent().file?.url;
319+
const mxc = mxEv.getContent().url ?? mxEv.getContent().file?.url;
320320
eventTileMarkup = eventTileMarkup.split(mxc).join(filePath);
321321
}
322322
eventTileMarkup = eventTileMarkup.replace(/<span class="mx_MFileBody_info_icon".*?>.*?<\/span>/, '');
@@ -382,7 +382,9 @@ export default class HTMLExporter extends Exporter {
382382
joined,
383383
);
384384
}
385-
} else eventTile = await this.getEventTileMarkup(mxEv, joined);
385+
} else {
386+
eventTile = await this.getEventTileMarkup(mxEv, joined);
387+
}
386388
} catch (e) {
387389
// TODO: Handle callEvent errors
388390
logger.error(e);

test/utils/export-test.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,29 @@ describe('export', function() {
115115
});
116116
}
117117

118+
function mkImageEvent() {
119+
return new MatrixEvent({
120+
"content": {
121+
"body": "image.png",
122+
"info": {
123+
"mimetype": "image/png",
124+
"size": 31613,
125+
},
126+
"msgtype": "m.image",
127+
"url": "mxc://test.org",
128+
},
129+
"origin_server_ts": 1628872988364,
130+
"sender": MY_USER_ID,
131+
"type": "m.room.message",
132+
"unsigned": {
133+
"age": 266,
134+
"transaction_id": "m99999999.2",
135+
},
136+
"event_id": "$99999999999999999999",
137+
"room_id": mockRoom.roomId,
138+
});
139+
}
140+
118141
function mkEvents() {
119142
const matrixEvents = [];
120143
let i: number;
@@ -204,6 +227,18 @@ describe('export', function() {
204227
).toBeTruthy();
205228
});
206229

230+
it("should export images if attachments are enabled", () => {
231+
const exporter = new HTMLExporter(mockRoom, ExportType.Beginning, {
232+
numberOfMessages: 5,
233+
maxSize: 100 * 1024 * 1024,
234+
attachmentsIncluded: true,
235+
}, null);
236+
const imageRegex = /<img.+ src="mxc:\/\/test.org" alt="image.png"\/>/;
237+
expect(imageRegex.test(
238+
renderToString(exporter.getEventTile(mkImageEvent(), true))),
239+
).toBeTruthy();
240+
});
241+
207242
const invalidExportOptions: [string, IExportOptions][] = [
208243
['numberOfMessages exceeds max', {
209244
numberOfMessages: 10 ** 9,

0 commit comments

Comments
 (0)