From 28d87bd58002816fd7b2d1ff78be4642bedba041 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Tue, 6 Dec 2022 13:29:41 +0100 Subject: [PATCH] Audio Player: More playback speed options (#2164) --- src/components/middle/AudioPlayer.scss | 25 ++++-- src/components/middle/AudioPlayer.tsx | 105 ++++++++++++++++++------ src/components/middle/MiddleHeader.scss | 2 +- src/components/ui/DropdownMenu.tsx | 11 ++- 4 files changed, 107 insertions(+), 36 deletions(-) diff --git a/src/components/middle/AudioPlayer.scss b/src/components/middle/AudioPlayer.scss index 0addb3b29..21965a4fc 100644 --- a/src/components/middle/AudioPlayer.scss +++ b/src/components/middle/AudioPlayer.scss @@ -8,7 +8,7 @@ transition: none !important; } - > .Button { + > .Button, & > .playback-rate-menu { flex-shrink: 0; margin: 0.125rem; } @@ -21,14 +21,9 @@ } .player-button { - &.smaller { - width: 3rem; - height: 3rem; - - i { - font-size: 1.625rem; - margin-top: -0.0625rem; - } + &.smaller i { + font-size: 1.625rem; + margin-top: -0.0625rem; } i { @@ -108,6 +103,8 @@ } .playback-button { + overflow: visible; + &.applied { --color-text-secondary: var(--color-primary); } @@ -185,4 +182,14 @@ } } } + + .playback-rate-menu .bubble { + min-width: auto; + + .icon-check, .icon-placeholder { + margin-left: 0.25rem; + margin-right: 0.25rem; + width: 1.5rem; + } + } } diff --git a/src/components/middle/AudioPlayer.tsx b/src/components/middle/AudioPlayer.tsx index bb0a778b3..ffb5f9a31 100644 --- a/src/components/middle/AudioPlayer.tsx +++ b/src/components/middle/AudioPlayer.tsx @@ -1,4 +1,6 @@ -import React, { useCallback, useEffect, useMemo } from '../../lib/teact/teact'; +import React, { + useCallback, useEffect, useMemo, useRef, +} from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; import type { FC } from '../../lib/teact/teact'; @@ -19,14 +21,18 @@ import buildClassName from '../../util/buildClassName'; import { makeTrackId } from '../../util/audioPlayer'; import { clearMediaSession } from '../../util/mediaSession'; import windowSize from '../../util/windowSize'; -import useAudioPlayer from '../../hooks/useAudioPlayer'; import useLang from '../../hooks/useLang'; -import useMessageMediaMetadata from '../../hooks/useMessageMediaMetadata'; import renderText from '../common/helpers/renderText'; +import useAudioPlayer from '../../hooks/useAudioPlayer'; +import useMessageMediaMetadata from '../../hooks/useMessageMediaMetadata'; +import useContextMenuHandlers from '../../hooks/useContextMenuHandlers'; + import RippleEffect from '../ui/RippleEffect'; import Button from '../ui/Button'; import RangeSlider from '../ui/RangeSlider'; +import DropdownMenu from '../ui/DropdownMenu'; +import MenuItem from '../ui/MenuItem'; import './AudioPlayer.scss'; @@ -45,7 +51,13 @@ type StateProps = { isMuted: boolean; }; -const FAST_PLAYBACK_RATE = 1.8; +const PLAYBACK_RATES: Record = { + 0.5: 0.66, + 0.75: 0.8, + 1: 1, + 1.5: 1.4, + 2: 1.8, +}; const AudioPlayer: FC = ({ message, @@ -65,6 +77,8 @@ const AudioPlayer: FC = ({ closeAudioPlayer, } = getActions(); + // eslint-disable-next-line no-null/no-null + const ref = useRef(null); const lang = useLang(); const { audio, voice, video } = getMessageContent(message); const isVoice = Boolean(voice || video); @@ -113,6 +127,12 @@ const AudioPlayer: FC = ({ }; }, [isVoicePlaying]); + const { + isContextMenuOpen, + handleBeforeContextMenu, handleContextMenu, + handleContextMenuClose, handleContextMenuHide, + } = useContextMenuHandlers(ref); + const handleClick = useCallback(() => { focusMessage({ chatId: message.chatId, messageId: message.id }); }, [focusMessage, message.chatId, message.id]); @@ -138,15 +158,37 @@ const AudioPlayer: FC = ({ setAudioPlayerMuted({ isMuted: !isMuted }); }, [isMuted, setAudioPlayerMuted, toggleMuted]); + const updatePlaybackRate = useCallback((newRate: number) => { + const rate = PLAYBACK_RATES[newRate]; + setAudioPlayerPlaybackRate({ playbackRate: rate }); + setPlaybackRate(rate); + }, [setAudioPlayerPlaybackRate, setPlaybackRate]); + const handlePlaybackClick = useCallback(() => { - if (playbackRate === 1) { - setPlaybackRate(FAST_PLAYBACK_RATE); - setAudioPlayerPlaybackRate({ playbackRate: FAST_PLAYBACK_RATE }); - } else { - setPlaybackRate(1); - setAudioPlayerPlaybackRate({ playbackRate: 1 }); - } - }, [playbackRate, setAudioPlayerPlaybackRate, setPlaybackRate]); + if (isContextMenuOpen) return; + updatePlaybackRate(playbackRate === 1 ? 2 : 1); + }, [isContextMenuOpen, playbackRate, updatePlaybackRate]); + + const PlaybackRateButton = useCallback(() => { + const displayRate = Object.entries(PLAYBACK_RATES).find(([, rate]) => rate === playbackRate)?.[0] || 1; + return ( + + ); + }, [handleBeforeContextMenu, handleContextMenu, handlePlaybackClick, playbackRate]); const volumeIcon = useMemo(() => { if (volume === 0 || isMuted) return 'icon-muted'; @@ -160,7 +202,7 @@ const AudioPlayer: FC = ({ } return ( -
+
{audio ? renderAudio(audio) : renderVoice(lang('AttachAudio'), senderName)} @@ -223,17 +265,21 @@ const AudioPlayer: FC = ({ {shouldRenderPlaybackButton && ( - + {renderPlaybackRateMenuItem(0.5, playbackRate, updatePlaybackRate)} + {renderPlaybackRateMenuItem(0.75, playbackRate, updatePlaybackRate)} + {renderPlaybackRateMenuItem(1, playbackRate, updatePlaybackRate)} + {renderPlaybackRateMenuItem(1.5, playbackRate, updatePlaybackRate)} + {renderPlaybackRateMenuItem(2, playbackRate, updatePlaybackRate)} + )}