@@ -16,31 +16,41 @@ limitations under the License.
1616*/
1717
1818import React from 'react' ;
19- import PropTypes from 'prop-types' ;
2019import MFileBody from './MFileBody' ;
2120import { MatrixClientPeg } from '../../../MatrixClientPeg' ;
2221import { decryptFile } from '../../../utils/DecryptFile' ;
2322import { _t } from '../../../languageHandler' ;
2423import SettingsStore from "../../../settings/SettingsStore" ;
2524import InlineSpinner from '../elements/InlineSpinner' ;
2625
27- export default class MVideoBody extends React . Component {
28- static propTypes = {
29- /* the MatrixEvent to show */
30- mxEvent : PropTypes . object . isRequired ,
26+ interface IProps {
27+ /* the MatrixEvent to show */
28+ mxEvent : any ;
29+ /* called when the video has loaded */
30+ onHeightChanged : ( ) => void ;
31+ }
3132
32- /* called when the video has loaded */
33- onHeightChanged : PropTypes . func . isRequired ,
34- } ;
33+ interface IState {
34+ decryptedUrl : string | null ,
35+ decryptedThumbnailUrl : string | null ,
36+ decryptedBlob : Blob | null ,
37+ error : any | null ,
38+ fetchingData : boolean ,
39+ }
3540
36- state = {
37- decryptedUrl : null ,
38- decryptedThumbnailUrl : null ,
39- decryptedBlob : null ,
40- error : null ,
41- } ;
41+ export default class MVideoBody extends React . PureComponent < IProps , IState > {
42+ constructor ( props ) {
43+ super ( props ) ;
44+ this . state = {
45+ fetchingData : false ,
46+ decryptedUrl : null ,
47+ decryptedThumbnailUrl : null ,
48+ decryptedBlob : null ,
49+ error : null ,
50+ }
51+ }
4252
43- thumbScale ( fullWidth , fullHeight , thumbWidth , thumbHeight ) {
53+ thumbScale ( fullWidth : number , fullHeight : number , thumbWidth : number , thumbHeight : number ) {
4454 if ( ! fullWidth || ! fullHeight ) {
4555 // Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
4656 // log this because it's spammy
@@ -61,7 +71,7 @@ export default class MVideoBody extends React.Component {
6171 }
6272 }
6373
64- _getContentUrl ( ) {
74+ _getContentUrl ( ) : string | null {
6575 const content = this . props . mxEvent . getContent ( ) ;
6676 if ( content . file !== undefined ) {
6777 return this . state . decryptedUrl ;
@@ -70,7 +80,7 @@ export default class MVideoBody extends React.Component {
7080 }
7181 }
7282
73- _getThumbUrl ( ) {
83+ _getThumbUrl ( ) : string | null {
7484 const content = this . props . mxEvent . getContent ( ) ;
7585 if ( content . file !== undefined ) {
7686 return this . state . decryptedThumbnailUrl ;
@@ -81,7 +91,8 @@ export default class MVideoBody extends React.Component {
8191 }
8292 }
8393
84- componentDidMount ( ) {
94+ async componentDidMount ( ) {
95+ const autoplay = SettingsStore . getValue ( "autoplayGifsAndVideos" ) as boolean ;
8596 const content = this . props . mxEvent . getContent ( ) ;
8697 if ( content . file !== undefined && this . state . decryptedUrl === null ) {
8798 let thumbnailPromise = Promise . resolve ( null ) ;
@@ -92,26 +103,33 @@ export default class MVideoBody extends React.Component {
92103 return URL . createObjectURL ( blob ) ;
93104 } ) ;
94105 }
95- let decryptedBlob ;
96- thumbnailPromise . then ( ( thumbnailUrl ) => {
97- return decryptFile ( content . file ) . then ( function ( blob ) {
98- decryptedBlob = blob ;
99- return URL . createObjectURL ( blob ) ;
100- } ) . then ( ( contentUrl ) => {
106+ try {
107+ const thumbnailUrl = await thumbnailPromise ;
108+ if ( autoplay ) {
109+ console . log ( "Preloading video" ) ;
110+ const decryptedBlob = await decryptFile ( content . file ) ;
111+ const contentUrl = URL . createObjectURL ( decryptedBlob ) ;
101112 this . setState ( {
102113 decryptedUrl : contentUrl ,
103114 decryptedThumbnailUrl : thumbnailUrl ,
104115 decryptedBlob : decryptedBlob ,
105116 } ) ;
106117 this . props . onHeightChanged ( ) ;
107- } ) ;
108- } ) . catch ( ( err ) => {
118+ } else {
119+ console . log ( "NOT preloading video" ) ;
120+ this . setState ( {
121+ decryptedUrl : null ,
122+ decryptedThumbnailUrl : thumbnailUrl ,
123+ decryptedBlob : null ,
124+ } ) ;
125+ }
126+ } catch ( err ) {
109127 console . warn ( "Unable to decrypt attachment: " , err ) ;
110128 // Set a placeholder image when we can't decrypt the image.
111129 this . setState ( {
112130 error : err ,
113131 } ) ;
114- } ) ;
132+ }
115133 }
116134 }
117135
@@ -124,8 +142,35 @@ export default class MVideoBody extends React.Component {
124142 }
125143 }
126144
145+ async _videoOnPlay ( ) {
146+ const autoplay = SettingsStore . getValue ( "autoplayGifsAndVideos" ) as boolean ;
147+ if ( autoplay || this . state . decryptedUrl || this . state . fetchingData || this . state . error ) {
148+ // The video has or will have the data.
149+ return ;
150+ }
151+ this . setState ( {
152+ // To stop subsequent download attempts
153+ fetchingData : true ,
154+ } ) ;
155+ const content = this . props . mxEvent . getContent ( ) ;
156+ if ( ! content . file ) {
157+ this . setState ( {
158+ error : "No file given in content" ,
159+ } ) ;
160+ return ;
161+ }
162+ const decryptedBlob = await decryptFile ( content . file ) ;
163+ const contentUrl = URL . createObjectURL ( decryptedBlob ) ;
164+ this . setState ( {
165+ decryptedUrl : contentUrl ,
166+ decryptedBlob : decryptedBlob ,
167+ } ) ;
168+ this . props . onHeightChanged ( ) ;
169+ }
170+
127171 render ( ) {
128172 const content = this . props . mxEvent . getContent ( ) ;
173+ const autoplay = SettingsStore . getValue ( "autoplayGifsAndVideos" ) ;
129174
130175 if ( this . state . error !== null ) {
131176 return (
@@ -136,7 +181,8 @@ export default class MVideoBody extends React.Component {
136181 ) ;
137182 }
138183
139- if ( content . file !== undefined && this . state . decryptedUrl === null ) {
184+ // Important: If we aren't autoplaying and we haven't decrypred it yet, show a video with a poster.
185+ if ( content . file !== undefined && this . state . decryptedUrl === null && autoplay ) {
140186 // Need to decrypt the attachment
141187 // The attachment is decrypted in componentDidMount.
142188 // For now add an img tag with a spinner.
@@ -151,7 +197,6 @@ export default class MVideoBody extends React.Component {
151197
152198 const contentUrl = this . _getContentUrl ( ) ;
153199 const thumbUrl = this . _getThumbUrl ( ) ;
154- const autoplay = SettingsStore . getValue ( "autoplayGifsAndVideos" ) ;
155200 let height = null ;
156201 let width = null ;
157202 let poster = null ;
@@ -170,9 +215,9 @@ export default class MVideoBody extends React.Component {
170215 }
171216 return (
172217 < span className = "mx_MVideoBody" >
173- < video className = "mx_MVideoBody" src = { contentUrl } alt = { content . body }
218+ < video className = "mx_MVideoBody" src = { contentUrl } title = { content . body }
174219 controls preload = { preload } muted = { autoplay } autoPlay = { autoplay }
175- height = { height } width = { width } poster = { poster } >
220+ height = { height } width = { width } poster = { poster } onPlay = { this . _videoOnPlay . bind ( this ) } >
176221 </ video >
177222 < MFileBody { ...this . props } decryptedBlob = { this . state . decryptedBlob } />
178223 </ span >
0 commit comments