Skip to content

Commit 3d66641

Browse files
authored
Send and respect MSC4230 is_animated flag (#28513)
* PoC implementation for is_animated m.image flag Signed-off-by: Michael Telatynski <[email protected]> * Update MSC reference Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> --------- Signed-off-by: Michael Telatynski <[email protected]>
1 parent 7d20bd4 commit 3d66641

File tree

4 files changed

+26
-18
lines changed

4 files changed

+26
-18
lines changed

src/@types/matrix-js-sdk.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ declare module "matrix-js-sdk/src/types" {
2222
[BLURHASH_FIELD]?: string;
2323
}
2424

25+
export interface ImageInfo {
26+
/**
27+
* @see https://github.com/matrix-org/matrix-spec-proposals/pull/4230
28+
*/
29+
"org.matrix.msc4230.is_animated"?: boolean;
30+
}
31+
2532
export interface StateEvents {
2633
// Jitsi-backed video room state events
2734
[JitsiCallMemberEventType]: JitsiCallMemberContent;

src/ContentMessages.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import { createThumbnail } from "./utils/image-media";
5656
import { attachMentions, attachRelation } from "./components/views/rooms/SendMessageComposer";
5757
import { doMaybeLocalRoomAction } from "./utils/local-room";
5858
import { SdkContextClass } from "./contexts/SDKContext";
59+
import { blobIsAnimated } from "./utils/Image.ts";
5960

6061
// scraped out of a macOS hidpi (5660ppm) screenshot png
6162
// 5669 px (x-axis) , 5669 px (y-axis) , per metre
@@ -150,15 +151,20 @@ async function infoForImageFile(matrixClient: MatrixClient, roomId: string, imag
150151
thumbnailType = "image/jpeg";
151152
}
152153

154+
// We don't await this immediately so it can happen in the background
155+
const isAnimatedPromise = blobIsAnimated(imageFile.type, imageFile);
156+
153157
const imageElement = await loadImageElement(imageFile);
154158

155159
const result = await createThumbnail(imageElement.img, imageElement.width, imageElement.height, thumbnailType);
156160
const imageInfo = result.info;
157161

162+
imageInfo["org.matrix.msc4230.is_animated"] = await isAnimatedPromise;
163+
158164
// For lesser supported image types, always include the thumbnail even if it is larger
159165
if (!ALWAYS_INCLUDE_THUMBNAIL.includes(imageFile.type)) {
160166
// we do all sizing checks here because we still rely on thumbnail generation for making a blurhash from.
161-
const sizeDifference = imageFile.size - imageInfo.thumbnail_info!.size;
167+
const sizeDifference = imageFile.size - imageInfo.thumbnail_info!.size!;
162168
if (
163169
// image is small enough already
164170
imageFile.size <= IMAGE_SIZE_THRESHOLD_THUMBNAIL ||

src/components/views/messages/MImageBody.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
275275
}
276276

277277
const content = this.props.mxEvent.getContent<ImageContent>();
278-
let isAnimated = mayBeAnimated(content.info?.mimetype);
278+
let isAnimated = content.info?.["org.matrix.msc4230.is_animated"] ?? mayBeAnimated(content.info?.mimetype);
279279

280280
// If there is no included non-animated thumbnail then we will generate our own, we can't depend on the server
281281
// because 1. encryption and 2. we can't ask the server specifically for a non-animated thumbnail.
@@ -298,8 +298,15 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
298298
}
299299

300300
try {
301-
const blob = await this.props.mediaEventHelper!.sourceBlob.value;
302-
if (!(await blobIsAnimated(content.info?.mimetype, blob))) {
301+
// If we didn't receive the MSC4230 is_animated flag
302+
// then we need to check if the image is animated by downloading it.
303+
if (
304+
content.info?.["org.matrix.msc4230.is_animated"] === false ||
305+
!(await blobIsAnimated(
306+
content.info?.mimetype,
307+
await this.props.mediaEventHelper!.sourceBlob.value,
308+
))
309+
) {
303310
isAnimated = false;
304311
}
305312

src/utils/image-media.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
66
Please see LICENSE files in the repository root for full details.
77
*/
88

9-
import { EncryptedFile } from "matrix-js-sdk/src/types";
9+
import { ImageInfo } from "matrix-js-sdk/src/types";
1010

1111
import { BlurhashEncoder } from "../BlurhashEncoder";
1212

@@ -15,19 +15,7 @@ type ThumbnailableElement = HTMLImageElement | HTMLVideoElement;
1515
export const BLURHASH_FIELD = "xyz.amorgan.blurhash"; // MSC2448
1616

1717
interface IThumbnail {
18-
info: {
19-
thumbnail_info?: {
20-
w: number;
21-
h: number;
22-
mimetype: string;
23-
size: number;
24-
};
25-
w: number;
26-
h: number;
27-
[BLURHASH_FIELD]?: string;
28-
thumbnail_url?: string;
29-
thumbnail_file?: EncryptedFile;
30-
};
18+
info: ImageInfo;
3119
thumbnail: Blob;
3220
}
3321

0 commit comments

Comments
 (0)