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

Do not preload encrypted videos|images unless autoplay or thumbnailing is on #5352

Merged
merged 4 commits into from
Oct 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions src/components/views/messages/MImageBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export default class MImageBody extends React.Component {
showImage() {
localStorage.setItem("mx_ShowImage_" + this.props.mxEvent.getId(), "true");
this.setState({showImage: true});
this._downloadImage();
}

onClick(ev) {
Expand Down Expand Up @@ -253,10 +254,7 @@ export default class MImageBody extends React.Component {
}
}

componentDidMount() {
this.unmounted = false;
this.context.on('sync', this.onClientSync);

_downloadImage() {
const content = this.props.mxEvent.getContent();
if (content.file !== undefined && this.state.decryptedUrl === null) {
let thumbnailPromise = Promise.resolve(null);
Expand Down Expand Up @@ -289,9 +287,18 @@ export default class MImageBody extends React.Component {
});
});
}
}

componentDidMount() {
this.unmounted = false;
this.context.on('sync', this.onClientSync);

const showImage = this.state.showImage ||
localStorage.getItem("mx_ShowImage_" + this.props.mxEvent.getId()) === "true";

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,41 @@ limitations under the License.
*/

import React from 'react';
import PropTypes from 'prop-types';
import MFileBody from './MFileBody';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { decryptFile } from '../../../utils/DecryptFile';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
import InlineSpinner from '../elements/InlineSpinner';

export default class MVideoBody extends React.Component {
static propTypes = {
/* the MatrixEvent to show */
mxEvent: PropTypes.object.isRequired,
interface IProps {
/* the MatrixEvent to show */
mxEvent: any;
/* called when the video has loaded */
onHeightChanged: () => void;
}

/* called when the video has loaded */
onHeightChanged: PropTypes.func.isRequired,
};
interface IState {
decryptedUrl: string|null,
decryptedThumbnailUrl: string|null,
decryptedBlob: Blob|null,
error: any|null,
fetchingData: boolean,
}

state = {
decryptedUrl: null,
decryptedThumbnailUrl: null,
decryptedBlob: null,
error: null,
};
export default class MVideoBody extends React.PureComponent<IProps, IState> {
constructor(props) {
super(props);
this.state = {
fetchingData: false,
decryptedUrl: null,
decryptedThumbnailUrl: null,
decryptedBlob: null,
error: null,
}
}

thumbScale(fullWidth, fullHeight, thumbWidth, thumbHeight) {
thumbScale(fullWidth: number, fullHeight: number, thumbWidth: number, thumbHeight: number) {
if (!fullWidth || !fullHeight) {
// Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
// log this because it's spammy
Expand All @@ -61,7 +71,7 @@ export default class MVideoBody extends React.Component {
}
}

_getContentUrl() {
_getContentUrl(): string|null {
const content = this.props.mxEvent.getContent();
if (content.file !== undefined) {
return this.state.decryptedUrl;
Expand All @@ -70,7 +80,7 @@ export default class MVideoBody extends React.Component {
}
}

_getThumbUrl() {
_getThumbUrl(): string|null {
const content = this.props.mxEvent.getContent();
if (content.file !== undefined) {
return this.state.decryptedThumbnailUrl;
Expand All @@ -81,7 +91,8 @@ export default class MVideoBody extends React.Component {
}
}

componentDidMount() {
async componentDidMount() {
const autoplay = SettingsStore.getValue("autoplayGifsAndVideos") as boolean;
const content = this.props.mxEvent.getContent();
if (content.file !== undefined && this.state.decryptedUrl === null) {
let thumbnailPromise = Promise.resolve(null);
Expand All @@ -92,26 +103,33 @@ export default class MVideoBody extends React.Component {
return URL.createObjectURL(blob);
});
}
let decryptedBlob;
thumbnailPromise.then((thumbnailUrl) => {
return decryptFile(content.file).then(function(blob) {
decryptedBlob = blob;
return URL.createObjectURL(blob);
}).then((contentUrl) => {
try {
const thumbnailUrl = await thumbnailPromise;
if (autoplay) {
console.log("Preloading video");
const decryptedBlob = await decryptFile(content.file);
const contentUrl = URL.createObjectURL(decryptedBlob);
this.setState({
decryptedUrl: contentUrl,
decryptedThumbnailUrl: thumbnailUrl,
decryptedBlob: decryptedBlob,
});
this.props.onHeightChanged();
});
}).catch((err) => {
} else {
console.log("NOT preloading video");
this.setState({
decryptedUrl: null,
decryptedThumbnailUrl: thumbnailUrl,
decryptedBlob: null,
});
}
} catch (err) {
console.warn("Unable to decrypt attachment: ", err);
// Set a placeholder image when we can't decrypt the image.
this.setState({
error: err,
});
});
}
}
}

Expand All @@ -124,8 +142,35 @@ export default class MVideoBody extends React.Component {
}
}

async _videoOnPlay() {
const autoplay = SettingsStore.getValue("autoplayGifsAndVideos") as boolean;
if (autoplay || this.state.decryptedUrl || this.state.fetchingData || this.state.error) {
// The video has or will have the data.
return;
}
this.setState({
// To stop subsequent download attempts
fetchingData: true,
});
const content = this.props.mxEvent.getContent();
if (!content.file) {
this.setState({
error: "No file given in content",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not be i18n'd?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. I'd be happy for a commit to fix it to land directly on develop

});
Comment on lines +157 to +159
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be collapsed to just 1 line, but it ultimately is personal preference.

return;
}
const decryptedBlob = await decryptFile(content.file);
const contentUrl = URL.createObjectURL(decryptedBlob);
this.setState({
decryptedUrl: contentUrl,
decryptedBlob: decryptedBlob,
});
this.props.onHeightChanged();
}

render() {
const content = this.props.mxEvent.getContent();
const autoplay = SettingsStore.getValue("autoplayGifsAndVideos");

if (this.state.error !== null) {
return (
Expand All @@ -136,7 +181,8 @@ export default class MVideoBody extends React.Component {
);
}

if (content.file !== undefined && this.state.decryptedUrl === null) {
// Important: If we aren't autoplaying and we haven't decrypred it yet, show a video with a poster.
if (content.file !== undefined && this.state.decryptedUrl === null && autoplay) {
// Need to decrypt the attachment
// The attachment is decrypted in componentDidMount.
// For now add an img tag with a spinner.
Expand All @@ -151,7 +197,6 @@ export default class MVideoBody extends React.Component {

const contentUrl = this._getContentUrl();
const thumbUrl = this._getThumbUrl();
const autoplay = SettingsStore.getValue("autoplayGifsAndVideos");
let height = null;
let width = null;
let poster = null;
Expand All @@ -170,9 +215,9 @@ export default class MVideoBody extends React.Component {
}
return (
<span className="mx_MVideoBody">
<video className="mx_MVideoBody" src={contentUrl} alt={content.body}
<video className="mx_MVideoBody" src={contentUrl} title={content.body}
controls preload={preload} muted={autoplay} autoPlay={autoplay}
height={height} width={width} poster={poster}>
height={height} width={width} poster={poster} onPlay={this._videoOnPlay.bind(this)}>
</video>
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} />
</span>
Expand Down