@@ -46,6 +46,7 @@ enum Placeholder {
4646interface IState {
4747 contentUrl ?: string ;
4848 thumbUrl ?: string ;
49+ isAnimated ?: boolean ;
4950 error ?: Error ;
5051 imgError : boolean ;
5152 imgLoaded : boolean ;
@@ -58,6 +59,11 @@ interface IState {
5859 placeholder : Placeholder ;
5960}
6061
62+ function mayBeAnimated ( mimeType : string ) : boolean {
63+ // Both GIF and WEBP can be animated, and here we assume they are, as checking is much more difficult.
64+ return [ "image/gif" , "image/webp" ] . includes ( mimeType ) ;
65+ }
66+
6167@replaceableComponent ( "views.messages.MImageBody" )
6268export default class MImageBody extends React . Component < IBodyProps , IState > {
6369 static contextType = RoomContext ;
@@ -138,19 +144,10 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
138144 }
139145 } ;
140146
141- private isAnimatedMimeType ( mimeType : string ) : boolean {
142- return [ "image/gif" , "image/webp" ] . includes ( mimeType ) ;
143- }
144-
145- private get isAnimated ( ) : boolean {
146- // Both GIF and WEBP can be animated, and here we assume they are, as checking is much more difficult.
147- return this . isAnimatedMimeType ( this . props . mxEvent . getContent ( ) . info ?. mimetype ) ;
148- }
149-
150147 private onImageEnter = ( e : React . MouseEvent < HTMLImageElement > ) : void => {
151148 this . setState ( { hover : true } ) ;
152149
153- if ( ! this . state . showImage || ! this . isAnimated || SettingsStore . getValue ( "autoplayGifs" ) ) {
150+ if ( ! this . state . showImage || ! this . state . isAnimated || SettingsStore . getValue ( "autoplayGifs" ) ) {
154151 return ;
155152 }
156153 const imgElement = e . currentTarget ;
@@ -160,7 +157,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
160157 private onImageLeave = ( e : React . MouseEvent < HTMLImageElement > ) : void => {
161158 this . setState ( { hover : false } ) ;
162159
163- if ( ! this . state . showImage || ! this . isAnimated || SettingsStore . getValue ( "autoplayGifs" ) ) {
160+ if ( ! this . state . showImage || ! this . state . isAnimated || SettingsStore . getValue ( "autoplayGifs" ) ) {
164161 return ;
165162 }
166163 const imgElement = e . currentTarget ;
@@ -231,7 +228,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
231228 // - On a low DPI device, always thumbnail to save bandwidth
232229 // - If there's no sizing info in the event, default to thumbnail
233230 if (
234- this . isAnimated ||
231+ this . state . isAnimated ||
235232 window . devicePixelRatio === 1.0 ||
236233 ( ! info || ! info . w || ! info . h || ! info . size )
237234 ) {
@@ -284,13 +281,13 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
284281 contentUrl = this . getContentUrl ( ) ;
285282 }
286283
284+ const content = this . props . mxEvent . getContent < IMediaEventContent > ( ) ;
285+ const isAnimated = mayBeAnimated ( content . info ?. mimetype ) ;
286+
287287 // If there is no included non-animated thumbnail then we will generate our own, we can't depend on the server
288288 // because 1. encryption and 2. we can't ask the server specifically for a non-animated thumbnail.
289- if ( this . isAnimated && ! SettingsStore . getValue ( "autoplayGifs" ) ) {
290- const content = this . props . mxEvent . getContent < IMediaEventContent > ( ) ;
291- if ( ! thumbUrl ||
292- ! content ?. info . thumbnail_info ||
293- this . isAnimatedMimeType ( content . info . thumbnail_info . mimetype ) ) {
289+ if ( isAnimated && ! SettingsStore . getValue ( "autoplayGifs" ) ) {
290+ if ( ! thumbUrl || ! content ?. info . thumbnail_info || mayBeAnimated ( content . info . thumbnail_info . mimetype ) ) {
294291 const img = document . createElement ( "img" ) ;
295292 const loadPromise = new Promise ( ( resolve , reject ) => {
296293 img . onload = function ( ) {
@@ -304,15 +301,19 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
304301 img . src = contentUrl ;
305302
306303 await loadPromise ;
307- const thumb = await createThumbnail ( img , img . width , img . height , content . info . mimetype , false ) ;
308- thumbUrl = URL . createObjectURL ( thumb . thumbnail ) ;
304+
305+ if ( isAnimated ) {
306+ const thumb = await createThumbnail ( img , img . width , img . height , content . info . mimetype , false ) ;
307+ thumbUrl = URL . createObjectURL ( thumb . thumbnail ) ;
308+ }
309309 }
310310 }
311311
312312 if ( this . unmounted ) return ;
313313 this . setState ( {
314314 contentUrl,
315315 thumbUrl,
316+ isAnimated,
316317 } ) ;
317318 }
318319
@@ -358,7 +359,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
358359 MatrixClientPeg . get ( ) . removeListener ( ClientEvent . Sync , this . onClientSync ) ;
359360 this . clearBlurhashTimeout ( ) ;
360361 SettingsStore . unwatchSetting ( this . sizeWatcher ) ;
361- if ( this . isAnimated && this . state . thumbUrl ) {
362+ if ( this . state . isAnimated && this . state . thumbUrl ) {
362363 URL . revokeObjectURL ( this . state . thumbUrl ) ;
363364 }
364365 }
@@ -455,7 +456,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
455456 showPlaceholder = false ; // because we're hiding the image, so don't show the placeholder.
456457 }
457458
458- if ( this . isAnimated && ! SettingsStore . getValue ( "autoplayGifs" ) && ! this . state . hover ) {
459+ if ( this . state . isAnimated && ! SettingsStore . getValue ( "autoplayGifs" ) && ! this . state . hover ) {
459460 // XXX: Arguably we may want a different label when the animated image is WEBP and not GIF
460461 gifLabel = < p className = "mx_MImageBody_gifLabel" > GIF</ p > ;
461462 }
@@ -567,7 +568,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
567568
568569 const contentUrl = this . state . contentUrl ;
569570 let thumbUrl : string ;
570- if ( this . props . forExport || ( this . isAnimated && SettingsStore . getValue ( "autoplayGifs" ) ) ) {
571+ if ( this . props . forExport || ( this . state . isAnimated && SettingsStore . getValue ( "autoplayGifs" ) ) ) {
571572 thumbUrl = contentUrl ;
572573 } else {
573574 thumbUrl = this . state . thumbUrl ;
0 commit comments