stream-chat-react
Version:
React components to create chat conversations or livestream style chat
68 lines (67 loc) • 5.27 kB
JavaScript
import React from 'react';
import { FileSizeIndicator, PlaybackRateButton, PlayButton, WaveProgressBar, } from './components';
import { displayDuration } from './utils';
import { FileIcon } from '../ReactFileUtilities';
import { useMessageContext, useTranslationContext } from '../../context';
import { useAudioPlayer } from '../AudioPlayback/';
import { useStateStore } from '../../store';
const rootClassName = 'str-chat__message-attachment__voice-recording-widget';
const audioPlayerStateSelector = (state) => ({
canPlayRecord: state.canPlayRecord,
isPlaying: state.isPlaying,
playbackRate: state.currentPlaybackRate,
progress: state.progressPercent,
secondsElapsed: state.secondsElapsed,
});
// todo: finish creating a BaseAudioPlayer derived from VoiceRecordingPlayerUI and AudioAttachmentUI
const VoiceRecordingPlayerUI = ({ audioPlayer }) => {
const { canPlayRecord, isPlaying, playbackRate, progress, secondsElapsed } = useStateStore(audioPlayer?.state, audioPlayerStateSelector) ?? {};
const displayedDuration = secondsElapsed || audioPlayer.durationSeconds;
return (React.createElement("div", { className: rootClassName, "data-testid": 'voice-recording-widget' },
React.createElement(PlayButton, { isPlaying: !!isPlaying, onClick: audioPlayer.togglePlay }),
React.createElement("div", { className: 'str-chat__message-attachment__voice-recording-widget__metadata' },
React.createElement("div", { className: 'str-chat__message-attachment__voice-recording-widget__title', "data-testid": 'voice-recording-title', title: audioPlayer.title }, audioPlayer.title),
React.createElement("div", { className: 'str-chat__message-attachment__voice-recording-widget__audio-state' },
React.createElement("div", { className: 'str-chat__message-attachment__voice-recording-widget__timer' }, audioPlayer.durationSeconds ? (displayDuration(displayedDuration)) : (React.createElement(FileSizeIndicator, { fileSize: audioPlayer.fileSize, maximumFractionDigits: 0 }))),
React.createElement(WaveProgressBar, { progress: progress, seek: audioPlayer.seek, waveformData: audioPlayer.waveformData || [] }))),
React.createElement("div", { className: 'str-chat__message-attachment__voice-recording-widget__right-section' }, isPlaying ? (React.createElement(PlaybackRateButton, { disabled: !canPlayRecord, onClick: audioPlayer.increasePlaybackRate },
playbackRate?.toFixed(1),
"x")) : (React.createElement(FileIcon, { big: true, mimeType: audioPlayer.mimeType, size: 40 })))));
};
export const VoiceRecordingPlayer = ({ attachment, playbackRates, }) => {
const { t } = useTranslationContext();
const { asset_url, duration = 0, file_size, mime_type, title = t('Voice message'), waveform_data, } = attachment;
/**
* Introducing message context. This could be breaking change, therefore the fallback to {} is provided.
* If this component is used outside the message context, then there will be no audio player namespacing
* => scrolling away from the message in virtualized ML would create a new AudioPlayer instance.
*
* Edge case: the requester (message) has multiple attachments with the same assetURL - does not happen
* with the default SDK components, but can be done with custom API calls.In this case all the Audio
* widgets will share the state.
*/
const { message, threadList } = useMessageContext() ?? {};
const audioPlayer = useAudioPlayer({
durationSeconds: duration ?? 0,
fileSize: file_size,
mimeType: mime_type,
playbackRates,
requester: message?.id &&
`${threadList ? (message.parent_id ?? message.id) : ''}${message.id}`,
src: asset_url,
title,
waveformData: waveform_data,
});
return audioPlayer ? React.createElement(VoiceRecordingPlayerUI, { audioPlayer: audioPlayer }) : null;
};
export const QuotedVoiceRecording = ({ attachment }) => {
const { t } = useTranslationContext();
const title = attachment.title || t('Voice message');
return (React.createElement("div", { className: rootClassName, "data-testid": 'quoted-voice-recording-widget' },
React.createElement("div", { className: 'str-chat__message-attachment__voice-recording-widget__metadata' },
title && (React.createElement("div", { className: 'str-chat__message-attachment__voice-recording-widget__title', "data-testid": 'voice-recording-title', title: title }, title)),
React.createElement("div", { className: 'str-chat__message-attachment__voice-recording-widget__audio-state' },
React.createElement("div", { className: 'str-chat__message-attachment__voice-recording-widget__timer' }, attachment.duration ? (displayDuration(attachment.duration)) : (React.createElement(FileSizeIndicator, { fileSize: attachment.file_size, maximumFractionDigits: 0 }))))),
React.createElement(FileIcon, { big: true, mimeType: attachment.mime_type, size: 34 })));
};
export const VoiceRecording = ({ attachment, isQuoted }) => isQuoted ? (React.createElement(QuotedVoiceRecording, { attachment: attachment })) : (React.createElement(VoiceRecordingPlayer, { attachment: attachment }));