Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 0adb920

Browse files
authored
Merge pull request #5352 from matrix-org/hs/mvideobody
Do not preload encrypted videos|images unless autoplay or thumbnailing is on
2 parents 9146992 + 7eb54cc commit 0adb920

File tree

2 files changed

+89
-37
lines changed

2 files changed

+89
-37
lines changed

src/components/views/messages/MImageBody.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export default class MImageBody extends React.Component {
8585
showImage() {
8686
localStorage.setItem("mx_ShowImage_" + this.props.mxEvent.getId(), "true");
8787
this.setState({showImage: true});
88+
this._downloadImage();
8889
}
8990

9091
onClick(ev) {
@@ -253,10 +254,7 @@ export default class MImageBody extends React.Component {
253254
}
254255
}
255256

256-
componentDidMount() {
257-
this.unmounted = false;
258-
this.context.on('sync', this.onClientSync);
259-
257+
_downloadImage() {
260258
const content = this.props.mxEvent.getContent();
261259
if (content.file !== undefined && this.state.decryptedUrl === null) {
262260
let thumbnailPromise = Promise.resolve(null);
@@ -289,9 +287,18 @@ export default class MImageBody extends React.Component {
289287
});
290288
});
291289
}
290+
}
291+
292+
componentDidMount() {
293+
this.unmounted = false;
294+
this.context.on('sync', this.onClientSync);
295+
296+
const showImage = this.state.showImage ||
297+
localStorage.getItem("mx_ShowImage_" + this.props.mxEvent.getId()) === "true";
292298

293-
// Remember that the user wanted to show this particular image
294-
if (!this.state.showImage && localStorage.getItem("mx_ShowImage_" + this.props.mxEvent.getId()) === "true") {
299+
if (showImage) {
300+
// Don't download anything becaue we don't want to display anything.
301+
this._downloadImage();
295302
this.setState({showImage: true});
296303
}
297304

src/components/views/messages/MVideoBody.js renamed to src/components/views/messages/MVideoBody.tsx

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,41 @@ limitations under the License.
1616
*/
1717

1818
import React from 'react';
19-
import PropTypes from 'prop-types';
2019
import MFileBody from './MFileBody';
2120
import {MatrixClientPeg} from '../../../MatrixClientPeg';
2221
import { decryptFile } from '../../../utils/DecryptFile';
2322
import { _t } from '../../../languageHandler';
2423
import SettingsStore from "../../../settings/SettingsStore";
2524
import 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

Comments
 (0)