session-desktop/ts/components/conversation/H5AudioPlayer.tsx

255 lines
6.5 KiB
TypeScript

// Audio Player
import React, { useEffect, useRef, useState } from 'react';
import H5AudioPlayer, { RHAP_UI } from 'react-h5-audio-player';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import { useEncryptedFileFetch } from '../../hooks/useEncryptedFileFetch';
import { setNextMessageToPlayId } from '../../state/ducks/conversations';
import {
getNextMessageToPlayId,
getSortedMessagesOfSelectedConversation,
isMessageSelectionMode,
} from '../../state/selectors/conversations';
import { getAudioAutoplay } from '../../state/selectors/userConfig';
import { SessionButton, SessionButtonType } from '../basic/SessionButton';
import { SessionIcon } from '../icon';
const StyledSpeedButton = styled.div`
padding: var(--margins-xs);
transition: none;
.session-button {
transition: none;
width: 34px;
padding: 0px;
}
`;
export const StyledH5AudioPlayer = styled(H5AudioPlayer)`
&.rhap_container {
min-width: 220px;
padding: 0px;
outline: none;
padding: var(--padding-message-content);
border-radius: var(--border-radius-message-box);
svg {
transition: fill var(--default-duration);
}
button {
outline: none;
}
}
.rhap_progress-container {
margin: 0 0 0 calc(10px + 1%);
outline: none;
}
.rhap_total-time {
display: none;
}
.rhap_current-time {
margin: 0 5px 0 4px;
flex-shrink: 0;
}
.rhap_play-pause-button {
display: flex;
justify-content: center;
align-items: center;
}
.rhap_volume-bar {
display: none;
}
.rhap_volume-button {
.module-message__container--incoming & {
color: var(--message-bubbles-received-text-color);
}
.module-message__container--outgoing & {
color: var(--message-bubbles-sent-text-color);
}
}
.rhap_volume-container div[role='progressbar'] {
display: none;
}
.rhap_time {
.module-message__container--incoming & {
color: var(--message-bubbles-received-text-color);
}
.module-message__container--outgoing & {
color: var(--message-bubbles-sent-text-color);
}
font-size: 12px;
}
.rhap_progress-bar {
box-sizing: border-box;
position: relative;
z-index: 0;
width: 100%;
height: 5px;
border-radius: 2px;
}
.rhap_progress-filled {
padding-left: 5px;
}
.rhap_download-progress {
height: 100%;
position: absolute;
z-index: 1;
border-radius: 2px;
}
.rhap_progress-indicator {
z-index: 3;
width: 15px;
height: 15px;
top: -5px;
margin-left: -10px;
box-shadow: rgba(0, 0, 0, 0.5) 0 0 5px !important;
}
.rhap_controls-section {
display: flex;
justify-content: space-between;
align-items: center;
}
.rhap_additional-controls {
display: none;
}
.rhap_play-pause-button {
width: unset;
height: unset;
}
.rhap_controls-section {
flex: unset;
justify-content: flex-start;
}
.rhap_volume-button {
font-size: 20px;
width: 20px;
height: 20px;
margin-right: 0px;
}
`;
export const AudioPlayerWithEncryptedFile = (props: {
src: string;
contentType: string;
messageId: string;
}) => {
const { messageId, contentType, src } = props;
const dispatch = useDispatch();
const [playbackSpeed, setPlaybackSpeed] = useState(1.0);
const { urlToLoad } = useEncryptedFileFetch(src, contentType, false);
const player = useRef<H5AudioPlayer | null>(null);
const autoPlaySetting = useSelector(getAudioAutoplay);
const messageProps = useSelector(getSortedMessagesOfSelectedConversation);
const nextMessageToPlayId = useSelector(getNextMessageToPlayId);
const multiSelectMode = useSelector(isMessageSelectionMode);
useEffect(() => {
// updates playback speed to value selected in context menu
if (
player.current?.audio.current &&
player.current?.audio.current?.playbackRate !== playbackSpeed
) {
player.current.audio.current.playbackRate = playbackSpeed;
}
}, [playbackSpeed, player]);
useEffect(() => {
if (messageId !== undefined && messageId === nextMessageToPlayId) {
player.current?.audio.current?.play();
}
}, [messageId, nextMessageToPlayId, player]);
const triggerPlayNextMessageIfNeeded = (endedMessageId: string) => {
const justEndedMessageIndex = messageProps.findIndex(
m => m.propsForMessage.id === endedMessageId
);
if (justEndedMessageIndex === -1) {
// make sure that even with switching convo or stuff, the next message to play is unset
dispatch(setNextMessageToPlayId(undefined));
return;
}
const isLastMessage = justEndedMessageIndex === 0;
// to prevent autoplaying as soon as a message is received.
if (isLastMessage) {
dispatch(setNextMessageToPlayId(undefined));
return;
}
// justEndedMessageIndex cannot be -1 nor 0, so it is >= 1
const nextMessageIndex = justEndedMessageIndex - 1;
// stop auto-playing when the audio messages change author.
const prevAuthorNumber = messageProps[justEndedMessageIndex].propsForMessage.sender;
const nextAuthorNumber = messageProps[nextMessageIndex].propsForMessage.sender;
const differentAuthor = prevAuthorNumber !== nextAuthorNumber;
if (differentAuthor) {
dispatch(setNextMessageToPlayId(undefined));
} else {
dispatch(setNextMessageToPlayId(messageProps[nextMessageIndex].propsForMessage.id));
}
};
const onEnded = () => {
// if audio autoplay is enabled, call method to start playing
// the next playable message
if (autoPlaySetting === true && messageId) {
triggerPlayNextMessageIfNeeded(messageId);
}
};
return (
<StyledH5AudioPlayer
src={urlToLoad}
preload="metadata"
style={{ pointerEvents: multiSelectMode ? 'none' : 'inherit' }}
layout="horizontal-reverse"
showSkipControls={false}
autoPlay={false}
autoPlayAfterSrcChange={false}
showJumpControls={false}
showDownloadProgress={false}
listenInterval={100}
onEnded={onEnded}
ref={player}
customControlsSection={[
RHAP_UI.MAIN_CONTROLS,
<StyledSpeedButton key="togglePlaybackSpeed">
<SessionButton
text={`${playbackSpeed}x`}
onClick={() => {
setPlaybackSpeed(playbackSpeed === 1 ? 1.5 : 1);
}}
buttonType={SessionButtonType.Simple}
/>
</StyledSpeedButton>,
]}
customIcons={{
play: <SessionIcon iconType="play" iconSize="small" />,
pause: <SessionIcon iconType="pause" iconSize="small" />,
}}
/>
);
};