diff --git a/src/components/middle/AudioPlayer.tsx b/src/components/middle/AudioPlayer.tsx index 61daa2b2c..1a561c7d2 100644 --- a/src/components/middle/AudioPlayer.tsx +++ b/src/components/middle/AudioPlayer.tsx @@ -129,6 +129,9 @@ const AudioPlayer: FC = ({ }); const handleClose = useLastCallback(() => { + if (!stop) { + return; + } if (isPlaying) { playPause(); } @@ -138,18 +141,26 @@ const AudioPlayer: FC = ({ }); const handleVolumeChange = useLastCallback((value: number) => { + if (!setVolume) { + return; + } setAudioPlayerVolume({ volume: value / 100 }); - setVolume(value / 100); }); const handleVolumeClick = useLastCallback(() => { if (IS_TOUCH_ENV && !IS_IOS) return; + if (!toggleMuted) { + return; + } toggleMuted(); setAudioPlayerMuted({ isMuted: !isMuted }); }); const updatePlaybackRate = useLastCallback((newRate: number, isActive = true) => { + if (!setPlaybackRate) { + return; + } const rate = PLAYBACK_RATES[newRate]; const shouldBeActive = newRate !== REGULAR_PLAYBACK_RATE && isActive; setAudioPlayerPlaybackRate({ playbackRate: rate, isPlaybackRateActive: shouldBeActive }); @@ -227,7 +238,7 @@ const AudioPlayer: FC = ({ color="translucent" size="smaller" className="player-button" - disabled={isFirst()} + disabled={isFirst?.()} onClick={requestPreviousTrack} ariaLabel="Previous track" > @@ -251,7 +262,7 @@ const AudioPlayer: FC = ({ color="translucent" size="smaller" className="player-button" - disabled={isLast()} + disabled={isLast?.()} onClick={requestNextTrack} ariaLabel="Next track" > diff --git a/src/components/middle/MessageList.tsx b/src/components/middle/MessageList.tsx index 43d548684..1104f8f71 100644 --- a/src/components/middle/MessageList.tsx +++ b/src/components/middle/MessageList.tsx @@ -31,7 +31,6 @@ import { isChatChannel, isChatGroup, isChatWithRepliesBot, - isLocalMessageId, isUserId, } from '../../global/helpers'; import { @@ -57,6 +56,7 @@ import { import animateScroll, { isAnimatingScroll, restartCurrentScrollAnimation } from '../../util/animateScroll'; import buildClassName from '../../util/buildClassName'; import { orderBy } from '../../util/iteratees'; +import { isLocalMessageId } from '../../util/messageKey'; import resetScroll from '../../util/resetScroll'; import { debounce, onTickEnd } from '../../util/schedulers'; import { groupMessages } from './helpers/groupMessages'; diff --git a/src/global/actions/api/chats.ts b/src/global/actions/api/chats.ts index 334cad4e7..8483b7ade 100644 --- a/src/global/actions/api/chats.ts +++ b/src/global/actions/api/chats.ts @@ -36,6 +36,7 @@ import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getOrderedIds } from '../../../util/folderManager'; import { buildCollectionByKey, omit, pick } from '../../../util/iteratees'; import * as langProvider from '../../../util/langProvider'; +import { isLocalMessageId } from '../../../util/messageKey'; import { debounce, pause, throttle } from '../../../util/schedulers'; import { extractCurrentThemeParams } from '../../../util/themeStyle'; import { callApi } from '../../../api/gramjs'; @@ -45,7 +46,6 @@ import { isChatBasicGroup, isChatChannel, isChatSuperGroup, - isLocalMessageId, isUserBot, toChannelId, } from '../../helpers'; diff --git a/src/global/actions/api/messages.ts b/src/global/actions/api/messages.ts index a1bd80321..36e707dbb 100644 --- a/src/global/actions/api/messages.ts +++ b/src/global/actions/api/messages.ts @@ -45,7 +45,7 @@ import { unique, } from '../../../util/iteratees'; import { translate } from '../../../util/langProvider'; -import { getMessageKey } from '../../../util/messageKey'; +import { getMessageKey, isLocalMessageId } from '../../../util/messageKey'; import { debounce, onTickEnd, rafPromise } from '../../../util/schedulers'; import { IS_IOS } from '../../../util/windowEnvironment'; import { callApi, cancelApiProgress } from '../../../api/gramjs'; @@ -54,7 +54,6 @@ import { getUserFullName, isChatChannel, isDeletedUser, - isLocalMessageId, isMessageLocal, isServiceNotificationMessage, isUserBot, diff --git a/src/global/actions/apiUpdaters/chats.ts b/src/global/actions/apiUpdaters/chats.ts index aac106b73..7444d1583 100644 --- a/src/global/actions/apiUpdaters/chats.ts +++ b/src/global/actions/apiUpdaters/chats.ts @@ -4,9 +4,10 @@ import { MAIN_THREAD_ID } from '../../../api/types'; import { ARCHIVED_FOLDER_ID, MAX_ACTIVE_PINNED_CHATS } from '../../../config'; import { buildCollectionByKey, omit } from '../../../util/iteratees'; +import { isLocalMessageId } from '../../../util/messageKey'; import { closeMessageNotifications, notifyAboutMessage } from '../../../util/notifications'; import { buildLocalMessage } from '../../../api/gramjs/apiBuilders/messages'; -import { isChatChannel, isLocalMessageId } from '../../helpers'; +import { isChatChannel } from '../../helpers'; import { addActionHandler, getGlobal, setGlobal, } from '../../index'; diff --git a/src/global/actions/apiUpdaters/messages.ts b/src/global/actions/apiUpdaters/messages.ts index f8339f922..b0231678a 100644 --- a/src/global/actions/apiUpdaters/messages.ts +++ b/src/global/actions/apiUpdaters/messages.ts @@ -12,11 +12,11 @@ import { SERVICE_NOTIFICATIONS_USER_ID } from '../../../config'; import { areDeepEqual } from '../../../util/areDeepEqual'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { omit, pickTruthy, unique } from '../../../util/iteratees'; -import { getMessageKey } from '../../../util/messageKey'; +import { getMessageKey, isLocalMessageId } from '../../../util/messageKey'; import { notifyAboutMessage } from '../../../util/notifications'; import { onTickEnd } from '../../../util/schedulers'; import { - checkIfHasUnreadReactions, getIsSavedDialog, getMessageContent, getMessageText, isActionMessage, isLocalMessageId, + checkIfHasUnreadReactions, getIsSavedDialog, getMessageContent, getMessageText, isActionMessage, isMessageLocal, isUserId, } from '../../helpers'; import { getMessageReplyInfo, getStoryReplyInfo } from '../../helpers/replies'; diff --git a/src/global/cache.ts b/src/global/cache.ts index e70f662a7..e147cbe97 100644 --- a/src/global/cache.ts +++ b/src/global/cache.ts @@ -498,26 +498,30 @@ function omitLocalMedia(message: ApiMessage): ApiMessage { photo, video, document, sticker, } = message.content; - if (photo) { - photo.blobUrl = undefined; - } - - if (video) { - video.blobUrl = undefined; - video.previewBlobUrl = undefined; - } - - if (document) { - document.previewBlobUrl = undefined; - } - - if (sticker) { - sticker.isPreloadedGlobally = undefined; - } - - message.previousLocalId = undefined; - - return message; + return { + ...message, + content: { + ...message.content, + photo: photo && { + ...photo, + blobUrl: undefined, + }, + video: video && { + ...video, + blobUrl: undefined, + previewBlobUrl: undefined, + }, + document: document && { + ...document, + previewBlobUrl: undefined, + }, + sticker: sticker && { + ...sticker, + isPreloadedGlobally: undefined, + }, + }, + previousLocalId: undefined, + }; } function reduceSettings(global: T): GlobalState['settings'] { diff --git a/src/global/helpers/messageMedia.ts b/src/global/helpers/messageMedia.ts index c91b361a6..5ffccc21f 100644 --- a/src/global/helpers/messageMedia.ts +++ b/src/global/helpers/messageMedia.ts @@ -14,7 +14,7 @@ import type { } from '../../api/types'; import { ApiMediaFormat } from '../../api/types'; -import { getMessageKey } from '../../util/messageKey'; +import { getMessageServerKey } from '../../util/messageKey'; import { IS_OPFS_SUPPORTED, IS_OPUS_SUPPORTED, @@ -229,8 +229,13 @@ export function getMessageMediaHash( return undefined; } + const messageKey = getMessageServerKey(message); + if (!messageKey) { + return undefined; + } + const mediaId = content.id; - const base = `${getMessageKey(message)}${mediaId ? `:${mediaId}` : ''}`; + const base = `${messageKey}${mediaId ? `:${mediaId}` : ''}`; if (messageVideo) { switch (target) { diff --git a/src/global/helpers/messages.ts b/src/global/helpers/messages.ts index 423200c0f..81fc329e2 100644 --- a/src/global/helpers/messages.ts +++ b/src/global/helpers/messages.ts @@ -11,7 +11,7 @@ import { SUPPORTED_IMAGE_CONTENT_TYPES, SUPPORTED_VIDEO_CONTENT_TYPES, VIDEO_STICKER_MIME_TYPE, } from '../../config'; import { areSortedArraysIntersecting, unique } from '../../util/iteratees'; -import { getMessageKey } from '../../util/messageKey'; +import { getMessageKey, isLocalMessageId } from '../../util/messageKey'; import { getServerTime } from '../../util/serverTime'; import { IS_OPUS_SUPPORTED } from '../../util/windowEnvironment'; import { getGlobal } from '../index'; @@ -188,10 +188,6 @@ export function isMessageFailed(message: ApiMessage) { return message.sendingState === 'messageSendingStateFailed'; } -export function isLocalMessageId(id: number) { - return !Number.isInteger(id); -} - export function isHistoryClearMessage(message: ApiMessage) { return message.content.action && message.content.action.type === 'historyClear'; } diff --git a/src/global/init.ts b/src/global/init.ts index 063d9be34..2d3341f92 100644 --- a/src/global/init.ts +++ b/src/global/init.ts @@ -6,6 +6,7 @@ import { IS_MOCKED_CLIENT } from '../config'; import { isCacheApiSupported } from '../util/cacheApi'; import { getCurrentTabId, reestablishMasterToSelf } from '../util/establishMultitabRole'; import { cloneDeep } from '../util/iteratees'; +import { isLocalMessageId } from '../util/messageKey'; import { Bundles, loadBundle } from '../util/moduleLoader'; import { parseLocationHash } from '../util/routing'; import { clearStoredSession } from '../util/sessions'; @@ -13,7 +14,6 @@ import { updatePeerColors } from '../util/theme'; import { IS_MULTITAB_SUPPORTED } from '../util/windowEnvironment'; import { updateTabState } from './reducers/tabs'; import { initCache, loadCache } from './cache'; -import { isLocalMessageId } from './helpers'; import { addActionHandler, getGlobal, setGlobal, } from './index'; diff --git a/src/global/reducers/messages.ts b/src/global/reducers/messages.ts index 74f00f6a9..f1344c0cf 100644 --- a/src/global/reducers/messages.ts +++ b/src/global/reducers/messages.ts @@ -1,6 +1,5 @@ import type { ApiMessage, ApiSponsoredMessage, ApiThreadInfo } from '../../api/types'; import type { FocusDirection, ThreadId } from '../../types'; -import type { MessageKey } from '../../util/messageKey'; import type { GlobalState, MessageList, MessageListType, TabArgs, TabThread, Thread, } from '../types'; @@ -13,9 +12,9 @@ import { getCurrentTabId } from '../../util/establishMultitabRole'; import { areSortedArraysEqual, excludeSortedArray, omit, pick, pickTruthy, unique, } from '../../util/iteratees'; +import { isLocalMessageId, type MessageKey } from '../../util/messageKey'; import { - hasMessageTtl, - isLocalMessageId, mergeIdRanges, orderHistoryIds, orderPinnedIds, + hasMessageTtl, mergeIdRanges, orderHistoryIds, orderPinnedIds, } from '../helpers'; import { selectChat, diff --git a/src/global/selectors/messages.ts b/src/global/selectors/messages.ts index 31cf9a760..d5fe98073 100644 --- a/src/global/selectors/messages.ts +++ b/src/global/selectors/messages.ts @@ -22,7 +22,7 @@ import { import { getCurrentTabId } from '../../util/establishMultitabRole'; import { findLast } from '../../util/iteratees'; import { MEMO_EMPTY_ARRAY } from '../../util/memo'; -import { getMessageKey } from '../../util/messageKey'; +import { getMessageKey, isLocalMessageId } from '../../util/messageKey'; import { getServerTime } from '../../util/serverTime'; import { IS_TRANSLATION_SUPPORTED } from '../../util/windowEnvironment'; import { @@ -47,7 +47,6 @@ import { isChatSuperGroup, isCommonBoxChat, isForwardedMessage, - isLocalMessageId, isMessageDocumentSticker, isMessageFailed, isMessageLocal, diff --git a/src/hooks/useAudioPlayer.ts b/src/hooks/useAudioPlayer.ts index 1de4b0f1b..9d039e59f 100644 --- a/src/hooks/useAudioPlayer.ts +++ b/src/hooks/useAudioPlayer.ts @@ -1,4 +1,6 @@ -import { useEffect, useRef, useState } from '../lib/teact/teact'; +import { + useEffect, useMemo, useRef, useState, +} from '../lib/teact/teact'; import { getActions, getGlobal } from '../global'; import type { Track, TrackId } from '../util/audioPlayer'; @@ -20,7 +22,7 @@ type Handler = (e: Event) => void; const DEFAULT_SKIP_TIME = 10; const useAudioPlayer = ( - trackId: TrackId, + trackId: TrackId | undefined, originalDuration: number, // Sometimes incorrect for voice messages trackType: Track['type'], src?: string, @@ -50,6 +52,9 @@ const useAudioPlayer = ( }); useSyncEffect(() => { + if (!trackId) { + return; + } controllerRef.current = register(trackId, trackType, (eventName, e) => { if (noHandleEvents) { return; @@ -141,11 +146,17 @@ const useAudioPlayer = ( requestPreviousTrack, setPlaybackRate, toggleMuted, - } = controllerRef.current!; - const duration = proxy.duration && Number.isFinite(proxy.duration) ? proxy.duration : originalDuration; + } = controllerRef.current ?? {}; + + const duration = useMemo(() => { + return proxy?.duration && Number.isFinite(proxy.duration) ? proxy.duration : originalDuration; + }, [proxy?.duration, originalDuration]); // RAF progress useEffect(() => { + if (!proxy) { + return; + } if (noReset && proxy.currentTime === 0) { return; } @@ -156,7 +167,7 @@ const useAudioPlayer = ( // Cleanup useEffect(() => () => { - destroy(noPlaylist); + destroy?.(noPlaylist); }, [destroy, noPlaylist]); // Autoplay once `src` is present @@ -166,32 +177,32 @@ const useAudioPlayer = ( } // When paused by another player - if (proxy.src && proxy.paused) { + if (proxy?.src && proxy?.paused) { return; } if (shouldPlay && src && !isPlaying) { - play(src); + play?.(src); } - }, [shouldPlay, src, isPlaying, play, proxy.src, proxy.paused, trackType]); + }, [shouldPlay, src, isPlaying, play, proxy?.src, proxy?.paused, trackType]); const playIfPresent = useLastCallback(() => { if (src) { - play(src); + play?.(src); } }); const playPause = useLastCallback(() => { if (isPlaying) { - pause(); + pause?.(); } else { playIfPresent(); } }); const setTime = useLastCallback((time: number) => { - setCurrentTime(time); - if (duration) { + setCurrentTime?.(time); + if (duration && proxy) { setPlayProgress(proxy.currentTime / duration); } }); diff --git a/src/hooks/useBuffering.ts b/src/hooks/useBuffering.ts index d5ef6da43..9499787cd 100644 --- a/src/hooks/useBuffering.ts +++ b/src/hooks/useBuffering.ts @@ -56,6 +56,7 @@ const useBuffering = (noInitiallyBuffered = false, onTimeUpdate?: AnyToVoidFunct }); const bufferingHandlers = { + onPLay: handleBuffering, onLoadedData: handleBuffering, onPlaying: handleBuffering, onLoadStart: handleBuffering, // Needed for Safari to start diff --git a/src/util/audioPlayer.ts b/src/util/audioPlayer.ts index 429bf0595..288decdb6 100644 --- a/src/util/audioPlayer.ts +++ b/src/util/audioPlayer.ts @@ -6,7 +6,7 @@ import { AudioOrigin, GlobalSearchContent } from '../types'; import { requestNextMutation } from '../lib/fasterdom/fasterdom'; import { selectCurrentMessageList, selectTabState } from '../global/selectors'; -import { getMessageKey, parseMessageKey } from './messageKey'; +import { getMessageServerKey, parseMessageKey } from './messageKey'; import { isSafariPatchInProgress, patchSafariProgressiveAudio } from './patchSafariProgressiveAudio'; import safePlay from './safePlay'; import { IS_SAFARI } from './windowEnvironment'; @@ -24,6 +24,7 @@ export interface Track { } const tracks = new Map(); + let voiceQueue: TrackId[] = []; let musicQueue: TrackId[] = []; @@ -331,8 +332,12 @@ function findNextInQueue(currentId: TrackId, origin = AudioOrigin.Inline, isReve return chatAudio[index + direction]; } -export function makeTrackId(message: ApiMessage): TrackId { - return `${getMessageKey(message)}-${message.date}`; +export function makeTrackId(message: ApiMessage): TrackId | undefined { + const key = getMessageServerKey(message); + if (!key) { + return undefined; + } + return `${key}-${message.date}`; } function splitTrackId(trackId: TrackId) { diff --git a/src/util/messageKey.ts b/src/util/messageKey.ts index 5121070d4..14a8e2103 100644 --- a/src/util/messageKey.ts +++ b/src/util/messageKey.ts @@ -8,7 +8,15 @@ export function getMessageKey(message: ApiMessage): MessageKey { return buildMessageKey(chatId, previousLocalId || id); } -function buildMessageKey(chatId: string, msgId: number): MessageKey { +export function getMessageServerKey(message: ApiMessage): MessageKey | undefined { + if (isLocalMessageId(message.id)) { + return undefined; + } + const { chatId, id } = message; + return buildMessageKey(chatId, id); +} + +export function buildMessageKey(chatId: string, msgId: number): MessageKey { return `msg${chatId}-${msgId}`; } @@ -17,3 +25,7 @@ export function parseMessageKey(key: MessageKey) { return { chatId: match[1], messageId: Number(match[2]) }; } + +export function isLocalMessageId(id: number) { + return !Number.isInteger(id); +}