diff --git a/src/components/common/Audio.tsx b/src/components/common/Audio.tsx index b9689b4ea..bdb342d60 100644 --- a/src/components/common/Audio.tsx +++ b/src/components/common/Audio.tsx @@ -258,14 +258,14 @@ const Audio: FC = ({ const isOwn = isOwnMessage(message); const renderedWaveform = useMemo( - () => voice && renderWaveform( + () => origin === AudioOrigin.Inline && voice && renderWaveform( voice, (isMediaUnread && !isOwn) ? 1 : playProgress, isOwn, theme, seekerRef, ), - [voice, isMediaUnread, isOwn, playProgress, theme], + [origin, voice, isMediaUnread, isOwn, playProgress, theme], ); const fullClassName = buildClassName( diff --git a/src/components/middle/AudioPlayer.scss b/src/components/middle/AudioPlayer.scss index a9cf669e3..4fe05dfbc 100644 --- a/src/components/middle/AudioPlayer.scss +++ b/src/components/middle/AudioPlayer.scss @@ -105,6 +105,22 @@ } } + .playback-button { + &.applied { + --color-text-secondary: var(--color-primary); + } + + .playback-button-inner { + transition: 0.2s color ease-in-out; + font-size: 0.75rem; + font-weight: bold; + border: 2px solid; + border-radius: 0.375rem; + padding: 0.15rem 0.25rem; + font-variant-numeric: tabular-nums; + } + } + &-content { display: flex; justify-content: center; diff --git a/src/components/middle/AudioPlayer.tsx b/src/components/middle/AudioPlayer.tsx index 362dae9d5..0bddb246e 100644 --- a/src/components/middle/AudioPlayer.tsx +++ b/src/components/middle/AudioPlayer.tsx @@ -7,7 +7,8 @@ import { ApiAudio, ApiChat, ApiMessage, ApiUser, } from '../../api/types'; -import { IS_IOS, IS_SINGLE_COLUMN_LAYOUT } from '../../util/environment'; +import { IS_IOS, IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../util/environment'; + import * as mediaLoader from '../../util/mediaLoader'; import { getMediaDuration, getMessageContent, getMessageMediaHash, getSenderTitle, @@ -39,9 +40,19 @@ type StateProps = { sender?: ApiChat | ApiUser; chat?: ApiChat; volume: number; + playbackRate: number; + isMuted: boolean; }; -type DispatchProps = Pick; +type DispatchProps = Pick; + +const FAST_PLAYBACK_RATE = 1.8; const AudioPlayer: FC = ({ message, @@ -51,19 +62,32 @@ const AudioPlayer: FC = ({ sender, chat, volume, + playbackRate, + isMuted, setAudioPlayerVolume, + setAudioPlayerPlaybackRate, + setAudioPlayerMuted, focusMessage, closeAudioPlayer, }) => { const lang = useLang(); - const { audio, voice } = getMessageContent(message); - const isVoice = Boolean(voice); + const { audio, voice, video } = getMessageContent(message); + const isVoice = Boolean(voice || video); const senderName = sender ? getSenderTitle(lang, sender) : undefined; const mediaData = mediaLoader.getFromMemory(getMessageMediaHash(message, 'inline')!) as (string | undefined); const mediaMetadata = useMessageMediaMetadata(message, sender, chat); const { - playPause, stop, isPlaying, requestNextTrack, requestPreviousTrack, isFirst, isLast, setVolume, + playPause, + stop, + isPlaying, + requestNextTrack, + requestPreviousTrack, + isFirst, + isLast, + setVolume, + toggleMuted, + setPlaybackRate, } = useAudioPlayer( makeTrackId(message), getMediaDuration(message)!, @@ -94,15 +118,33 @@ const AudioPlayer: FC = ({ const handleVolumeChange = useCallback((value: number) => { setAudioPlayerVolume({ volume: value / 100 }); + setAudioPlayerMuted({ isMuted: false }); + setVolume(value / 100); - }, [setAudioPlayerVolume, setVolume]); + }, [setAudioPlayerMuted, setAudioPlayerVolume, setVolume]); + + const handleVolumeClick = useCallback(() => { + if (IS_TOUCH_ENV && !IS_IOS) return; + toggleMuted(); + setAudioPlayerMuted({ isMuted: !isMuted }); + }, [isMuted, setAudioPlayerMuted, toggleMuted]); + + const handlePlaybackClick = useCallback(() => { + if (playbackRate === 1) { + setPlaybackRate(FAST_PLAYBACK_RATE); + setAudioPlayerPlaybackRate({ playbackRate: FAST_PLAYBACK_RATE }); + } else { + setPlaybackRate(1); + setAudioPlayerPlaybackRate({ playbackRate: 1 }); + } + }, [playbackRate, setAudioPlayerPlaybackRate, setPlaybackRate]); const volumeIcon = useMemo(() => { - if (volume === 0) return 'icon-muted'; + if (volume === 0 || isMuted) return 'icon-muted'; if (volume < 0.3) return 'icon-volume-1'; if (volume < 0.6) return 'icon-volume-2'; return 'icon-volume-3'; - }, [volume]); + }, [volume, isMuted]); if (noUi) { return undefined; @@ -152,22 +194,39 @@ const AudioPlayer: FC = ({ - {!IS_IOS && ( + + + {isVoice && ( )} +