From 52fc8ff255f1bf9180f00144b679c48b842943d8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 8 Sep 2022 11:00:58 +0100 Subject: [PATCH] Fix html export not including images (#9260) * Fix html export not including images * Respect showPlaceholder * Add tests --- src/components/views/messages/MImageBody.tsx | 50 ++++++++++++-------- src/utils/exportUtils/HtmlExport.tsx | 6 ++- test/utils/export-test.tsx | 35 ++++++++++++++ 3 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/components/views/messages/MImageBody.tsx b/src/components/views/messages/MImageBody.tsx index 87d7e419e35..da579664b27 100644 --- a/src/components/views/messages/MImageBody.tsx +++ b/src/components/views/messages/MImageBody.tsx @@ -391,7 +391,7 @@ export default class MImageBody extends React.Component { // By doing this, the image "pops" into the timeline, but is still restricted // by the same width and height logic below. if (!this.state.loadedImageDimensions) { - let imageElement; + let imageElement: JSX.Element; if (!this.state.showImage) { imageElement = ; } else { @@ -425,7 +425,13 @@ export default class MImageBody extends React.Component { let gifLabel: JSX.Element; if (!this.props.forExport && !this.state.imgLoaded) { - placeholder = this.getPlaceholder(maxWidth, maxHeight); + const classes = classNames('mx_MImageBody_placeholder', { + 'mx_MImageBody_placeholder--blurhash': this.props.mxEvent.getContent().info?.[BLURHASH_FIELD], + }); + + placeholder =
+ { this.getPlaceholder(maxWidth, maxHeight) } +
; } let showPlaceholder = Boolean(placeholder); @@ -463,29 +469,26 @@ export default class MImageBody extends React.Component { banner = this.getBanner(content); } - const classes = classNames({ - 'mx_MImageBody_placeholder': true, - 'mx_MImageBody_placeholder--blurhash': this.props.mxEvent.getContent().info?.[BLURHASH_FIELD], - }); - // many SVGs don't have an intrinsic size if used in elements. // due to this we have to set our desired width directly. // this way if the image is forced to shrink, the height adapts appropriately. const sizing = infoSvg ? { maxHeight, maxWidth, width: maxWidth } : { maxHeight, maxWidth }; + if (!this.props.forExport) { + placeholder = + + { showPlaceholder ? placeholder : <> /* Transition always expects a child */ } + + ; + } + const thumbnail = (
- - - { showPlaceholder ?
- { placeholder } -
: <> /* Transition always expects a child */ } -
-
+ { placeholder }
{ img } @@ -494,7 +497,9 @@ export default class MImageBody extends React.Component {
{ /* HACK: This div fills out space while the image loads, to prevent scroll jumps */ } - { !this.state.imgLoaded &&
} + { !this.props.forExport && !this.state.imgLoaded && ( +
+ ) } { this.state.hover && this.getTooltip() }
@@ -559,9 +564,12 @@ export default class MImageBody extends React.Component { ); } - const contentUrl = this.state.contentUrl; + let contentUrl = this.state.contentUrl; let thumbUrl: string; - if (this.props.forExport || (this.state.isAnimated && SettingsStore.getValue("autoplayGifs"))) { + if (this.props.forExport) { + contentUrl = this.props.mxEvent.getContent().url ?? this.props.mxEvent.getContent().file?.url; + thumbUrl = contentUrl; + } else if (this.state.isAnimated && SettingsStore.getValue("autoplayGifs")) { thumbUrl = contentUrl; } else { thumbUrl = this.state.thumbUrl ?? this.state.contentUrl; diff --git a/src/utils/exportUtils/HtmlExport.tsx b/src/utils/exportUtils/HtmlExport.tsx index f74d4a91cab..dcc7994ace6 100644 --- a/src/utils/exportUtils/HtmlExport.tsx +++ b/src/utils/exportUtils/HtmlExport.tsx @@ -316,7 +316,7 @@ export default class HTMLExporter extends Exporter { } if (filePath) { - const mxc = mxEv.getContent().url || mxEv.getContent().file?.url; + const mxc = mxEv.getContent().url ?? mxEv.getContent().file?.url; eventTileMarkup = eventTileMarkup.split(mxc).join(filePath); } eventTileMarkup = eventTileMarkup.replace(/.*?<\/span>/, ''); @@ -382,7 +382,9 @@ export default class HTMLExporter extends Exporter { joined, ); } - } else eventTile = await this.getEventTileMarkup(mxEv, joined); + } else { + eventTile = await this.getEventTileMarkup(mxEv, joined); + } } catch (e) { // TODO: Handle callEvent errors logger.error(e); diff --git a/test/utils/export-test.tsx b/test/utils/export-test.tsx index 1c09ecf6def..00a614b8552 100644 --- a/test/utils/export-test.tsx +++ b/test/utils/export-test.tsx @@ -115,6 +115,29 @@ describe('export', function() { }); } + function mkImageEvent() { + return new MatrixEvent({ + "content": { + "body": "image.png", + "info": { + "mimetype": "image/png", + "size": 31613, + }, + "msgtype": "m.image", + "url": "mxc://test.org", + }, + "origin_server_ts": 1628872988364, + "sender": MY_USER_ID, + "type": "m.room.message", + "unsigned": { + "age": 266, + "transaction_id": "m99999999.2", + }, + "event_id": "$99999999999999999999", + "room_id": mockRoom.roomId, + }); + } + function mkEvents() { const matrixEvents = []; let i: number; @@ -204,6 +227,18 @@ describe('export', function() { ).toBeTruthy(); }); + it("should export images if attachments are enabled", () => { + const exporter = new HTMLExporter(mockRoom, ExportType.Beginning, { + numberOfMessages: 5, + maxSize: 100 * 1024 * 1024, + attachmentsIncluded: true, + }, null); + const imageRegex = //; + expect(imageRegex.test( + renderToString(exporter.getEventTile(mkImageEvent(), true))), + ).toBeTruthy(); + }); + const invalidExportOptions: [string, IExportOptions][] = [ ['numberOfMessages exceeds max', { numberOfMessages: 10 ** 9,