mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 21:24:59 +08:00
Convert images, audio, and voice messages over to the new helper
This commit is contained in:
parent
040802e29f
commit
5fce0ccd9d
@ -18,22 +18,22 @@ import React from "react";
|
|||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { Playback } from "../../../voice/Playback";
|
import { Playback } from "../../../voice/Playback";
|
||||||
import MFileBody from "./MFileBody";
|
|
||||||
import InlineSpinner from '../elements/InlineSpinner';
|
import InlineSpinner from '../elements/InlineSpinner';
|
||||||
import { _t } from "../../../languageHandler";
|
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 AudioPlayer from "../audio_messages/AudioPlayer";
|
||||||
|
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
|
||||||
|
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
|
||||||
|
import { TileShape } from "../rooms/EventTile";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
tileShape?: TileShape;
|
||||||
|
mediaEventHelper: MediaEventHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
error?: Error;
|
error?: Error;
|
||||||
playback?: Playback;
|
playback?: Playback;
|
||||||
decryptedBlob?: Blob;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.messages.MAudioBody")
|
@replaceableComponent("views.messages.MAudioBody")
|
||||||
@ -46,33 +46,34 @@ export default class MAudioBody extends React.PureComponent<IProps, IState> {
|
|||||||
|
|
||||||
public async componentDidMount() {
|
public async componentDidMount() {
|
||||||
let buffer: ArrayBuffer;
|
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);
|
try {
|
||||||
|
const blob = await this.props.mediaEventHelper.sourceBlob.value;
|
||||||
buffer = await blob.arrayBuffer();
|
buffer = await blob.arrayBuffer();
|
||||||
this.setState({ decryptedBlob: blob });
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setState({ error: e });
|
this.setState({ error: e });
|
||||||
console.warn("Unable to decrypt audio message", e);
|
console.warn("Unable to decrypt audio message", e);
|
||||||
return; // stop processing the audio file
|
return; // stop processing the audio file
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
buffer = await media.downloadSource().then(r => r.blob()).then(r => r.arrayBuffer());
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setState({ error: e });
|
this.setState({ error: e });
|
||||||
console.warn("Unable to download audio message", e);
|
console.warn("Unable to decrypt/download audio message", e);
|
||||||
return; // stop processing the audio file
|
return; // stop processing the audio file
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// We should have a buffer to work with now: let's set it up
|
// 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);
|
playback.clockInfo.populatePlaceholdersFrom(this.props.mxEvent);
|
||||||
this.setState({ playback });
|
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() {
|
public componentWillUnmount() {
|
||||||
@ -103,7 +104,7 @@ export default class MAudioBody extends React.PureComponent<IProps, IState> {
|
|||||||
return (
|
return (
|
||||||
<span className="mx_MAudioBody">
|
<span className="mx_MAudioBody">
|
||||||
<AudioPlayer playback={this.state.playback} mediaName={this.props.mxEvent.getContent().body} />
|
<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>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015 - 2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2018 New Vector Ltd
|
|
||||||
Copyright 2018, 2019 Michael Telatynski <7t3chguy@gmail.com>
|
Copyright 2018, 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -21,7 +20,6 @@ import { Blurhash } from "react-blurhash";
|
|||||||
|
|
||||||
import MFileBody from './MFileBody';
|
import MFileBody from './MFileBody';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
import { decryptFile } from '../../../utils/DecryptFile';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
@ -34,6 +32,7 @@ import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
|||||||
import { IMediaEventContent } from '../../../customisations/models/IMediaEventContent';
|
import { IMediaEventContent } from '../../../customisations/models/IMediaEventContent';
|
||||||
import ImageView from '../elements/ImageView';
|
import ImageView from '../elements/ImageView';
|
||||||
import { SyncState } from 'matrix-js-sdk/src/sync.api';
|
import { SyncState } from 'matrix-js-sdk/src/sync.api';
|
||||||
|
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
|
||||||
|
|
||||||
export interface IProps {
|
export interface IProps {
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
@ -46,6 +45,7 @@ export interface IProps {
|
|||||||
|
|
||||||
/* the permalinkCreator */
|
/* the permalinkCreator */
|
||||||
permalinkCreator?: RoomPermalinkCreator;
|
permalinkCreator?: RoomPermalinkCreator;
|
||||||
|
mediaEventHelper: MediaEventHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
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();
|
const content = this.props.mxEvent.getContent();
|
||||||
if (content.file !== undefined && this.state.decryptedUrl === null) {
|
if (this.props.mediaEventHelper.media.isEncrypted && this.state.decryptedUrl === null) {
|
||||||
let thumbnailPromise = Promise.resolve(null);
|
try {
|
||||||
if (content.info && content.info.thumbnail_file) {
|
const thumbnailUrl = await this.props.mediaEventHelper.thumbnailUrl.value;
|
||||||
thumbnailPromise = decryptFile(
|
|
||||||
content.info.thumbnail_file,
|
|
||||||
).then(function(blob) {
|
|
||||||
return URL.createObjectURL(blob);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
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({
|
this.setState({
|
||||||
decryptedUrl: contentUrl,
|
decryptedUrl: await this.props.mediaEventHelper.sourceUrl.value,
|
||||||
decryptedThumbnailUrl: thumbnailUrl,
|
decryptedThumbnailUrl: thumbnailUrl,
|
||||||
decryptedBlob: decryptedBlob,
|
decryptedBlob: await this.props.mediaEventHelper.sourceBlob.value,
|
||||||
});
|
});
|
||||||
});
|
} catch (err) {
|
||||||
}).catch((err) => {
|
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
console.warn("Unable to decrypt attachment: ", err);
|
console.warn("Unable to decrypt attachment: ", err);
|
||||||
// Set a placeholder image when we can't decrypt the image.
|
// Set a placeholder image when we can't decrypt the image.
|
||||||
this.setState({
|
this.setState({
|
||||||
error: err,
|
error: err,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,10 +286,10 @@ export default class MImageBody extends React.Component<IProps, IState> {
|
|||||||
localStorage.getItem("mx_ShowImage_" + this.props.mxEvent.getId()) === "true";
|
localStorage.getItem("mx_ShowImage_" + this.props.mxEvent.getId()) === "true";
|
||||||
|
|
||||||
if (showImage) {
|
if (showImage) {
|
||||||
// Don't download anything becaue we don't want to display anything.
|
// noinspection JSIgnoredPromiseFromCall
|
||||||
this.downloadImage();
|
this.downloadImage();
|
||||||
this.setState({ showImage: true });
|
this.setState({ showImage: true });
|
||||||
}
|
} // else don't download anything because we don't want to display anything.
|
||||||
|
|
||||||
this._afterComponentDidMount();
|
this._afterComponentDidMount();
|
||||||
}
|
}
|
||||||
@ -316,13 +302,6 @@ export default class MImageBody extends React.Component<IProps, IState> {
|
|||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
this.context.removeListener('sync', this.onClientSync);
|
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(
|
protected messageContent(
|
||||||
|
@ -15,72 +15,16 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { Playback } from "../../../voice/Playback";
|
|
||||||
import MFileBody from "./MFileBody";
|
|
||||||
import InlineSpinner from '../elements/InlineSpinner';
|
import InlineSpinner from '../elements/InlineSpinner';
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import { mediaFromContent } from "../../../customisations/Media";
|
|
||||||
import { decryptFile } from "../../../utils/DecryptFile";
|
|
||||||
import RecordingPlayback from "../audio_messages/RecordingPlayback";
|
import RecordingPlayback from "../audio_messages/RecordingPlayback";
|
||||||
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
|
import MAudioBody from "./MAudioBody";
|
||||||
import { TileShape } from "../rooms/EventTile";
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
mxEvent: MatrixEvent;
|
|
||||||
tileShape?: TileShape;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IState {
|
|
||||||
error?: Error;
|
|
||||||
playback?: Playback;
|
|
||||||
decryptedBlob?: Blob;
|
|
||||||
}
|
|
||||||
|
|
||||||
@replaceableComponent("views.messages.MVoiceMessageBody")
|
@replaceableComponent("views.messages.MVoiceMessageBody")
|
||||||
export default class MVoiceMessageBody extends React.PureComponent<IProps, IState> {
|
export default class MVoiceMessageBody extends MAudioBody {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {};
|
// A voice message is an audio file but rendered in a special way.
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
if (this.state.error) {
|
if (this.state.error) {
|
||||||
@ -106,7 +50,7 @@ export default class MVoiceMessageBody extends React.PureComponent<IProps, IStat
|
|||||||
return (
|
return (
|
||||||
<span className="mx_MVoiceMessageBody">
|
<span className="mx_MVoiceMessageBody">
|
||||||
<RecordingPlayback playback={this.state.playback} tileShape={this.props.tileShape} />
|
<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>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user