Files
ThreadNet-Web/src/components/views/messages/MAudioBody.tsx
T
KerryandGitHub 7afc8c5869 Task - replace img icons with svg components (#8963)
* add role=presentation to backdrop panle image

* replace img icons with svg components in InviteDialog

* replace img icon with svg component

* img icons to svg icons in MImageBody

* remove log

* img icon to svg in SecurityRoomSettingsTab

* use shared error message for media message tiles

* remove nbsp

* dont snapshot test entire rtl render response

* use aria-describedby for uploadconfirm preview

* use aria-labelledby instead
2022-07-06 13:29:26 +02:00

131 lines
4.8 KiB
TypeScript

/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { Playback } from "../../../audio/Playback";
import InlineSpinner from '../elements/InlineSpinner';
import { _t } from "../../../languageHandler";
import AudioPlayer from "../audio_messages/AudioPlayer";
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
import MFileBody from "./MFileBody";
import { IBodyProps } from "./IBodyProps";
import { PlaybackManager } from "../../../audio/PlaybackManager";
import { isVoiceMessage } from "../../../utils/EventUtils";
import { PlaybackQueue } from "../../../audio/PlaybackQueue";
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
import MediaProcessingError from "./shared/MediaProcessingError";
interface IState {
error?: Error;
playback?: Playback;
}
export default class MAudioBody extends React.PureComponent<IBodyProps, IState> {
static contextType = RoomContext;
public context!: React.ContextType<typeof RoomContext>;
constructor(props: IBodyProps) {
super(props);
this.state = {};
}
public async componentDidMount() {
let buffer: ArrayBuffer;
try {
try {
const blob = await this.props.mediaEventHelper.sourceBlob.value;
buffer = await blob.arrayBuffer();
} catch (e) {
this.setState({ error: e });
logger.warn("Unable to decrypt audio message", e);
return; // stop processing the audio file
}
} catch (e) {
this.setState({ error: e });
logger.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
// 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 = PlaybackManager.instance.createPlaybackInstance(buffer, waveform);
playback.clockInfo.populatePlaceholdersFrom(this.props.mxEvent);
this.setState({ playback });
if (isVoiceMessage(this.props.mxEvent)) {
PlaybackQueue.forRoom(this.props.mxEvent.getRoomId()).unsortedEnqueue(this.props.mxEvent, playback);
}
// Note: the components later on will handle preparing the Playback class for us.
}
public componentWillUnmount() {
this.state.playback?.destroy();
}
protected get showFileBody(): boolean {
return this.context.timelineRenderingType !== TimelineRenderingType.Room &&
this.context.timelineRenderingType !== TimelineRenderingType.Pinned &&
this.context.timelineRenderingType !== TimelineRenderingType.Search;
}
public render() {
if (this.state.error) {
return (
<MediaProcessingError className="mx_MAudioBody">
{ _t("Error processing audio message") }
</MediaProcessingError>
);
}
if (this.props.forExport) {
const content = this.props.mxEvent.getContent();
// During export, the content url will point to the MSC, which will later point to a local url
const contentUrl = content.file?.url || content.url;
return (
<span className="mx_MAudioBody">
<audio src={contentUrl} controls />
</span>
);
}
if (!this.state.playback) {
return (
<span className="mx_MAudioBody">
<InlineSpinner />
</span>
);
}
// At this point we should have a playable state
return (
<span className="mx_MAudioBody">
<AudioPlayer playback={this.state.playback} mediaName={this.props.mxEvent.getContent().body} />
{ this.showFileBody && <MFileBody {...this.props} showGenericPlaceholder={false} /> }
</span>
);
}
}