Convert images, audio, and voice messages over to the new helper

pull/21833/head
Travis Ralston 2021-07-15 16:37:48 -06:00
parent 040802e29f
commit 5fce0ccd9d
3 changed files with 41 additions and 117 deletions

View File

@ -18,22 +18,22 @@ import React from "react";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { Playback } from "../../../voice/Playback";
import MFileBody from "./MFileBody";
import InlineSpinner from '../elements/InlineSpinner';
import { _t } from "../../../languageHandler";
import { mediaFromContent } from "../../../customisations/Media";
import { decryptFile } from "../../../utils/DecryptFile";
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
import AudioPlayer from "../audio_messages/AudioPlayer";
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
import { TileShape } from "../rooms/EventTile";
interface IProps {
mxEvent: MatrixEvent;
tileShape?: TileShape;
mediaEventHelper: MediaEventHelper;
}
interface IState {
error?: Error;
playback?: Playback;
decryptedBlob?: Blob;
}
@replaceableComponent("views.messages.MAudioBody")
@ -46,33 +46,34 @@ export default class MAudioBody extends React.PureComponent<IProps, IState> {
public async componentDidMount() {
let buffer: ArrayBuffer;
const content: IMediaEventContent = this.props.mxEvent.getContent();
const media = mediaFromContent(content);
if (media.isEncrypted) {
try {
try {
const blob = await decryptFile(content.file);
const blob = await this.props.mediaEventHelper.sourceBlob.value;
buffer = await blob.arrayBuffer();
this.setState({ decryptedBlob: blob });
} catch (e) {
this.setState({ error: e });
console.warn("Unable to decrypt audio message", e);
return; // stop processing the audio file
}
} else {
try {
buffer = await media.downloadSource().then(r => r.blob()).then(r => r.arrayBuffer());
} catch (e) {
this.setState({ error: e });
console.warn("Unable to download audio message", e);
return; // stop processing the audio file
}
} catch (e) {
this.setState({ error: e });
console.warn("Unable to decrypt/download audio message", e);
return; // stop processing the audio file
}
// We should have a buffer to work with now: let's set it up
const playback = new Playback(buffer);
// Note: we don't actually need a waveform to render an audio event, but voice messages do.
const content = this.props.mxEvent.getContent<IMediaEventContent>();
const waveform = content?.["org.matrix.msc1767.audio"]?.waveform?.map(p => p / 1024);
// We should have a buffer to work with now: let's set it up
const playback = new Playback(buffer, waveform);
playback.clockInfo.populatePlaceholdersFrom(this.props.mxEvent);
this.setState({ playback });
// Note: the RecordingPlayback component will handle preparing the Playback class for us.
// Note: the components later on will handle preparing the Playback class for us.
}
public componentWillUnmount() {
@ -103,7 +104,7 @@ export default class MAudioBody extends React.PureComponent<IProps, IState> {
return (
<span className="mx_MAudioBody">
<AudioPlayer playback={this.state.playback} mediaName={this.props.mxEvent.getContent().body} />
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />
{/*<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />*/}
</span>
);
}

View File

@ -1,6 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
Copyright 2015 - 2021 The Matrix.org Foundation C.I.C.
Copyright 2018, 2019 Michael Telatynski <7t3chguy@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
@ -21,7 +20,6 @@ import { Blurhash } from "react-blurhash";
import MFileBody from './MFileBody';
import Modal from '../../../Modal';
import { decryptFile } from '../../../utils/DecryptFile';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
@ -34,6 +32,7 @@ import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
import { IMediaEventContent } from '../../../customisations/models/IMediaEventContent';
import ImageView from '../elements/ImageView';
import { SyncState } from 'matrix-js-sdk/src/sync.api';
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
export interface IProps {
/* the MatrixEvent to show */
@ -46,6 +45,7 @@ export interface IProps {
/* the permalinkCreator */
permalinkCreator?: RoomPermalinkCreator;
mediaEventHelper: MediaEventHelper;
}
interface IState {
@ -257,38 +257,24 @@ export default class MImageBody extends React.Component<IProps, IState> {
}
}
private downloadImage(): void {
private async downloadImage() {
const content = this.props.mxEvent.getContent();
if (content.file !== undefined && this.state.decryptedUrl === null) {
let thumbnailPromise = Promise.resolve(null);
if (content.info && content.info.thumbnail_file) {
thumbnailPromise = decryptFile(
content.info.thumbnail_file,
).then(function(blob) {
return URL.createObjectURL(blob);
if (this.props.mediaEventHelper.media.isEncrypted && this.state.decryptedUrl === null) {
try {
const thumbnailUrl = await this.props.mediaEventHelper.thumbnailUrl.value;
this.setState({
decryptedUrl: await this.props.mediaEventHelper.sourceUrl.value,
decryptedThumbnailUrl: thumbnailUrl,
decryptedBlob: await this.props.mediaEventHelper.sourceBlob.value,
});
}
let decryptedBlob;
thumbnailPromise.then((thumbnailUrl) => {
return decryptFile(content.file).then(function(blob) {
decryptedBlob = blob;
return URL.createObjectURL(blob);
}).then((contentUrl) => {
if (this.unmounted) return;
this.setState({
decryptedUrl: contentUrl,
decryptedThumbnailUrl: thumbnailUrl,
decryptedBlob: decryptedBlob,
});
});
}).catch((err) => {
} catch (err) {
if (this.unmounted) return;
console.warn("Unable to decrypt attachment: ", err);
// Set a placeholder image when we can't decrypt the image.
this.setState({
error: err,
});
});
}
}
}
@ -300,10 +286,10 @@ export default class MImageBody extends React.Component<IProps, IState> {
localStorage.getItem("mx_ShowImage_" + this.props.mxEvent.getId()) === "true";
if (showImage) {
// Don't download anything becaue we don't want to display anything.
// noinspection JSIgnoredPromiseFromCall
this.downloadImage();
this.setState({ showImage: true });
}
} // else don't download anything because we don't want to display anything.
this._afterComponentDidMount();
}
@ -316,13 +302,6 @@ export default class MImageBody extends React.Component<IProps, IState> {
componentWillUnmount() {
this.unmounted = true;
this.context.removeListener('sync', this.onClientSync);
if (this.state.decryptedUrl) {
URL.revokeObjectURL(this.state.decryptedUrl);
}
if (this.state.decryptedThumbnailUrl) {
URL.revokeObjectURL(this.state.decryptedThumbnailUrl);
}
}
protected messageContent(

View File

@ -15,72 +15,16 @@ limitations under the License.
*/
import React from "react";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { Playback } from "../../../voice/Playback";
import MFileBody from "./MFileBody";
import InlineSpinner from '../elements/InlineSpinner';
import { _t } from "../../../languageHandler";
import { mediaFromContent } from "../../../customisations/Media";
import { decryptFile } from "../../../utils/DecryptFile";
import RecordingPlayback from "../audio_messages/RecordingPlayback";
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
import { TileShape } from "../rooms/EventTile";
interface IProps {
mxEvent: MatrixEvent;
tileShape?: TileShape;
}
interface IState {
error?: Error;
playback?: Playback;
decryptedBlob?: Blob;
}
import MAudioBody from "./MAudioBody";
@replaceableComponent("views.messages.MVoiceMessageBody")
export default class MVoiceMessageBody extends React.PureComponent<IProps, IState> {
constructor(props: IProps) {
super(props);
export default class MVoiceMessageBody extends MAudioBody {
this.state = {};
}
public async componentDidMount() {
let buffer: ArrayBuffer;
const content: IMediaEventContent = this.props.mxEvent.getContent();
const media = mediaFromContent(content);
if (media.isEncrypted) {
try {
const blob = await decryptFile(content.file);
buffer = await blob.arrayBuffer();
this.setState({ decryptedBlob: blob });
} catch (e) {
this.setState({ error: e });
console.warn("Unable to decrypt voice message", e);
return; // stop processing the audio file
}
} else {
try {
buffer = await media.downloadSource().then(r => r.blob()).then(r => r.arrayBuffer());
} catch (e) {
this.setState({ error: e });
console.warn("Unable to download voice message", e);
return; // stop processing the audio file
}
}
const waveform = content?.["org.matrix.msc1767.audio"]?.waveform?.map(p => p / 1024);
// We should have a buffer to work with now: let's set it up
const playback = new Playback(buffer, waveform);
this.setState({ playback });
// Note: the RecordingPlayback component will handle preparing the Playback class for us.
}
public componentWillUnmount() {
this.state.playback?.destroy();
}
// A voice message is an audio file but rendered in a special way.
public render() {
if (this.state.error) {
@ -106,7 +50,7 @@ export default class MVoiceMessageBody extends React.PureComponent<IProps, IStat
return (
<span className="mx_MVoiceMessageBody">
<RecordingPlayback playback={this.state.playback} tileShape={this.props.tileShape} />
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />
{/*<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />*/}
</span>
);
}