Media Message: Fix playing voice and audio after send (#4347)

This commit is contained in:
Alexander Zinchuk 2024-03-22 13:06:01 +01:00
parent c23752a28e
commit b8b8c15919
16 changed files with 102 additions and 59 deletions

View File

@ -129,6 +129,9 @@ const AudioPlayer: FC<OwnProps & StateProps> = ({
}); });
const handleClose = useLastCallback(() => { const handleClose = useLastCallback(() => {
if (!stop) {
return;
}
if (isPlaying) { if (isPlaying) {
playPause(); playPause();
} }
@ -138,18 +141,26 @@ const AudioPlayer: FC<OwnProps & StateProps> = ({
}); });
const handleVolumeChange = useLastCallback((value: number) => { const handleVolumeChange = useLastCallback((value: number) => {
if (!setVolume) {
return;
}
setAudioPlayerVolume({ volume: value / 100 }); setAudioPlayerVolume({ volume: value / 100 });
setVolume(value / 100); setVolume(value / 100);
}); });
const handleVolumeClick = useLastCallback(() => { const handleVolumeClick = useLastCallback(() => {
if (IS_TOUCH_ENV && !IS_IOS) return; if (IS_TOUCH_ENV && !IS_IOS) return;
if (!toggleMuted) {
return;
}
toggleMuted(); toggleMuted();
setAudioPlayerMuted({ isMuted: !isMuted }); setAudioPlayerMuted({ isMuted: !isMuted });
}); });
const updatePlaybackRate = useLastCallback((newRate: number, isActive = true) => { const updatePlaybackRate = useLastCallback((newRate: number, isActive = true) => {
if (!setPlaybackRate) {
return;
}
const rate = PLAYBACK_RATES[newRate]; const rate = PLAYBACK_RATES[newRate];
const shouldBeActive = newRate !== REGULAR_PLAYBACK_RATE && isActive; const shouldBeActive = newRate !== REGULAR_PLAYBACK_RATE && isActive;
setAudioPlayerPlaybackRate({ playbackRate: rate, isPlaybackRateActive: shouldBeActive }); setAudioPlayerPlaybackRate({ playbackRate: rate, isPlaybackRateActive: shouldBeActive });
@ -227,7 +238,7 @@ const AudioPlayer: FC<OwnProps & StateProps> = ({
color="translucent" color="translucent"
size="smaller" size="smaller"
className="player-button" className="player-button"
disabled={isFirst()} disabled={isFirst?.()}
onClick={requestPreviousTrack} onClick={requestPreviousTrack}
ariaLabel="Previous track" ariaLabel="Previous track"
> >
@ -251,7 +262,7 @@ const AudioPlayer: FC<OwnProps & StateProps> = ({
color="translucent" color="translucent"
size="smaller" size="smaller"
className="player-button" className="player-button"
disabled={isLast()} disabled={isLast?.()}
onClick={requestNextTrack} onClick={requestNextTrack}
ariaLabel="Next track" ariaLabel="Next track"
> >

View File

@ -31,7 +31,6 @@ import {
isChatChannel, isChatChannel,
isChatGroup, isChatGroup,
isChatWithRepliesBot, isChatWithRepliesBot,
isLocalMessageId,
isUserId, isUserId,
} from '../../global/helpers'; } from '../../global/helpers';
import { import {
@ -57,6 +56,7 @@ import {
import animateScroll, { isAnimatingScroll, restartCurrentScrollAnimation } from '../../util/animateScroll'; import animateScroll, { isAnimatingScroll, restartCurrentScrollAnimation } from '../../util/animateScroll';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
import { orderBy } from '../../util/iteratees'; import { orderBy } from '../../util/iteratees';
import { isLocalMessageId } from '../../util/messageKey';
import resetScroll from '../../util/resetScroll'; import resetScroll from '../../util/resetScroll';
import { debounce, onTickEnd } from '../../util/schedulers'; import { debounce, onTickEnd } from '../../util/schedulers';
import { groupMessages } from './helpers/groupMessages'; import { groupMessages } from './helpers/groupMessages';

View File

@ -36,6 +36,7 @@ import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { getOrderedIds } from '../../../util/folderManager'; import { getOrderedIds } from '../../../util/folderManager';
import { buildCollectionByKey, omit, pick } from '../../../util/iteratees'; import { buildCollectionByKey, omit, pick } from '../../../util/iteratees';
import * as langProvider from '../../../util/langProvider'; import * as langProvider from '../../../util/langProvider';
import { isLocalMessageId } from '../../../util/messageKey';
import { debounce, pause, throttle } from '../../../util/schedulers'; import { debounce, pause, throttle } from '../../../util/schedulers';
import { extractCurrentThemeParams } from '../../../util/themeStyle'; import { extractCurrentThemeParams } from '../../../util/themeStyle';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
@ -45,7 +46,6 @@ import {
isChatBasicGroup, isChatBasicGroup,
isChatChannel, isChatChannel,
isChatSuperGroup, isChatSuperGroup,
isLocalMessageId,
isUserBot, isUserBot,
toChannelId, toChannelId,
} from '../../helpers'; } from '../../helpers';

View File

@ -45,7 +45,7 @@ import {
unique, unique,
} from '../../../util/iteratees'; } from '../../../util/iteratees';
import { translate } from '../../../util/langProvider'; import { translate } from '../../../util/langProvider';
import { getMessageKey } from '../../../util/messageKey'; import { getMessageKey, isLocalMessageId } from '../../../util/messageKey';
import { debounce, onTickEnd, rafPromise } from '../../../util/schedulers'; import { debounce, onTickEnd, rafPromise } from '../../../util/schedulers';
import { IS_IOS } from '../../../util/windowEnvironment'; import { IS_IOS } from '../../../util/windowEnvironment';
import { callApi, cancelApiProgress } from '../../../api/gramjs'; import { callApi, cancelApiProgress } from '../../../api/gramjs';
@ -54,7 +54,6 @@ import {
getUserFullName, getUserFullName,
isChatChannel, isChatChannel,
isDeletedUser, isDeletedUser,
isLocalMessageId,
isMessageLocal, isMessageLocal,
isServiceNotificationMessage, isServiceNotificationMessage,
isUserBot, isUserBot,

View File

@ -4,9 +4,10 @@ import { MAIN_THREAD_ID } from '../../../api/types';
import { ARCHIVED_FOLDER_ID, MAX_ACTIVE_PINNED_CHATS } from '../../../config'; import { ARCHIVED_FOLDER_ID, MAX_ACTIVE_PINNED_CHATS } from '../../../config';
import { buildCollectionByKey, omit } from '../../../util/iteratees'; import { buildCollectionByKey, omit } from '../../../util/iteratees';
import { isLocalMessageId } from '../../../util/messageKey';
import { closeMessageNotifications, notifyAboutMessage } from '../../../util/notifications'; import { closeMessageNotifications, notifyAboutMessage } from '../../../util/notifications';
import { buildLocalMessage } from '../../../api/gramjs/apiBuilders/messages'; import { buildLocalMessage } from '../../../api/gramjs/apiBuilders/messages';
import { isChatChannel, isLocalMessageId } from '../../helpers'; import { isChatChannel } from '../../helpers';
import { import {
addActionHandler, getGlobal, setGlobal, addActionHandler, getGlobal, setGlobal,
} from '../../index'; } from '../../index';

View File

@ -12,11 +12,11 @@ import { SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
import { areDeepEqual } from '../../../util/areDeepEqual'; import { areDeepEqual } from '../../../util/areDeepEqual';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { omit, pickTruthy, unique } from '../../../util/iteratees'; import { omit, pickTruthy, unique } from '../../../util/iteratees';
import { getMessageKey } from '../../../util/messageKey'; import { getMessageKey, isLocalMessageId } from '../../../util/messageKey';
import { notifyAboutMessage } from '../../../util/notifications'; import { notifyAboutMessage } from '../../../util/notifications';
import { onTickEnd } from '../../../util/schedulers'; import { onTickEnd } from '../../../util/schedulers';
import { import {
checkIfHasUnreadReactions, getIsSavedDialog, getMessageContent, getMessageText, isActionMessage, isLocalMessageId, checkIfHasUnreadReactions, getIsSavedDialog, getMessageContent, getMessageText, isActionMessage,
isMessageLocal, isUserId, isMessageLocal, isUserId,
} from '../../helpers'; } from '../../helpers';
import { getMessageReplyInfo, getStoryReplyInfo } from '../../helpers/replies'; import { getMessageReplyInfo, getStoryReplyInfo } from '../../helpers/replies';

View File

@ -498,26 +498,30 @@ function omitLocalMedia(message: ApiMessage): ApiMessage {
photo, video, document, sticker, photo, video, document, sticker,
} = message.content; } = message.content;
if (photo) { return {
photo.blobUrl = undefined; ...message,
} content: {
...message.content,
if (video) { photo: photo && {
video.blobUrl = undefined; ...photo,
video.previewBlobUrl = undefined; blobUrl: undefined,
} },
video: video && {
if (document) { ...video,
document.previewBlobUrl = undefined; blobUrl: undefined,
} previewBlobUrl: undefined,
},
if (sticker) { document: document && {
sticker.isPreloadedGlobally = undefined; ...document,
} previewBlobUrl: undefined,
},
message.previousLocalId = undefined; sticker: sticker && {
...sticker,
return message; isPreloadedGlobally: undefined,
},
},
previousLocalId: undefined,
};
} }
function reduceSettings<T extends GlobalState>(global: T): GlobalState['settings'] { function reduceSettings<T extends GlobalState>(global: T): GlobalState['settings'] {

View File

@ -14,7 +14,7 @@ import type {
} from '../../api/types'; } from '../../api/types';
import { ApiMediaFormat } from '../../api/types'; import { ApiMediaFormat } from '../../api/types';
import { getMessageKey } from '../../util/messageKey'; import { getMessageServerKey } from '../../util/messageKey';
import { import {
IS_OPFS_SUPPORTED, IS_OPFS_SUPPORTED,
IS_OPUS_SUPPORTED, IS_OPUS_SUPPORTED,
@ -229,8 +229,13 @@ export function getMessageMediaHash(
return undefined; return undefined;
} }
const messageKey = getMessageServerKey(message);
if (!messageKey) {
return undefined;
}
const mediaId = content.id; const mediaId = content.id;
const base = `${getMessageKey(message)}${mediaId ? `:${mediaId}` : ''}`; const base = `${messageKey}${mediaId ? `:${mediaId}` : ''}`;
if (messageVideo) { if (messageVideo) {
switch (target) { switch (target) {

View File

@ -11,7 +11,7 @@ import {
SUPPORTED_IMAGE_CONTENT_TYPES, SUPPORTED_VIDEO_CONTENT_TYPES, VIDEO_STICKER_MIME_TYPE, SUPPORTED_IMAGE_CONTENT_TYPES, SUPPORTED_VIDEO_CONTENT_TYPES, VIDEO_STICKER_MIME_TYPE,
} from '../../config'; } from '../../config';
import { areSortedArraysIntersecting, unique } from '../../util/iteratees'; import { areSortedArraysIntersecting, unique } from '../../util/iteratees';
import { getMessageKey } from '../../util/messageKey'; import { getMessageKey, isLocalMessageId } from '../../util/messageKey';
import { getServerTime } from '../../util/serverTime'; import { getServerTime } from '../../util/serverTime';
import { IS_OPUS_SUPPORTED } from '../../util/windowEnvironment'; import { IS_OPUS_SUPPORTED } from '../../util/windowEnvironment';
import { getGlobal } from '../index'; import { getGlobal } from '../index';
@ -188,10 +188,6 @@ export function isMessageFailed(message: ApiMessage) {
return message.sendingState === 'messageSendingStateFailed'; return message.sendingState === 'messageSendingStateFailed';
} }
export function isLocalMessageId(id: number) {
return !Number.isInteger(id);
}
export function isHistoryClearMessage(message: ApiMessage) { export function isHistoryClearMessage(message: ApiMessage) {
return message.content.action && message.content.action.type === 'historyClear'; return message.content.action && message.content.action.type === 'historyClear';
} }

View File

@ -6,6 +6,7 @@ import { IS_MOCKED_CLIENT } from '../config';
import { isCacheApiSupported } from '../util/cacheApi'; import { isCacheApiSupported } from '../util/cacheApi';
import { getCurrentTabId, reestablishMasterToSelf } from '../util/establishMultitabRole'; import { getCurrentTabId, reestablishMasterToSelf } from '../util/establishMultitabRole';
import { cloneDeep } from '../util/iteratees'; import { cloneDeep } from '../util/iteratees';
import { isLocalMessageId } from '../util/messageKey';
import { Bundles, loadBundle } from '../util/moduleLoader'; import { Bundles, loadBundle } from '../util/moduleLoader';
import { parseLocationHash } from '../util/routing'; import { parseLocationHash } from '../util/routing';
import { clearStoredSession } from '../util/sessions'; import { clearStoredSession } from '../util/sessions';
@ -13,7 +14,6 @@ import { updatePeerColors } from '../util/theme';
import { IS_MULTITAB_SUPPORTED } from '../util/windowEnvironment'; import { IS_MULTITAB_SUPPORTED } from '../util/windowEnvironment';
import { updateTabState } from './reducers/tabs'; import { updateTabState } from './reducers/tabs';
import { initCache, loadCache } from './cache'; import { initCache, loadCache } from './cache';
import { isLocalMessageId } from './helpers';
import { import {
addActionHandler, getGlobal, setGlobal, addActionHandler, getGlobal, setGlobal,
} from './index'; } from './index';

View File

@ -1,6 +1,5 @@
import type { ApiMessage, ApiSponsoredMessage, ApiThreadInfo } from '../../api/types'; import type { ApiMessage, ApiSponsoredMessage, ApiThreadInfo } from '../../api/types';
import type { FocusDirection, ThreadId } from '../../types'; import type { FocusDirection, ThreadId } from '../../types';
import type { MessageKey } from '../../util/messageKey';
import type { import type {
GlobalState, MessageList, MessageListType, TabArgs, TabThread, Thread, GlobalState, MessageList, MessageListType, TabArgs, TabThread, Thread,
} from '../types'; } from '../types';
@ -13,9 +12,9 @@ import { getCurrentTabId } from '../../util/establishMultitabRole';
import { import {
areSortedArraysEqual, excludeSortedArray, omit, pick, pickTruthy, unique, areSortedArraysEqual, excludeSortedArray, omit, pick, pickTruthy, unique,
} from '../../util/iteratees'; } from '../../util/iteratees';
import { isLocalMessageId, type MessageKey } from '../../util/messageKey';
import { import {
hasMessageTtl, hasMessageTtl, mergeIdRanges, orderHistoryIds, orderPinnedIds,
isLocalMessageId, mergeIdRanges, orderHistoryIds, orderPinnedIds,
} from '../helpers'; } from '../helpers';
import { import {
selectChat, selectChat,

View File

@ -22,7 +22,7 @@ import {
import { getCurrentTabId } from '../../util/establishMultitabRole'; import { getCurrentTabId } from '../../util/establishMultitabRole';
import { findLast } from '../../util/iteratees'; import { findLast } from '../../util/iteratees';
import { MEMO_EMPTY_ARRAY } from '../../util/memo'; import { MEMO_EMPTY_ARRAY } from '../../util/memo';
import { getMessageKey } from '../../util/messageKey'; import { getMessageKey, isLocalMessageId } from '../../util/messageKey';
import { getServerTime } from '../../util/serverTime'; import { getServerTime } from '../../util/serverTime';
import { IS_TRANSLATION_SUPPORTED } from '../../util/windowEnvironment'; import { IS_TRANSLATION_SUPPORTED } from '../../util/windowEnvironment';
import { import {
@ -47,7 +47,6 @@ import {
isChatSuperGroup, isChatSuperGroup,
isCommonBoxChat, isCommonBoxChat,
isForwardedMessage, isForwardedMessage,
isLocalMessageId,
isMessageDocumentSticker, isMessageDocumentSticker,
isMessageFailed, isMessageFailed,
isMessageLocal, isMessageLocal,

View File

@ -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 { getActions, getGlobal } from '../global';
import type { Track, TrackId } from '../util/audioPlayer'; import type { Track, TrackId } from '../util/audioPlayer';
@ -20,7 +22,7 @@ type Handler = (e: Event) => void;
const DEFAULT_SKIP_TIME = 10; const DEFAULT_SKIP_TIME = 10;
const useAudioPlayer = ( const useAudioPlayer = (
trackId: TrackId, trackId: TrackId | undefined,
originalDuration: number, // Sometimes incorrect for voice messages originalDuration: number, // Sometimes incorrect for voice messages
trackType: Track['type'], trackType: Track['type'],
src?: string, src?: string,
@ -50,6 +52,9 @@ const useAudioPlayer = (
}); });
useSyncEffect(() => { useSyncEffect(() => {
if (!trackId) {
return;
}
controllerRef.current = register(trackId, trackType, (eventName, e) => { controllerRef.current = register(trackId, trackType, (eventName, e) => {
if (noHandleEvents) { if (noHandleEvents) {
return; return;
@ -141,11 +146,17 @@ const useAudioPlayer = (
requestPreviousTrack, requestPreviousTrack,
setPlaybackRate, setPlaybackRate,
toggleMuted, toggleMuted,
} = controllerRef.current!; } = controllerRef.current ?? {};
const duration = proxy.duration && Number.isFinite(proxy.duration) ? proxy.duration : originalDuration;
const duration = useMemo(() => {
return proxy?.duration && Number.isFinite(proxy.duration) ? proxy.duration : originalDuration;
}, [proxy?.duration, originalDuration]);
// RAF progress // RAF progress
useEffect(() => { useEffect(() => {
if (!proxy) {
return;
}
if (noReset && proxy.currentTime === 0) { if (noReset && proxy.currentTime === 0) {
return; return;
} }
@ -156,7 +167,7 @@ const useAudioPlayer = (
// Cleanup // Cleanup
useEffect(() => () => { useEffect(() => () => {
destroy(noPlaylist); destroy?.(noPlaylist);
}, [destroy, noPlaylist]); }, [destroy, noPlaylist]);
// Autoplay once `src` is present // Autoplay once `src` is present
@ -166,32 +177,32 @@ const useAudioPlayer = (
} }
// When paused by another player // When paused by another player
if (proxy.src && proxy.paused) { if (proxy?.src && proxy?.paused) {
return; return;
} }
if (shouldPlay && src && !isPlaying) { 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(() => { const playIfPresent = useLastCallback(() => {
if (src) { if (src) {
play(src); play?.(src);
} }
}); });
const playPause = useLastCallback(() => { const playPause = useLastCallback(() => {
if (isPlaying) { if (isPlaying) {
pause(); pause?.();
} else { } else {
playIfPresent(); playIfPresent();
} }
}); });
const setTime = useLastCallback((time: number) => { const setTime = useLastCallback((time: number) => {
setCurrentTime(time); setCurrentTime?.(time);
if (duration) { if (duration && proxy) {
setPlayProgress(proxy.currentTime / duration); setPlayProgress(proxy.currentTime / duration);
} }
}); });

View File

@ -56,6 +56,7 @@ const useBuffering = (noInitiallyBuffered = false, onTimeUpdate?: AnyToVoidFunct
}); });
const bufferingHandlers = { const bufferingHandlers = {
onPLay: handleBuffering,
onLoadedData: handleBuffering, onLoadedData: handleBuffering,
onPlaying: handleBuffering, onPlaying: handleBuffering,
onLoadStart: handleBuffering, // Needed for Safari to start onLoadStart: handleBuffering, // Needed for Safari to start

View File

@ -6,7 +6,7 @@ import { AudioOrigin, GlobalSearchContent } from '../types';
import { requestNextMutation } from '../lib/fasterdom/fasterdom'; import { requestNextMutation } from '../lib/fasterdom/fasterdom';
import { selectCurrentMessageList, selectTabState } from '../global/selectors'; import { selectCurrentMessageList, selectTabState } from '../global/selectors';
import { getMessageKey, parseMessageKey } from './messageKey'; import { getMessageServerKey, parseMessageKey } from './messageKey';
import { isSafariPatchInProgress, patchSafariProgressiveAudio } from './patchSafariProgressiveAudio'; import { isSafariPatchInProgress, patchSafariProgressiveAudio } from './patchSafariProgressiveAudio';
import safePlay from './safePlay'; import safePlay from './safePlay';
import { IS_SAFARI } from './windowEnvironment'; import { IS_SAFARI } from './windowEnvironment';
@ -24,6 +24,7 @@ export interface Track {
} }
const tracks = new Map<TrackId, Track>(); const tracks = new Map<TrackId, Track>();
let voiceQueue: TrackId[] = []; let voiceQueue: TrackId[] = [];
let musicQueue: TrackId[] = []; let musicQueue: TrackId[] = [];
@ -331,8 +332,12 @@ function findNextInQueue(currentId: TrackId, origin = AudioOrigin.Inline, isReve
return chatAudio[index + direction]; return chatAudio[index + direction];
} }
export function makeTrackId(message: ApiMessage): TrackId { export function makeTrackId(message: ApiMessage): TrackId | undefined {
return `${getMessageKey(message)}-${message.date}`; const key = getMessageServerKey(message);
if (!key) {
return undefined;
}
return `${key}-${message.date}`;
} }
function splitTrackId(trackId: TrackId) { function splitTrackId(trackId: TrackId) {

View File

@ -8,7 +8,15 @@ export function getMessageKey(message: ApiMessage): MessageKey {
return buildMessageKey(chatId, previousLocalId || id); 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}`; return `msg${chatId}-${msgId}`;
} }
@ -17,3 +25,7 @@ export function parseMessageKey(key: MessageKey) {
return { chatId: match[1], messageId: Number(match[2]) }; return { chatId: match[1], messageId: Number(match[2]) };
} }
export function isLocalMessageId(id: number) {
return !Number.isInteger(id);
}