@@ -51,6 +51,7 @@ interface IState {
51
51
naturalHeight : number ;
52
52
} ;
53
53
hover : boolean ;
54
+ focus : boolean ;
54
55
showImage : boolean ;
55
56
placeholder : Placeholder ;
56
57
}
@@ -71,6 +72,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
71
72
imgError : false ,
72
73
imgLoaded : false ,
73
74
hover : false ,
75
+ focus : false ,
74
76
showImage : SettingsStore . getValue ( "showImages" ) ,
75
77
placeholder : Placeholder . NoImage ,
76
78
} ;
@@ -120,30 +122,29 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
120
122
}
121
123
} ;
122
124
123
- protected onImageEnter = ( e : React . MouseEvent < HTMLImageElement > ) : void => {
124
- this . setState ( { hover : true } ) ;
125
-
126
- if (
125
+ private get shouldAutoplay ( ) : boolean {
126
+ return ! (
127
127
! this . state . contentUrl ||
128
128
! this . state . showImage ||
129
129
! this . state . isAnimated ||
130
130
SettingsStore . getValue ( "autoplayGifs" )
131
- ) {
132
- return ;
133
- }
134
- const imgElement = e . currentTarget ;
135
- imgElement . src = this . state . contentUrl ;
131
+ ) ;
132
+ }
133
+
134
+ protected onImageEnter = ( ) : void => {
135
+ this . setState ( { hover : true } ) ;
136
136
} ;
137
137
138
- protected onImageLeave = ( e : React . MouseEvent < HTMLImageElement > ) : void => {
138
+ protected onImageLeave = ( ) : void => {
139
139
this . setState ( { hover : false } ) ;
140
+ } ;
140
141
141
- const url = this . state . thumbUrl ?? this . state . contentUrl ;
142
- if ( ! url || ! this . state . showImage || ! this . state . isAnimated || SettingsStore . getValue ( "autoplayGifs" ) ) {
143
- return ;
144
- }
145
- const imgElement = e . currentTarget ;
146
- imgElement . src = url ;
142
+ private onFocus = ( ) : void => {
143
+ this . setState ( { focus : true } ) ;
144
+ } ;
145
+
146
+ private onBlur = ( ) : void => {
147
+ this . setState ( { focus : false } ) ;
147
148
} ;
148
149
149
150
private reconnectedListener = createReconnectedListener ( ( ) : void => {
@@ -470,14 +471,20 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
470
471
471
472
let showPlaceholder = Boolean ( placeholder ) ;
472
473
474
+ const hoverOrFocus = this . state . hover || this . state . focus ;
473
475
if ( thumbUrl && ! this . state . imgError ) {
476
+ let url = thumbUrl ;
477
+ if ( hoverOrFocus && this . shouldAutoplay ) {
478
+ url = this . state . contentUrl ! ;
479
+ }
480
+
474
481
// Restrict the width of the thumbnail here, otherwise it will fill the container
475
482
// which has the same width as the timeline
476
483
// mx_MImageBody_thumbnail resizes img to exactly container size
477
484
img = (
478
485
< img
479
486
className = "mx_MImageBody_thumbnail"
480
- src = { thumbUrl }
487
+ src = { url }
481
488
ref = { this . image }
482
489
alt = { content . body }
483
490
onError = { this . onImageError }
@@ -493,13 +500,13 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
493
500
showPlaceholder = false ; // because we're hiding the image, so don't show the placeholder.
494
501
}
495
502
496
- if ( this . state . isAnimated && ! SettingsStore . getValue ( "autoplayGifs" ) && ! this . state . hover ) {
503
+ if ( this . state . isAnimated && ! SettingsStore . getValue ( "autoplayGifs" ) && ! hoverOrFocus ) {
497
504
// XXX: Arguably we may want a different label when the animated image is WEBP and not GIF
498
505
gifLabel = < p className = "mx_MImageBody_gifLabel" > GIF</ p > ;
499
506
}
500
507
501
508
let banner : ReactNode | undefined ;
502
- if ( this . state . showImage && this . state . hover ) {
509
+ if ( this . state . showImage && hoverOrFocus ) {
503
510
banner = this . getBanner ( content ) ;
504
511
}
505
512
@@ -568,7 +575,13 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
568
575
protected wrapImage ( contentUrl : string | null | undefined , children : JSX . Element ) : ReactNode {
569
576
if ( contentUrl ) {
570
577
return (
571
- < a href = { contentUrl } target = { this . props . forExport ? "_blank" : undefined } onClick = { this . onClick } >
578
+ < a
579
+ href = { contentUrl }
580
+ target = { this . props . forExport ? "_blank" : undefined }
581
+ onClick = { this . onClick }
582
+ onFocus = { this . onFocus }
583
+ onBlur = { this . onBlur }
584
+ >
572
585
{ children }
573
586
</ a >
574
587
) ;
@@ -657,17 +670,14 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
657
670
}
658
671
659
672
interface PlaceholderIProps {
660
- hover ?: boolean ;
661
673
maxWidth ?: number ;
662
674
}
663
675
664
676
export class HiddenImagePlaceholder extends React . PureComponent < PlaceholderIProps > {
665
677
public render ( ) : React . ReactNode {
666
678
const maxWidth = this . props . maxWidth ? this . props . maxWidth + "px" : null ;
667
- let className = "mx_HiddenImagePlaceholder" ;
668
- if ( this . props . hover ) className += " mx_HiddenImagePlaceholder_hover" ;
669
679
return (
670
- < div className = { className } style = { { maxWidth : `min(100%, ${ maxWidth } px)` } } >
680
+ < div className = "mx_HiddenImagePlaceholder" style = { { maxWidth : `min(100%, ${ maxWidth } px)` } } >
671
681
< div className = "mx_HiddenImagePlaceholder_button" >
672
682
< span className = "mx_HiddenImagePlaceholder_eye" />
673
683
< span > { _t ( "timeline|m.image|show_image" ) } </ span >
0 commit comments