diff --git a/src/components/middle/panes/AudioPlayer.tsx b/src/components/middle/panes/AudioPlayer.tsx index 44e59e191..3b72b636a 100644 --- a/src/components/middle/panes/AudioPlayer.tsx +++ b/src/components/middle/panes/AudioPlayer.tsx @@ -10,6 +10,7 @@ import type { IconName } from '../../../types/icons'; import { PLAYBACK_RATE_FOR_AUDIO_MIN_DURATION } from '../../../config'; import { + getMediaFormat, getMessageContent, isMessageLocal, } from '../../../global/helpers'; import { getPeerTitle } from '../../../global/helpers/peers'; @@ -20,7 +21,6 @@ import { selectMessageMediaDuration } from '../../../global/selectors/media'; import { makeTrackId } from '../../../util/audioPlayer'; import { IS_IOS, IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment'; import buildClassName from '../../../util/buildClassName'; -import * as mediaLoader from '../../../util/mediaLoader'; import { clearMediaSession } from '../../../util/mediaSession'; import renderText from '../../common/helpers/renderText'; @@ -30,6 +30,7 @@ import useAudioPlayer from '../../../hooks/useAudioPlayer'; import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers'; import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev'; import useLastCallback from '../../../hooks/useLastCallback'; +import useMedia from '../../../hooks/useMedia'; import useMessageMediaMetadata from '../../../hooks/useMessageMediaMetadata'; import useOldLang from '../../../hooks/useOldLang'; import useShowTransition from '../../../hooks/useShowTransition'; @@ -109,8 +110,10 @@ const AudioPlayer: FC = ({ const shouldRenderPlaybackButton = isVoice || (audio?.duration || 0) > PLAYBACK_RATE_FOR_AUDIO_MIN_DURATION; const senderName = sender ? getPeerTitle(lang, sender) : undefined; + const playableMedia = voice || video || audio; const mediaHash = useMessageMediaHash(renderingMessage, 'inline'); - const mediaData = mediaHash && mediaLoader.getFromMemory(mediaHash); + const mediaFormat = playableMedia && getMediaFormat(playableMedia, 'inline'); + const mediaData = useMedia(mediaHash, false, mediaFormat); const mediaMetadata = useMessageMediaMetadata(renderingMessage, sender, chat); const { @@ -424,8 +427,9 @@ export default withGlobal( const sender = message && selectSender(global, message); const chat = message && selectChat(global, message.chatId); const { - volume, playbackRate, isMuted, isPlaybackRateActive, timestamp, + playbackRate, isMuted, isPlaybackRateActive, timestamp, } = selectTabState(global).audioPlayer; + const { volume } = global.audioPlayer; const mediaDuration = message ? selectMessageMediaDuration(global, message) : undefined; diff --git a/src/global/actions/ui/messages.ts b/src/global/actions/ui/messages.ts index cdb91aa4a..aae3af383 100644 --- a/src/global/actions/ui/messages.ts +++ b/src/global/actions/ui/messages.ts @@ -183,7 +183,7 @@ addActionHandler('replyToNextMessage', (global, actions, payload): ActionReturnT addActionHandler('openAudioPlayer', (global, actions, payload): ActionReturnType => { const { - chatId, threadId, messageId, origin, volume, playbackRate, isMuted, timestamp, + chatId, threadId, messageId, origin, playbackRate, isMuted, timestamp, tabId = getCurrentTabId(), } = payload; @@ -195,7 +195,6 @@ addActionHandler('openAudioPlayer', (global, actions, payload): ActionReturnType messageId, timestamp, origin: origin ?? tabState.audioPlayer.origin, - volume: volume ?? tabState.audioPlayer.volume, playbackRate: playbackRate || tabState.audioPlayer.playbackRate || global.audioPlayer.lastPlaybackRate, isPlaybackRateActive: (tabState.audioPlayer.isPlaybackRateActive === undefined ? global.audioPlayer.isLastPlaybackRateActive @@ -210,13 +209,20 @@ addActionHandler('setAudioPlayerVolume', (global, actions, payload): ActionRetur volume, tabId = getCurrentTabId(), } = payload; - return updateTabState(global, { + global = updateTabState(global, { audioPlayer: { ...selectTabState(global, tabId).audioPlayer, - volume, isMuted: false, }, }, tabId); + global = { + ...global, + audioPlayer: { + ...global.audioPlayer, + volume, + }, + }; + return global; }); addActionHandler('setAudioPlayerPlaybackRate', (global, actions, payload): ActionReturnType => { @@ -273,7 +279,6 @@ addActionHandler('closeAudioPlayer', (global, actions, payload): ActionReturnTyp const tabState = selectTabState(global, tabId); return updateTabState(global, { audioPlayer: { - volume: tabState.audioPlayer.volume, playbackRate: tabState.audioPlayer.playbackRate, isPlaybackRateActive: tabState.audioPlayer.isPlaybackRateActive, isMuted: tabState.audioPlayer.isMuted, diff --git a/src/global/cache.ts b/src/global/cache.ts index 32feb3383..27bd5b634 100644 --- a/src/global/cache.ts +++ b/src/global/cache.ts @@ -367,6 +367,10 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) { cached.auth = initialState.auth; cached.auth.rememberMe = untypedCached.rememberMe; } + + if (cached.audioPlayer.volume === undefined) { + cached.audioPlayer.volume = initialState.audioPlayer.volume; + } } function updateCache(force?: boolean) { diff --git a/src/global/initialState.ts b/src/global/initialState.ts index 99c8a5b86..7057bb5da 100644 --- a/src/global/initialState.ts +++ b/src/global/initialState.ts @@ -106,6 +106,7 @@ export const INITIAL_GLOBAL_STATE: GlobalState = { appConfig: DEFAULT_APP_CONFIG, audioPlayer: { + volume: DEFAULT_VOLUME, lastPlaybackRate: DEFAULT_PLAYBACK_RATE, }, @@ -426,7 +427,6 @@ export const INITIAL_TAB_STATE: TabState = { }, audioPlayer: { - volume: DEFAULT_VOLUME, playbackRate: DEFAULT_PLAYBACK_RATE, isMuted: false, }, diff --git a/src/global/types/globalState.ts b/src/global/types/globalState.ts index 2b188a1b8..e0882c595 100644 --- a/src/global/types/globalState.ts +++ b/src/global/types/globalState.ts @@ -105,6 +105,7 @@ export type GlobalState = { botFreezeAppealId?: string; audioPlayer: { + volume: number; lastPlaybackRate: number; isLastPlaybackRateActive?: boolean; }; diff --git a/src/global/types/tabState.ts b/src/global/types/tabState.ts index aa6e6dcb2..e6ccf51e2 100644 --- a/src/global/types/tabState.ts +++ b/src/global/types/tabState.ts @@ -378,7 +378,6 @@ export type TabState = { messageId?: number; threadId?: ThreadId; origin?: AudioOrigin; - volume: number; playbackRate: number; isPlaybackRateActive?: boolean; timestamp?: number; diff --git a/src/hooks/useAudioPlayer.ts b/src/hooks/useAudioPlayer.ts index faef6d333..fb42574d6 100644 --- a/src/hooks/useAudioPlayer.ts +++ b/src/hooks/useAudioPlayer.ts @@ -65,17 +65,21 @@ const useAudioPlayer = ( const { setVolume, setPlaybackRate, toggleMuted, proxy, } = controllerRef.current!; + const global = getGlobal(); + setIsPlaying(true); if (trackType !== 'oneTimeVoice') { registerMediaSession(metadata, makeMediaHandlers(controllerRef)); } setPlaybackState('playing'); - const { audioPlayer } = selectTabState(getGlobal()); - setVolume(audioPlayer.volume); - toggleMuted(Boolean(audioPlayer.isMuted)); + + const { audioPlayer: tabAudioPlayerState } = selectTabState(global); + const { audioPlayer: globalAudioPlayerState } = global; + setVolume(globalAudioPlayerState.volume); + toggleMuted(Boolean(tabAudioPlayerState.isMuted)); const duration = proxy.duration && Number.isFinite(proxy.duration) ? proxy.duration : originalDuration; if (trackType === 'voice' || duration > PLAYBACK_RATE_FOR_AUDIO_MIN_DURATION) { - setPlaybackRate(audioPlayer.playbackRate); + setPlaybackRate(tabAudioPlayerState.playbackRate); } setPositionState({ duration: proxy.duration || 0, diff --git a/src/util/audioPlayer.ts b/src/util/audioPlayer.ts index c13941902..fbf45fd43 100644 --- a/src/util/audioPlayer.ts +++ b/src/util/audioPlayer.ts @@ -4,7 +4,6 @@ import type { ApiMessage } from '../api/types'; import type { MessageKey } from './keys/messageKey'; import { AudioOrigin, GlobalSearchContent } from '../types'; -import { requestNextMutation } from '../lib/fasterdom/fasterdom'; import { selectCurrentMessageList, selectTabState } from '../global/selectors'; import { IS_SAFARI } from './browser/windowEnvironment'; import { getMessageServerKey, parseMessageKey } from './keys/messageKey'; @@ -179,14 +178,15 @@ export function register( stop() { if (currentTrackId === trackId) { - // Hack, reset `src` to remove default media session notification const prevSrc = audio.src; audio.pause(); - // `onPause` not called otherwise, but required to sync UI - requestNextMutation(() => { + + // `onPause` is required to reset UI state + audio.addEventListener('pause', () => { + // Hack, reset `src` to remove default media session notification audio.src = ''; audio.src = prevSrc; - }); + }, { once: true }); } },