Files
ThreadNet-Web/src/components/views/messages/MAudioBody.tsx
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

122 lines
4.6 KiB
TypeScript
Raw Normal View History

/*
2024-09-09 14:57:16 +01:00
Copyright 2024 New Vector Ltd.
Copyright 2021 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
2024-09-09 14:57:16 +01:00
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
2021-10-22 17:23:32 -05:00
import { logger } from "matrix-js-sdk/src/logger";
2025-02-05 13:25:06 +00:00
import { type IContent } from "matrix-js-sdk/src/matrix";
import { type MediaEventContent } from "matrix-js-sdk/src/types";
2021-10-22 17:23:32 -05:00
2025-02-05 13:25:06 +00:00
import { type Playback } from "../../../audio/Playback";
import InlineSpinner from "../elements/InlineSpinner";
2021-06-21 16:18:39 -06:00
import { _t } from "../../../languageHandler";
import AudioPlayer from "../audio_messages/AudioPlayer";
2021-07-16 10:57:14 -06:00
import MFileBody from "./MFileBody";
2025-02-05 13:25:06 +00:00
import { type IBodyProps } from "./IBodyProps";
import { PlaybackManager } from "../../../audio/PlaybackManager";
2021-08-30 14:20:03 -06:00
import { isVoiceMessage } from "../../../utils/EventUtils";
import { PlaybackQueue } from "../../../audio/PlaybackQueue";
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
import MediaProcessingError from "./shared/MediaProcessingError";
interface IState {
error?: boolean;
playback?: Playback;
}
2021-07-16 10:57:14 -06:00
export default class MAudioBody extends React.PureComponent<IBodyProps, IState> {
public static contextType = RoomContext;
2024-12-02 09:39:36 +00:00
declare public context: React.ContextType<typeof RoomContext>;
2024-08-01 13:01:05 +01:00
public state: IState = {};
public async componentDidMount(): Promise<void> {
let buffer: ArrayBuffer;
try {
try {
2023-07-07 13:37:26 +01:00
const blob = await this.props.mediaEventHelper!.sourceBlob.value;
buffer = await blob.arrayBuffer();
} catch (e) {
this.setState({ error: true });
2021-10-15 16:31:29 +02:00
logger.warn("Unable to decrypt audio message", e);
return; // stop processing the audio file
}
} catch (e) {
this.setState({ error: true });
2021-10-15 16:31:29 +02:00
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<MediaEventContent & IContent>();
2023-02-13 11:39:16 +00:00
const waveform = content?.["org.matrix.msc1767.audio"]?.waveform?.map((p: number) => p / 1024);
// We should have a buffer to work with now: let's set it up
2021-07-20 23:34:27 -06:00
const playback = PlaybackManager.instance.createPlaybackInstance(buffer, waveform);
2021-06-21 16:18:39 -06:00
playback.clockInfo.populatePlaceholdersFrom(this.props.mxEvent);
this.setState({ playback });
2021-08-30 14:20:03 -06:00
if (isVoiceMessage(this.props.mxEvent)) {
PlaybackQueue.forRoom(this.props.mxEvent.getRoomId()!).unsortedEnqueue(this.props.mxEvent, playback);
2021-08-30 14:20:03 -06:00
}
// Note: the components later on will handle preparing the Playback class for us.
}
public componentWillUnmount(): void {
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(): React.ReactNode {
if (this.state.error) {
return (
<MediaProcessingError className="mx_MAudioBody">
{_t("timeline|m.audio|error_processing_audio")}
</MediaProcessingError>
);
}
if (this.props.forExport) {
const content = this.props.mxEvent.getContent();
2021-08-14 00:14:57 +05:30
// 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 (
2021-06-21 15:13:12 -06:00
<span className="mx_MAudioBody">
<InlineSpinner />
</span>
);
}
// At this point we should have a playable state
return (
2021-06-21 15:13:12 -06:00
<span className="mx_MAudioBody">
2021-06-23 19:34:34 -06:00
<AudioPlayer playback={this.state.playback} mediaName={this.props.mxEvent.getContent().body} />
{this.showFileBody && <MFileBody {...this.props} showGenericPlaceholder={false} />}
</span>
2021-06-30 13:29:37 -06:00
);
}
}