From 0c32fede2975481c70c3ede2bb31d96492c88ec3 Mon Sep 17 00:00:00 2001 From: zubiden <19638254+zubiden@users.noreply.github.com> Date: Tue, 5 May 2026 13:46:25 +0200 Subject: [PATCH] Multitab: Force sync at least every 500ms (#6914) --- src/api/gramjs/methods/messages.ts | 28 ++++++++++++++++------ src/util/browser/multitab.ts | 37 ++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/api/gramjs/methods/messages.ts b/src/api/gramjs/methods/messages.ts index 912579259..318cca519 100644 --- a/src/api/gramjs/methods/messages.ts +++ b/src/api/gramjs/methods/messages.ts @@ -379,9 +379,10 @@ export function sendApiMessage( if (!chat) return undefined; - // This is expected to arrive after `updateMessageSendSucceeded` which replaces the local ID, - // so in most cases this will be simply ignored + let isSendCompleted = false; const timeout = setTimeout(() => { + if (isSendCompleted) return; + sendApiUpdate({ '@type': localMessage.isScheduled ? 'updateScheduledMessage' : 'updateMessage', id: localMessage.id, @@ -392,6 +393,10 @@ export function sendApiMessage( isFull: false, }); }, FAST_SEND_TIMEOUT); + const cancelSendingStatusTimeout = () => { + isSendCompleted = true; + clearTimeout(timeout); + }; const randomId = generateRandomBigInt(); @@ -408,7 +413,7 @@ export function sendApiMessage( scheduledAt, scheduleRepeatPeriod, messagePriceInStars, - }, randomId, localMessage, onProgress); + }, randomId, localMessage, onProgress, cancelSendingStatusTimeout); } const messagePromise = (async () => { @@ -564,8 +569,11 @@ export function sendApiMessage( }); } + cancelSendingStatusTimeout(); if (update) handleLocalMessageUpdate(localMessage, update); } catch (error: any) { + cancelSendingStatusTimeout(); + if (error.errorMessage === 'PRIVACY_PREMIUM_REQUIRED') { sendApiUpdate({ '@type': 'updateRequestUserUpdate', id: chat.id }); } @@ -576,7 +584,6 @@ export function sendApiMessage( localId: localMessage.id, error: error.errorMessage, }); - clearTimeout(timeout); } })(); @@ -595,6 +602,7 @@ const groupedUploads: Record; localMessages: Record; + cancelSendingStatusTimeouts: Record; }> = {}; function sendGroupedMedia( @@ -627,7 +635,8 @@ function sendGroupedMedia( }, randomId: GramJs.long, localMessage: ApiMessage, - onProgress?: ApiOnProgress, + onProgress: ApiOnProgress | undefined, + cancelSendingStatusTimeout: NoneToVoidFunction, ) { let groupIndex = -1; if (!groupedUploads[groupedId]) { @@ -635,6 +644,7 @@ function sendGroupedMedia( counter: 0, singleMediaByIndex: {}, localMessages: {}, + cancelSendingStatusTimeouts: {}, }; } @@ -689,12 +699,13 @@ function sendGroupedMedia( entities: entities ? entities.map(buildMtpMessageEntity) : undefined, }); groupedUploads[groupedId].localMessages[randomId.toString()] = localMessage; + groupedUploads[groupedId].cancelSendingStatusTimeouts[randomId.toString()] = cancelSendingStatusTimeout; if (Object.keys(groupedUploads[groupedId].singleMediaByIndex).length < groupedUploads[groupedId].counter) { return; } - const { singleMediaByIndex, localMessages } = groupedUploads[groupedId]; + const { singleMediaByIndex, localMessages, cancelSendingStatusTimeouts } = groupedUploads[groupedId]; delete groupedUploads[groupedId]; const count = Object.values(singleMediaByIndex).length; @@ -713,7 +724,10 @@ function sendGroupedMedia( shouldIgnoreUpdates: true, }); - if (update) handleMultipleLocalMessagesUpdate(localMessages, update); + if (!update) return; + + Object.values(cancelSendingStatusTimeouts).forEach((cancel) => cancel()); + handleMultipleLocalMessagesUpdate(localMessages, update); })(); return mediaQueue; diff --git a/src/util/browser/multitab.ts b/src/util/browser/multitab.ts index c83f2f2b0..d9663db17 100644 --- a/src/util/browser/multitab.ts +++ b/src/util/browser/multitab.ts @@ -94,6 +94,7 @@ type BroadcastChannelInitApi = { }; const MULTITAB_ESTABLISH_TIMEOUT = 800; +const BROADCAST_DIFF_FALLBACK_TIMEOUT = 500; export type TypedBroadcastChannel = { postMessage: (message: BroadcastChannelMessage) => void; @@ -117,6 +118,28 @@ const channel = new BroadcastChannel(DATA_BROADCAST_CHANNEL_NAME) as TypedBroadc let isBroadcastDiffScheduled = false; let lastBroadcastDiffGlobal: GlobalState | undefined; +let broadcastDiffFallbackTimeout: number | undefined; + +function flushBroadcastDiff() { + if (!isBroadcastDiffScheduled) return; + + if (broadcastDiffFallbackTimeout) { + clearTimeout(broadcastDiffFallbackTimeout); + broadcastDiffFallbackTimeout = undefined; + } + + const diff = deepDiff(lastBroadcastDiffGlobal, currentGlobal); + + if (typeof diff !== 'symbol') { + channel.postMessage({ + type: 'globalDiffUpdate', + diff, + }); + } + + isBroadcastDiffScheduled = false; + lastBroadcastDiffGlobal = undefined; +} function broadcastDiffOnIdle() { if (isBroadcastDiffScheduled) return; @@ -124,18 +147,8 @@ function broadcastDiffOnIdle() { isBroadcastDiffScheduled = true; lastBroadcastDiffGlobal = currentGlobal; - onFullyIdle(() => { - const diff = deepDiff(lastBroadcastDiffGlobal, currentGlobal); - - if (typeof diff !== 'symbol') { - channel.postMessage({ - type: 'globalDiffUpdate', - diff, - }); - } - - isBroadcastDiffScheduled = false; - }); + onFullyIdle(flushBroadcastDiff); + broadcastDiffFallbackTimeout = window.setTimeout(flushBroadcastDiff, BROADCAST_DIFF_FALLBACK_TIMEOUT); } export function unsubcribeFromMultitabBroadcastChannel() {