diff --git a/package-lock.json b/package-lock.json index 0f348597e..f77f4144d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -105,7 +105,6 @@ } }, "dev/eslint-multitab": { - "name": "eslint-plugin-eslint-multitab-tt", "version": "0.0.0", "dev": true, "license": "ISC", diff --git a/src/api/gramjs/localDb.ts b/src/api/gramjs/localDb.ts index f372e9c0a..e581d921e 100644 --- a/src/api/gramjs/localDb.ts +++ b/src/api/gramjs/localDb.ts @@ -1,6 +1,5 @@ import BigInt from 'big-integer'; import type { Api as GramJs } from '../../lib/gramjs'; -import type { ApiMessage } from '../types'; import { omitVirtualClassFields } from './apiBuilders/helpers'; import { DATA_BROADCAST_CHANNEL_NAME } from '../../config'; import { constructors } from '../../lib/gramjs/tl'; @@ -10,7 +9,6 @@ import { throttle } from '../../util/schedulers'; const IS_MULTITAB_SUPPORTED = 'BroadcastChannel' in self; export interface LocalDb { - localMessages: Record; // Used for loading avatars and media through in-memory Gram JS instances. chats: Record; users: Record; diff --git a/src/api/gramjs/methods/client.ts b/src/api/gramjs/methods/client.ts index 06c006191..241beb3a7 100644 --- a/src/api/gramjs/methods/client.ts +++ b/src/api/gramjs/methods/client.ts @@ -232,7 +232,7 @@ export async function invokeRequest( } if (!shouldIgnoreUpdates) { - handleUpdatesFromRequest(request, result); + handleUpdates(result); } return shouldReturnTrue ? result && true : result; @@ -256,7 +256,7 @@ export async function invokeRequest( } } -function handleUpdatesFromRequest(request: T, result: T['__response']) { +export function handleUpdates(result: {}) { let manyUpdates; let singleUpdate; @@ -279,10 +279,10 @@ function handleUpdatesFromRequest(request: T, resul injectUpdateEntities(manyUpdates); manyUpdates.updates.forEach((update) => { - updater(update, request); + updater(update); }); } else if (singleUpdate) { - updater(singleUpdate, request); + updater(singleUpdate); } } diff --git a/src/api/gramjs/methods/messages.ts b/src/api/gramjs/methods/messages.ts index 743e03638..d8a3556cc 100644 --- a/src/api/gramjs/methods/messages.ts +++ b/src/api/gramjs/methods/messages.ts @@ -30,7 +30,7 @@ import { SUPPORTED_IMAGE_CONTENT_TYPES, SUPPORTED_VIDEO_CONTENT_TYPES, } from '../../../config'; -import { invokeRequest, uploadFile } from './client'; +import { handleUpdates, invokeRequest, uploadFile } from './client'; import { buildApiMessage, buildLocalForwardedMessage, @@ -38,6 +38,8 @@ import { buildWebPage, buildApiSponsoredMessage, buildApiFormattedText, + buildMessageTextContent, + buildMessageMediaContent, } from '../apiBuilders/messages'; import { buildApiUser } from '../apiBuilders/users'; import { @@ -54,8 +56,8 @@ import { buildSendMessageAction, buildInputPollFromExisting, buildInputTextWithEntities, + buildMessageFromUpdate, } from '../gramjsBuilders'; -import localDb from '../localDb'; import { buildApiChatFromPreview, buildApiSendAsPeerId } from '../apiBuilders/chats'; import { fetchFile } from '../../../util/files'; import { @@ -287,7 +289,6 @@ export function sendMessage( }, FAST_SEND_TIMEOUT); const randomId = generateRandomBigInt(); - localDb.localMessages[String(randomId)] = localMessage; if (groupedId) { return sendGroupedMedia({ @@ -339,7 +340,7 @@ export function sendMessage( const RequestClass = media ? GramJs.messages.SendMedia : GramJs.messages.SendMessage; try { - await invokeRequest(new RequestClass({ + const update = await invokeRequest(new RequestClass({ clearDraft: true, message: text || '', entities: entities ? entities.map(buildMtpMessageEntity) : undefined, @@ -353,7 +354,8 @@ export function sendMessage( ...(noWebPage && { noWebpage: noWebPage }), ...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }), ...(shouldUpdateStickerSetsOrder && { updateStickersetsOrder: shouldUpdateStickerSetsOrder }), - }), true, true); + }), false, true, true); + if (update) handleLocalMessageUpdate(localMessage, update); } catch (error: any) { onUpdate({ '@type': 'updateMessageSendFailed', @@ -371,6 +373,7 @@ export function sendMessage( const groupedUploads: Record; + localMessages: Record; }> = {}; function sendGroupedMedia( @@ -406,6 +409,7 @@ function sendGroupedMedia( groupedUploads[groupedId] = { counter: 0, singleMediaByIndex: {}, + localMessages: {}, }; } @@ -453,15 +457,16 @@ function sendGroupedMedia( message: text || '', entities: entities ? entities.map(buildMtpMessageEntity) : undefined, }); + groupedUploads[groupedId].localMessages[randomId.toString()] = localMessage; if (Object.keys(groupedUploads[groupedId].singleMediaByIndex).length < groupedUploads[groupedId].counter) { return; } - const { singleMediaByIndex } = groupedUploads[groupedId]; + const { singleMediaByIndex, localMessages } = groupedUploads[groupedId]; delete groupedUploads[groupedId]; - await invokeRequest(new GramJs.messages.SendMultiMedia({ + const update = await invokeRequest(new GramJs.messages.SendMultiMedia({ clearDraft: true, peer: buildInputPeer(chat.id, chat.accessHash), multiMedia: Object.values(singleMediaByIndex), // Object keys are usually ordered @@ -470,7 +475,9 @@ function sendGroupedMedia( ...(isSilent && { silent: isSilent }), ...(scheduledAt && { scheduleDate: scheduledAt }), ...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }), - }), true); + }), false, undefined, true); + + if (update) handleMultipleLocalMessagesUpdate(localMessages, update); })(); return queue; @@ -554,9 +561,6 @@ export async function editMessage({ message: messageUpdate, }); - // TODO Revise intersecting with scheduled - localDb.localMessages[message.id] = { ...message, ...messageUpdate }; - const mtpEntities = entities && entities.map(buildMtpMessageEntity); await invokeRequest(new GramJs.messages.EditMessage({ @@ -1267,6 +1271,7 @@ export async function forwardMessages({ }) { const messageIds = messages.map(({ id }) => id); const randomIds = messages.map(generateRandomBigInt); + const localMessages: Record = {}; messages.forEach((message, index) => { const localMessage = buildLocalForwardedMessage({ @@ -1278,7 +1283,7 @@ export async function forwardMessages({ noCaptions, isCurrentUserPremium, }); - localDb.localMessages[String(randomIds[index])] = localMessage; + localMessages[randomIds[index].toString()] = localMessage; onUpdate({ '@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage', @@ -1288,7 +1293,7 @@ export async function forwardMessages({ }); }); - await invokeRequest(new GramJs.messages.ForwardMessages({ + const update = await invokeRequest(new GramJs.messages.ForwardMessages({ fromPeer: buildInputPeer(fromChat.id, fromChat.accessHash), toPeer: buildInputPeer(toChat.id, toChat.accessHash), randomId: randomIds, @@ -1300,7 +1305,8 @@ export async function forwardMessages({ ...(toThreadId && { topMsgId: toThreadId }), ...(scheduledAt && { scheduleDate: scheduledAt }), ...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }), - }), true); + }), false, undefined, true); + if (update) handleMultipleLocalMessagesUpdate(localMessages, update); } export async function findFirstMessageIdAfterDate({ @@ -1632,3 +1638,74 @@ export async function translateText(params: TranslateTextParams) { return formattedText; } + +function handleMultipleLocalMessagesUpdate( + localMessages: Record, update: GramJs.TypeUpdates, +) { + if (!('updates' in update)) return; + update.updates.forEach((u) => { + if (u instanceof GramJs.UpdateMessageID) { + const localMessage = localMessages[u.randomId.toString()]; + handleLocalMessageUpdate(localMessage, u); + } + }); +} + +function handleLocalMessageUpdate(localMessage: ApiMessage, update: GramJs.TypeUpdates) { + let messageUpdate; + if (update instanceof GramJs.UpdateShortSentMessage || update instanceof GramJs.UpdateMessageID) { + messageUpdate = update; + } else if ('updates' in update) { + messageUpdate = update.updates.find((u): u is GramJs.UpdateMessageID => u instanceof GramJs.UpdateMessageID); + } + + if (!messageUpdate) { + handleUpdates(update); + return; + } + + let newContent: ApiMessage['content'] | undefined; + if (messageUpdate instanceof GramJs.UpdateShortSentMessage) { + if (localMessage.content.text && messageUpdate.entities) { + newContent = { + text: buildMessageTextContent(localMessage.content.text.text, messageUpdate.entities), + }; + } + if (messageUpdate.media) { + newContent = { + ...newContent, + ...buildMessageMediaContent(messageUpdate.media), + }; + } + + const mtpMessage = buildMessageFromUpdate(messageUpdate.id, localMessage.chatId, messageUpdate); + if (isMessageWithMedia(mtpMessage)) { + addMessageToLocalDb(mtpMessage); + } + } + + // Edge case for "Send When Online" + const isSentBefore = 'date' in messageUpdate && messageUpdate.date * 1000 < Date.now() + getServerTimeOffset() * 1000; + + onUpdate({ + '@type': localMessage.isScheduled && !isSentBefore + ? 'updateScheduledMessageSendSucceeded' + : 'updateMessageSendSucceeded', + chatId: localMessage.chatId, + localId: localMessage.id, + message: { + ...localMessage, + ...(newContent && { + content: { + ...localMessage.content, + ...newContent, + }, + }), + id: messageUpdate.id, + sendingState: undefined, + ...('date' in messageUpdate && { date: messageUpdate.date }), + }, + }); + + handleUpdates(update); +} diff --git a/src/api/gramjs/updater.ts b/src/api/gramjs/updater.ts index 21b87f4ed..414c00c9e 100644 --- a/src/api/gramjs/updater.ts +++ b/src/api/gramjs/updater.ts @@ -12,7 +12,6 @@ import { buildApiMessageFromShort, buildApiMessageFromShortChat, buildMessageMediaContent, - buildMessageTextContent, buildPoll, buildPollResults, buildApiMessageFromNotification, @@ -116,7 +115,7 @@ function dispatchUserAndChatUpdates(entities: (GramJs.TypeUser | GramJs.TypeChat }); } -export function updater(update: Update, originRequest?: GramJs.AnyRequest) { +export function updater(update: Update) { if (update instanceof connection.UpdateServerTimeOffset) { setServerTimeOffset(update.timeOffset); @@ -440,70 +439,8 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) { message, }); } - } else if (( - originRequest instanceof GramJs.messages.SendMessage - || originRequest instanceof GramJs.messages.SendMedia - || originRequest instanceof GramJs.messages.SendMultiMedia - || originRequest instanceof GramJs.messages.ForwardMessages - ) && ( - update instanceof GramJs.UpdateMessageID || update instanceof GramJs.UpdateShortSentMessage - )) { - let randomId; - if ('randomId' in update) { - randomId = update.randomId; - } else if ('randomId' in originRequest) { - randomId = originRequest.randomId; - } - - const localMessage = randomId && localDb.localMessages[String(randomId)]; - if (!localMessage) { - throw new Error('Local message not found'); - } - - let newContent: ApiMessage['content'] | undefined; - if (update instanceof GramJs.UpdateShortSentMessage) { - if (localMessage.content.text && update.entities) { - newContent = { - text: buildMessageTextContent(localMessage.content.text.text, update.entities), - }; - } - if (update.media) { - newContent = { - ...newContent, - ...buildMessageMediaContent(update.media), - }; - } - - const mtpMessage = buildMessageFromUpdate(update.id, localMessage.chatId, update); - if (isMessageWithMedia(mtpMessage)) { - addMessageToLocalDb(mtpMessage); - } - } - + } else if (update instanceof GramJs.UpdateMessageID || update instanceof GramJs.UpdateShortSentMessage) { sentMessageIds.add(update.id); - - // Edge case for "Send When Online" - const isAlreadySent = 'date' in update && update.date * 1000 < Date.now() + getServerTimeOffset() * 1000; - - onUpdate({ - '@type': localMessage.isScheduled && !isAlreadySent - ? 'updateScheduledMessageSendSucceeded' - : 'updateMessageSendSucceeded', - chatId: localMessage.chatId, - localId: localMessage.id, - message: { - ...localMessage, - ...(newContent && { - content: { - ...localMessage.content, - ...newContent, - }, - }), - id: update.id, - sendingState: undefined, - ...('date' in update && { date: update.date }), - }, - }); } else if (update instanceof GramJs.UpdateReadMessagesContents) { onUpdate({ '@type': 'updateCommonBoxMessages', diff --git a/src/api/gramjs/worker/provider.ts b/src/api/gramjs/worker/provider.ts index f4327d638..b8fc10c79 100644 --- a/src/api/gramjs/worker/provider.ts +++ b/src/api/gramjs/worker/provider.ts @@ -25,7 +25,6 @@ let worker: Worker; const requestStates = new Map(); const requestStatesByCallback = new Map(); const savedLocalDb: LocalDb = { - localMessages: {}, chats: {}, users: {}, messages: {},