diff --git a/src/api/gramjs/apiBuilders/users.ts b/src/api/gramjs/apiBuilders/users.ts index 950aa30bd..991c58fad 100644 --- a/src/api/gramjs/apiBuilders/users.ts +++ b/src/api/gramjs/apiBuilders/users.ts @@ -128,27 +128,16 @@ export function buildApiUserStatus(mtpStatus?: GramJs.TypeUserStatus): ApiUserSt } } -export function buildApiUsersAndStatuses(mtpUsers: GramJs.TypeUser[]) { +export function buildApiUserStatuses(mtpUsers: GramJs.TypeUser[]) { const userStatusesById: Record = {}; - const usersById: Record = {}; - mtpUsers.forEach((mtpUser) => { - const user = buildApiUser(mtpUser); - if (!user) { - return; - } - - const duplicateUser = usersById[user.id]; - if (!duplicateUser || duplicateUser.isMin) { - usersById[user.id] = user; - } - if ('status' in mtpUser) { - userStatusesById[user.id] = buildApiUserStatus(mtpUser.status); + const userId = buildApiPeerId(mtpUser.id, 'user'); + userStatusesById[userId] = buildApiUserStatus(mtpUser.status); } }); - return { users: Object.values(usersById), userStatusesById }; + return userStatusesById; } export function buildApiPremiumGiftOption(option: GramJs.TypePremiumGiftOption): ApiPremiumGiftOption { diff --git a/src/api/gramjs/helpers.ts b/src/api/gramjs/helpers.ts index 43f1f8bd9..67656741b 100644 --- a/src/api/gramjs/helpers.ts +++ b/src/api/gramjs/helpers.ts @@ -199,16 +199,6 @@ export function addUserToLocalDb(user: GramJs.User) { localDb.users[id] = user; } -export function addEntitiesToLocalDb(entities: (GramJs.TypeUser | GramJs.TypeChat)[]) { - entities.forEach((entity) => { - if (entity instanceof GramJs.User) { - addUserToLocalDb(entity); - } else if ((entity instanceof GramJs.Chat || entity instanceof GramJs.Channel)) { - addChatToLocalDb(entity); - } - }); -} - export function addWebDocumentToLocalDb(webDocument: GramJs.TypeWebDocument) { localDb.webDocuments[webDocument.url] = webDocument; } diff --git a/src/api/gramjs/methods/account.ts b/src/api/gramjs/methods/account.ts index be6dbdb64..d1c611679 100644 --- a/src/api/gramjs/methods/account.ts +++ b/src/api/gramjs/methods/account.ts @@ -5,9 +5,7 @@ import type { ApiPeer, ApiPhoto, ApiReportReason, } from '../../types'; -import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { buildApiChatLink } from '../apiBuilders/misc'; -import { buildApiUser } from '../apiBuilders/users'; import { buildInputPeer, buildInputPhoto, buildInputReportReason } from '../gramjsBuilders'; import { invokeRequest } from './client'; @@ -83,14 +81,9 @@ export async function resolveBusinessChatLink({ slug } : { slug: string }) { }); if (!result) return undefined; - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); - const chatLink = buildApiChatLink(result); return { - users, - chats, chatLink, }; } diff --git a/src/api/gramjs/methods/auth.ts b/src/api/gramjs/methods/auth.ts index becff1093..240112b12 100644 --- a/src/api/gramjs/methods/auth.ts +++ b/src/api/gramjs/methods/auth.ts @@ -5,10 +5,10 @@ import type { ApiUpdateAuthorizationStateType, ApiUser, ApiUserFullInfo, - OnApiUpdate, } from '../../types'; import { DEBUG } from '../../../config'; +import { sendApiUpdate } from '../updates/apiUpdateEmitter'; const ApiErrors: { [k: string]: string } = { PHONE_NUMBER_INVALID: 'Invalid phone number.', @@ -23,20 +23,14 @@ const authController: { reject?: Function; } = {}; -let onUpdate: OnApiUpdate; - -export function init(_onUpdate: OnApiUpdate) { - onUpdate = _onUpdate; -} - export function onWebAuthTokenFailed() { - onUpdate({ + sendApiUpdate({ '@type': 'updateWebAuthTokenFailed', }); } export function onRequestPhoneNumber() { - onUpdate(buildAuthStateUpdate('authorizationStateWaitPhoneNumber')); + sendApiUpdate(buildAuthStateUpdate('authorizationStateWaitPhoneNumber')); return new Promise((resolve, reject) => { authController.resolve = resolve; @@ -45,7 +39,7 @@ export function onRequestPhoneNumber() { } export function onRequestCode(isCodeViaApp = false) { - onUpdate({ + sendApiUpdate({ ...buildAuthStateUpdate('authorizationStateWaitCode'), isCodeViaApp, }); @@ -57,7 +51,7 @@ export function onRequestCode(isCodeViaApp = false) { } export function onRequestPassword(hint?: string, noReset?: boolean) { - onUpdate({ + sendApiUpdate({ ...buildAuthStateUpdate('authorizationStateWaitPassword'), hint, noReset, @@ -69,7 +63,7 @@ export function onRequestPassword(hint?: string, noReset?: boolean) { } export function onRequestRegistration() { - onUpdate(buildAuthStateUpdate('authorizationStateWaitRegistration')); + sendApiUpdate(buildAuthStateUpdate('authorizationStateWaitRegistration')); return new Promise<[string, string?]>((resolve) => { authController.resolve = resolve; @@ -77,7 +71,7 @@ export function onRequestRegistration() { } export function onRequestQrCode(qrCode: { token: Buffer; expires: number }) { - onUpdate({ + sendApiUpdate({ ...buildAuthStateUpdate('authorizationStateWaitQrCode'), qrCode: { token: btoa(String.fromCharCode(...qrCode.token)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), @@ -109,18 +103,18 @@ export function onAuthError(err: Error) { } } - onUpdate({ + sendApiUpdate({ '@type': 'updateAuthorizationError', message, }); } export function onAuthReady() { - onUpdate(buildAuthStateUpdate('authorizationStateReady')); + sendApiUpdate(buildAuthStateUpdate('authorizationStateReady')); } export function onCurrentUserUpdate(currentUser: ApiUser, currentUserFullInfo: ApiUserFullInfo) { - onUpdate({ + sendApiUpdate({ '@type': 'updateCurrentUser', currentUser, currentUserFullInfo, diff --git a/src/api/gramjs/methods/bots.ts b/src/api/gramjs/methods/bots.ts index 1166e85f9..dc5ab535f 100644 --- a/src/api/gramjs/methods/bots.ts +++ b/src/api/gramjs/methods/bots.ts @@ -9,7 +9,6 @@ import type { ApiPeer, ApiThemeParameters, ApiUser, - OnApiUpdate, } from '../../types'; import { WEB_APP_PLATFORM } from '../../../config'; @@ -37,20 +36,14 @@ import { } from '../gramjsBuilders'; import { addDocumentToLocalDb, - addEntitiesToLocalDb, addPhotoToLocalDb, addUserToLocalDb, addWebDocumentToLocalDb, deserializeBytes, } from '../helpers'; +import { sendApiUpdate } from '../updates/apiUpdateEmitter'; import { invokeRequest } from './client'; -let onUpdate: OnApiUpdate; - -export function init(_onUpdate: OnApiUpdate) { - onUpdate = _onUpdate; -} - export async function answerCallbackButton({ chatId, accessHash, messageId, data, isGame, }: { @@ -80,7 +73,6 @@ export async function fetchTopInlineBots() { return { ids, - users, }; } @@ -98,7 +90,6 @@ export async function fetchTopBotApps() { return { ids, - users, }; } @@ -140,15 +131,12 @@ export async function fetchInlineBotResults({ return undefined; } - addEntitiesToLocalDb(result.users); - return { isGallery: Boolean(result.gallery), help: bot.botPlaceholder, nextOffset: getInlineBotResultsNextOffset(bot.usernames![0].username, result.nextOffset), switchPm: buildBotSwitchPm(result.switchPm), switchWebview: buildBotSwitchWebview(result.switchWebview), - users: result.users.map(buildApiUser).filter(Boolean), results: processInlineBotResult(String(result.queryId), result.results), cacheTime: result.cacheTime, }; @@ -394,11 +382,9 @@ export async function loadAttachBots({ })); if (result instanceof GramJs.AttachMenuBots) { - addEntitiesToLocalDb(result.users); return { hash: result.hash.toString(), bots: buildCollectionByKey(result.bots.map(buildApiAttachBot), 'id'), - users: result.users.map(buildApiUser).filter(Boolean), }; } return undefined; @@ -414,10 +400,8 @@ export async function loadAttachBot({ })); if (result instanceof GramJs.AttachMenuBotsBot) { - addEntitiesToLocalDb(result.users); return { bot: buildApiAttachBot(result.bot), - users: result.users.map(buildApiUser).filter(Boolean), }; } return undefined; @@ -456,7 +440,7 @@ export async function requestBotUrlAuth({ const authResult = buildApiUrlAuthResult(result); if (authResult?.type === 'request') { - onUpdate({ + sendApiUpdate({ '@type': 'updateUser', id: authResult.bot.id, user: authResult.bot, @@ -487,7 +471,7 @@ export async function acceptBotUrlAuth({ const authResult = buildApiUrlAuthResult(result); if (authResult?.type === 'request') { - onUpdate({ + sendApiUpdate({ '@type': 'updateUser', id: authResult.bot.id, user: authResult.bot, @@ -505,7 +489,7 @@ export async function requestLinkUrlAuth({ url }: { url: string }) { const authResult = buildApiUrlAuthResult(result); if (authResult?.type === 'request') { - onUpdate({ + sendApiUpdate({ '@type': 'updateUser', id: authResult.bot.id, user: authResult.bot, @@ -524,7 +508,7 @@ export async function acceptLinkUrlAuth({ url, isWriteAllowed }: { url: string; const authResult = buildApiUrlAuthResult(result); if (authResult?.type === 'request') { - onUpdate({ + sendApiUpdate({ '@type': 'updateUser', id: authResult.bot.id, user: authResult.bot, @@ -663,14 +647,11 @@ export async function fetchPopularAppBots({ return undefined; } - addEntitiesToLocalDb(result.users); - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.users.map((c) => buildApiChatFromPreview(c)).filter(Boolean); + const peerIds = users.map(({ id }) => id); return { - users, - chats, + peerIds, nextOffset: result.nextOffset, }; } diff --git a/src/api/gramjs/methods/calls.ts b/src/api/gramjs/methods/calls.ts index c0eeb2cc2..5b8724989 100644 --- a/src/api/gramjs/methods/calls.ts +++ b/src/api/gramjs/methods/calls.ts @@ -3,8 +3,7 @@ import { Api as GramJs } from '../../../lib/gramjs'; import type { JoinGroupCallPayload } from '../../../lib/secret-sauce'; import type { - ApiChat, ApiGroupCall, ApiPhoneCall, - ApiUser, OnApiUpdate, + ApiChat, ApiGroupCall, ApiPhoneCall, ApiUser, } from '../../types'; import { GROUP_CALL_PARTICIPANTS_LIMIT } from '../../../config'; @@ -13,20 +12,12 @@ import { buildApiGroupCallParticipant, buildCallProtocol, buildPhoneCall, } from '../apiBuilders/calls'; -import { buildApiChatFromPreview } from '../apiBuilders/chats'; -import { buildApiUser } from '../apiBuilders/users'; import { buildInputGroupCall, buildInputPeer, buildInputPhoneCall, generateRandomInt, } from '../gramjsBuilders'; -import { addEntitiesToLocalDb } from '../helpers'; +import { sendApiUpdate } from '../updates/apiUpdateEmitter'; import { invokeRequest, invokeRequestBeacon } from './client'; -let onUpdate: OnApiUpdate; - -export function init(_onUpdate: OnApiUpdate) { - onUpdate = _onUpdate; -} - export async function getGroupCall({ call, }: { @@ -40,16 +31,8 @@ export async function getGroupCall({ return undefined; } - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); - return { groupCall: buildApiGroupCall(result.call), - users, - chats, }; } @@ -130,25 +113,15 @@ export async function fetchGroupCallParticipants({ })); if (!result) { - return undefined; + return; } - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); - - onUpdate({ + sendApiUpdate({ '@type': 'updateGroupCallParticipants', groupCallId: call.id, participants: result.participants.map(buildApiGroupCallParticipant), nextOffset: result.nextOffset, }); - - return { - users, chats, - }; } export function leaveGroupCall({ @@ -316,16 +289,12 @@ export async function requestCall({ const call = buildPhoneCall(result.phoneCall); - onUpdate({ + sendApiUpdate({ '@type': 'updatePhoneCall', call, }); - addEntitiesToLocalDb(result.users); - - return { - users: result.users.map(buildApiUser).filter(Boolean), - }; + return true; } export function setCallRating({ @@ -369,16 +338,12 @@ export async function acceptCall({ call = buildPhoneCall(result.phoneCall); - onUpdate({ + sendApiUpdate({ '@type': 'updatePhoneCall', call, }); - addEntitiesToLocalDb(result.users); - - return { - users: result.users.map(buildApiUser).filter(Boolean), - }; + return true; } export async function confirmCall({ @@ -399,16 +364,12 @@ export async function confirmCall({ call = buildPhoneCall(result.phoneCall); - onUpdate({ + sendApiUpdate({ '@type': 'updatePhoneCall', call, }); - addEntitiesToLocalDb(result.users); - - return { - users: result.users.map(buildApiUser).filter(Boolean), - }; + return true; } export function sendSignalingData({ diff --git a/src/api/gramjs/methods/chats.ts b/src/api/gramjs/methods/chats.ts index eae6b4c48..80011c5d0 100644 --- a/src/api/gramjs/methods/chats.ts +++ b/src/api/gramjs/methods/chats.ts @@ -17,7 +17,6 @@ import type { ApiTopic, ApiUser, ApiUserStatus, - OnApiUpdate, } from '../../types'; import { @@ -54,7 +53,7 @@ import { buildApiPhoto } from '../apiBuilders/common'; import { buildApiMessage, buildMessageDraft } from '../apiBuilders/messages'; import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers'; import { buildStickerSet } from '../apiBuilders/symbols'; -import { buildApiUser, buildApiUsersAndStatuses } from '../apiBuilders/users'; +import { buildApiUser, buildApiUserStatuses } from '../apiBuilders/users'; import { buildChatAdminRights, buildChatBannedRights, @@ -68,22 +67,19 @@ import { generateRandomBigInt, } from '../gramjsBuilders'; import { - addEntitiesToLocalDb, - addMessageToLocalDb, addPhotoToLocalDb, deserializeBytes, isChatFolder, } from '../helpers'; import { scheduleMutedChatUpdate } from '../scheduleUnmute'; +import { sendApiUpdate } from '../updates/apiUpdateEmitter'; import { applyState, processAffectedHistory, updateChannelState, } from '../updates/updateManager'; -import { dispatchThreadInfoUpdates } from '../updates/updater'; import { handleGramJsUpdate, invokeRequest, uploadFile } from './client'; type FullChatData = { fullInfo: ApiChatFullInfo; - users: ApiUser[]; chats: ApiChat[]; userStatusesById: { [userId: string]: ApiUserStatus }; groupCall?: Partial; @@ -106,12 +102,6 @@ type ChatListData = { nextOffsetDate?: number; }; -let onUpdate: OnApiUpdate; - -export function init(_onUpdate: OnApiUpdate) { - onUpdate = _onUpdate; -} - export async function fetchChats({ limit, offsetDate, @@ -147,13 +137,6 @@ export async function fetchChats({ return undefined; } - if (resultPinned) { - dispatchThreadInfoUpdates(resultPinned.messages); - updateLocalDb(resultPinned); - } - dispatchThreadInfoUpdates(result.messages); - updateLocalDb(result); - const messages = (resultPinned ? resultPinned.messages : []) .concat(result.messages) .map(buildApiMessage) @@ -167,7 +150,7 @@ export async function fetchChats({ const chats: ApiChat[] = []; const draftsById: Record = {}; - const dialogs = (resultPinned ? resultPinned.dialogs : []).concat(result.dialogs); + const dialogs = (resultPinned?.dialogs || []).concat(result.dialogs); const orderedPinnedIds: string[] = []; const lastMessageByChatId: Record = {}; @@ -202,7 +185,7 @@ export async function fetchChats({ chats.push(chat); - scheduleMutedChatUpdate(chat.id, chat.muteUntil, onUpdate); + scheduleMutedChatUpdate(chat.id, chat.muteUntil, sendApiUpdate); if (withPinned && dialog.pinned) { orderedPinnedIds.push(chat.id); @@ -218,7 +201,8 @@ export async function fetchChats({ const chatIds = chats.map((chat) => chat.id); - const { users, userStatusesById } = buildApiUsersAndStatuses((resultPinned?.users || []).concat(result.users)); + const users = result.users.map(buildApiUser).filter(Boolean); + const userStatusesById = buildApiUserStatuses((resultPinned?.users || []).concat(result.users)); let totalChatCount: number; if (result instanceof GramJs.messages.DialogsSlice) { @@ -281,13 +265,6 @@ export async function fetchSavedChats({ const hasPinned = resultPinned && !(resultPinned instanceof GramJs.messages.SavedDialogsNotModified); - if (hasPinned) { - updateLocalDb(resultPinned); - } - updateLocalDb(result); - - dispatchThreadInfoUpdates(result.messages); - const messages = (hasPinned ? resultPinned.messages : []) .concat(result.messages) .map(buildApiMessage) @@ -321,7 +298,8 @@ export async function fetchSavedChats({ chats.push(chat); }); - const { users, userStatusesById } = buildApiUsersAndStatuses((hasPinned ? resultPinned.users : []) + const users = result.users.map(buildApiUser).filter(Boolean); + const userStatusesById = buildApiUserStatuses((hasPinned ? resultPinned.users : []) .concat(result.users)); let totalChatCount: number; @@ -377,10 +355,7 @@ export async function fetchChatSettings(chat: ApiChat) { return undefined; } - addEntitiesToLocalDb(result.users); - return { - users: result.users.map(buildApiUser).filter(Boolean), settings: buildApiChatSettings(result.settings), }; } @@ -391,22 +366,13 @@ export async function searchChats({ query }: { query: string }) { return undefined; } - updateLocalDb(result); - const accountPeerIds = result.myResults.map(getApiChatIdFromMtpPeer); const globalPeerIds = result.results.map(getApiChatIdFromMtpPeer) .filter((id) => !accountPeerIds.includes(id)); - const chats = result.chats.concat(result.users) - .map((user) => buildApiChatFromPreview(user)) - .filter(Boolean); - const users = result.users.map(buildApiUser).filter(Boolean); - return { accountResultIds: accountPeerIds, globalResultIds: globalPeerIds, - chats, - users, }; } @@ -444,7 +410,7 @@ export async function fetchChat({ return undefined; } - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: chat.id, chat, @@ -483,10 +449,7 @@ export async function requestChatUpdate({ return; } - updateLocalDb(result); - const lastRemoteMessage = buildApiMessage(result.messages[0]); - dispatchThreadInfoUpdates(result.messages); const lastMessage = lastLocalMessage && (!lastRemoteMessage || (lastLocalMessage.date > lastRemoteMessage.date)) ? lastLocalMessage @@ -494,14 +457,14 @@ export async function requestChatUpdate({ const chatUpdate = buildApiChatFromDialog(dialog, peerEntity); - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id, chat: chatUpdate, }); if (!noLastMessage && lastMessage) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChatLastMessage', id, lastMessage, @@ -510,7 +473,7 @@ export async function requestChatUpdate({ applyState(result.state); - scheduleMutedChatUpdate(chatUpdate.id, chatUpdate.muteUntil, onUpdate); + scheduleMutedChatUpdate(chatUpdate.id, chatUpdate.muteUntil, sendApiUpdate); } export function saveDraft({ @@ -537,8 +500,6 @@ async function getFullChatInfo(chatId: string): Promise isAdmin || isOwner) : undefined; const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined; const inviteLink = exportedInvite instanceof GramJs.ChatInviteExported ? exportedInvite.link : undefined; - const { users, userStatusesById } = buildApiUsersAndStatuses(result.users); + const userStatusesById = buildApiUserStatuses(result.users); const chats = result.chats.map((chat) => buildApiChatFromPreview(chat)).filter(Boolean); return { @@ -581,7 +542,6 @@ async function getFullChatInfo(chatId: string): Promise buildApiChatFromPreview(c)).filter(Boolean); if (result?.chats?.length > 1) { - updateLocalDb(result); - const [, mtpLinkedChat] = result.chats; const linkedChat = buildApiChatFromPreview(mtpLinkedChat); if (linkedChat) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: linkedChat.id, chat: linkedChat, @@ -734,7 +692,6 @@ async function getFullChannelInfo( boostsApplied, boostsToUnrestrict: boostsUnrestrict, }, - users: [...(users || []), ...(bannedUsers || []), ...(adminUsers || [])], chats, userStatusesById: statusesById, groupCall: call ? { @@ -767,7 +724,7 @@ export async function updateChatMutedState({ settings: new GramJs.InputPeerNotifySettings({ muteUntil }), })); - onUpdate({ + sendApiUpdate({ '@type': 'updateNotifyExceptions', chatId: chat.id, isMuted, @@ -795,7 +752,7 @@ export async function updateTopicMutedState({ settings: new GramJs.InputPeerNotifySettings({ muteUntil }), })); - onUpdate({ + sendApiUpdate({ '@type': 'updateTopicNotifyExceptions', chatId: chat.id, topicId, @@ -999,7 +956,7 @@ export async function toggleChatPinned({ })); if (isActionSuccessful) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChatPinned', id: chat.id, isPinned: shouldBePinned, @@ -1023,7 +980,7 @@ export async function toggleSavedDialogPinned({ })); if (isActionSuccessful) { - onUpdate({ + sendApiUpdate({ '@type': 'updateSavedDialogPinned', id: chat.id, isPinned: shouldBePinned, @@ -1101,7 +1058,7 @@ export async function editChatFolder({ })); if (isActionSuccessful) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChatFolder', id, folder: folderUpdate, @@ -1117,14 +1074,14 @@ export async function deleteChatFolder(id: number) { const recommendedChatFolders = await fetchRecommendedChatFolders(); if (isActionSuccessful) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChatFolder', id, folder: undefined, }); } if (recommendedChatFolders) { - onUpdate({ + sendApiUpdate({ '@type': 'updateRecommendedChatFolders', folders: recommendedChatFolders, }); @@ -1152,7 +1109,7 @@ export async function toggleDialogUnread({ })); if (isActionSuccessful) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: chat.id, chat: { hasUnreadMark }, @@ -1191,8 +1148,6 @@ function processResolvedPeer(result?: GramJs.contacts.TypeResolvedPeer) { return undefined; } - updateLocalDb(result); - return { chat, user: buildApiUser(users[0]), @@ -1285,7 +1240,7 @@ export async function updateChatAbout(chat: ApiChat, about: string) { return; } - onUpdate({ + sendApiUpdate({ '@type': 'updateChatFullInfo', id: chat.id, fullInfo: { @@ -1351,12 +1306,10 @@ export async function fetchMembers( return undefined; } - updateLocalDb(result); - const { users, userStatusesById } = buildApiUsersAndStatuses(result.users); + const userStatusesById = buildApiUserStatuses(result.users); return { members: buildChatMembers(result), - users, userStatusesById, }; } @@ -1381,10 +1334,7 @@ export async function fetchMember({ return undefined; } - updateLocalDb(result); - - const { users, userStatusesById } = buildApiUsersAndStatuses(result.users); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); + const userStatusesById = buildApiUserStatuses(result.users); const member = buildChatMember(result.participant); if (!member) { @@ -1393,9 +1343,7 @@ export async function fetchMember({ return { member, - users, userStatusesById, - chats, }; } @@ -1406,8 +1354,6 @@ export async function fetchGroupsForDiscussion() { return undefined; } - updateLocalDb(result); - return result.chats.map((chat) => buildApiChatFromPreview(chat)); } @@ -1446,8 +1392,6 @@ export async function migrateChat(chat: ApiChat) { return undefined; } - updateLocalDb(result); - const newChannelId = result.updates .find((update): update is GramJs.UpdateChannel => update instanceof GramJs.UpdateChannel)!.channelId; @@ -1476,7 +1420,7 @@ export async function openChatByInvite(hash: string) { addPhotoToLocalDb(result.photo); } - onUpdate({ + sendApiUpdate({ '@type': 'showInvite', data: { title, @@ -1492,7 +1436,7 @@ export async function openChatByInvite(hash: string) { chat = buildApiChatFromPreview(result.chat); if (chat) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: chat.id, chat, @@ -1534,7 +1478,7 @@ export async function addChatMembers(chat: ApiChat, users: ApiUser[]) { return addChatUsersResult.flat().filter(Boolean); } } catch (err) { - onUpdate({ + sendApiUpdate({ '@type': 'error', error: { message: (err as Error).message, @@ -1631,28 +1575,6 @@ function preparePeers( return store; } -function updateLocalDb(result: ( - GramJs.messages.Dialogs | GramJs.messages.DialogsSlice | GramJs.messages.PeerDialogs | - GramJs.messages.SavedDialogs | GramJs.messages.SavedDialogsSlice | - GramJs.messages.ChatFull | GramJs.contacts.Found | - GramJs.contacts.ResolvedPeer | GramJs.channels.ChannelParticipants | - GramJs.messages.Chats | GramJs.messages.ChatsSlice | GramJs.TypeUpdates | GramJs.messages.ForumTopics -)) { - if ('users' in result) { - addEntitiesToLocalDb(result.users); - } - - if ('chats' in result) { - addEntitiesToLocalDb(result.chats); - } - - if ('messages' in result) { - result.messages.forEach((message) => { - addMessageToLocalDb(message); - }); - } -} - export async function importChatInvite({ hash }: { hash: string }) { const updates = await invokeRequest(new GramJs.messages.ImportChatInvite({ hash })); if (!(updates instanceof GramJs.Updates) || !updates.chats.length) { @@ -1758,8 +1680,6 @@ export async function fetchTopics({ }): Promise<{ topics: ApiTopic[]; messages: ApiMessage[]; - users: ApiUser[]; - chats: ApiChat[]; count: number; shouldOrderByCreateDate?: boolean; draftsById: Record>; @@ -1778,15 +1698,10 @@ export async function fetchTopics({ if (!result) return undefined; - updateLocalDb(result); - const { count, orderByCreateDate } = result; const topics = result.topics.map(buildApiTopic).filter(Boolean); const messages = result.messages.map(buildApiMessage).filter(Boolean); - dispatchThreadInfoUpdates(result.messages); - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); const draftsById = result.topics.reduce((acc, topic) => { if (topic instanceof GramJs.ForumTopic && topic.draft) { acc[topic.id] = buildMessageDraft(topic.draft); @@ -1803,8 +1718,6 @@ export async function fetchTopics({ return { topics, messages, - users, - chats, // Include general topic count: count + 1, shouldOrderByCreateDate: orderByCreateDate, @@ -1821,8 +1734,6 @@ export async function fetchTopicById({ }): Promise<{ topic: ApiTopic; messages: ApiMessage[]; - users: ApiUser[]; - chats: ApiChat[]; } | undefined> { const { id, accessHash } = chat; @@ -1835,18 +1746,11 @@ export async function fetchTopicById({ return undefined; } - updateLocalDb(result); - const messages = result.messages.map(buildApiMessage).filter(Boolean); - dispatchThreadInfoUpdates(result.messages); - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); return { topic: buildApiTopic(result.topics[0])!, messages, - users, - chats, }; } @@ -1927,12 +1831,8 @@ export async function checkChatlistInvite({ if (!result || !invite) return undefined; - updateLocalDb(result); - return { invite, - users: result.users.map(buildApiUser).filter(Boolean), - chats: result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean), }; } @@ -2060,12 +1960,8 @@ export async function fetchChatlistInvites({ if (!result) return undefined; - updateLocalDb(result); - return { invites: result.invites.map(buildApiChatlistExportedInvite).filter(Boolean), - users: result.users.map(buildApiUser).filter(Boolean), - chats: result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean), }; } @@ -2101,8 +1997,6 @@ export async function fetchChannelRecommendations({ chat }: { chat?: ApiChat }) return undefined; } - updateLocalDb(result); - const similarChannels = result?.chats .map((c) => buildApiChatFromPreview(c)) .filter(Boolean); diff --git a/src/api/gramjs/methods/client.ts b/src/api/gramjs/methods/client.ts index b568f4c21..5a3effbd9 100644 --- a/src/api/gramjs/methods/client.ts +++ b/src/api/gramjs/methods/client.ts @@ -12,7 +12,6 @@ import type { ApiMediaFormat, ApiOnProgress, ApiSessionData, - OnApiUpdate, } from '../../types'; import { @@ -20,15 +19,20 @@ import { DEBUG, DEBUG_GRAMJS, IS_TEST, UPLOAD_WORKERS, } from '../../../config'; import { pause } from '../../../util/schedulers'; -import { buildApiMessage, setMessageBuilderCurrentUserId } from '../apiBuilders/messages'; +import { + buildApiMessage, + setMessageBuilderCurrentUserId, +} from '../apiBuilders/messages'; import { buildApiPeerId } from '../apiBuilders/peers'; import { buildApiStory } from '../apiBuilders/stories'; import { buildApiUser, buildApiUserFullInfo } from '../apiBuilders/users'; import { buildInputPeerFromLocalDb, getEntityTypeById } from '../gramjsBuilders'; import { - addEntitiesToLocalDb, addMessageToLocalDb, addStoryToLocalDb, addUserToLocalDb, isResponseUpdate, log, + addStoryToLocalDb, addUserToLocalDb, isResponseUpdate, log, } from '../helpers'; import localDb, { clearLocalDb, type RepairInfo } from '../localDb'; +import { sendApiUpdate } from '../updates/apiUpdateEmitter'; +import { processAndUpdateEntities, processMessageAndUpdateThreadInfo } from '../updates/entityProcessor'; import { getDifference, init as initUpdatesManager, @@ -55,18 +59,15 @@ const gramJsUpdateEventBuilder = { build: (update: object) => update }; const CHAT_ABORT_CONTROLLERS = new Map(); const ABORT_CONTROLLERS = new Map(); -let onUpdate: OnApiUpdate; let client: TelegramClient; let currentUserId: string | undefined; -export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) { +export async function init(initialArgs: ApiInitialArgs) { if (DEBUG) { // eslint-disable-next-line no-console console.log('>>> START INIT API'); } - onUpdate = _onUpdate; - const { userAgent, platform, sessionData, isTest, isWebmSupported, maxBufferSize, webAuthToken, dcId, mockScenario, shouldForceHttpTransport, shouldAllowHttpTransport, @@ -130,7 +131,7 @@ export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) console.error(err); if (err.message !== 'Disconnect' && err.message !== 'Cannot send requests while disconnected') { - onUpdate({ + sendApiUpdate({ '@type': 'updateConnectionState', connectionState: 'connectionStateBroken', }); @@ -147,7 +148,7 @@ export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) onAuthReady(); onSessionUpdate(session.getSessionData()); - onUpdate({ '@type': 'updateApiReady' }); + sendApiUpdate({ '@type': 'updateApiReady' }); initUpdatesManager(invokeRequest); @@ -191,7 +192,7 @@ export function getClient() { } function onSessionUpdate(sessionData: ApiSessionData) { - onUpdate({ + sendApiUpdate({ '@type': 'updateSession', sessionData, }); @@ -276,6 +277,8 @@ export async function invokeRequest( const result = await client.invoke(request, dcId, abortSignal, shouldRetryOnTimeout); + processAndUpdateEntities(result); + if (DEBUG) { log('RESPONSE', request.className, result); } @@ -414,7 +417,7 @@ export function dispatchErrorUpdate(err: Error, req const { message } = err; - onUpdate({ + sendApiUpdate({ '@type': 'error', error: { message, @@ -433,7 +436,7 @@ async function handleTerminatedSession() { }); } catch (err: any) { if (err.message === 'AUTH_KEY_UNREGISTERED' || err.message === 'SESSION_REVOKED') { - onUpdate({ + sendApiUpdate({ '@type': 'updateConnectionState', connectionState: 'connectionStateBroken', }); @@ -503,13 +506,12 @@ async function repairMessageMedia(peerId: string, messageId: number) { const message = result.messages[0]; if (message instanceof GramJs.MessageEmpty) return false; - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - addMessageToLocalDb(message); + + processMessageAndUpdateThreadInfo(message); const apiMessage = buildApiMessage(message); if (apiMessage) { - onUpdate({ + sendApiUpdate({ '@type': 'updateMessage', chatId: apiMessage.chatId, id: apiMessage.id, @@ -531,13 +533,12 @@ async function repairStoryMedia(peerId: string, storyId: number) { }); if (!result) return false; - addEntitiesToLocalDb(result.users); result.stories.forEach((story) => { const apiStory = buildApiStory(peerId, story); if (!apiStory || 'isDeleted' in apiStory) return; addStoryToLocalDb(story, peerId); - onUpdate({ + sendApiUpdate({ '@type': 'updateStory', peerId, story: apiStory, diff --git a/src/api/gramjs/methods/index.ts b/src/api/gramjs/methods/index.ts index c03085b2e..0cfaefe4f 100644 --- a/src/api/gramjs/methods/index.ts +++ b/src/api/gramjs/methods/index.ts @@ -3,106 +3,42 @@ export { setForceHttpTransport, setShouldDebugExportedSenders, setAllowHttpTransport, requestChannelDifference, } from './client'; -export * from './account'; - export { provideAuthPhoneNumber, provideAuthCode, provideAuthPassword, provideAuthRegistration, restartAuth, restartAuthWithQr, } from './auth'; -export { - fetchChats, fetchFullChat, searchChats, requestChatUpdate, fetchChatSettings, - saveDraft, fetchChat, updateChatMutedState, updateTopicMutedState, fetchMember, - createChannel, joinChannel, deleteChatUser, deleteChat, leaveChannel, deleteChannel, createGroupChat, editChatPhoto, - toggleChatPinned, toggleChatArchived, toggleDialogUnread, setChatEnabledReactions, - fetchChatFolders, editChatFolder, deleteChatFolder, sortChatFolders, fetchRecommendedChatFolders, - getChatByUsername, togglePreHistoryHidden, updateChatDefaultBannedRights, updateChatMemberBannedRights, - updateChatTitle, updateChatAbout, toggleSignatures, updateChatAdmin, fetchGroupsForDiscussion, setDiscussionGroup, - migrateChat, openChatByInvite, fetchMembers, importChatInvite, addChatMembers, deleteChatMember, toggleIsProtected, - getChatByPhoneNumber, toggleJoinToSend, toggleJoinRequest, fetchTopics, deleteTopic, togglePinnedTopic, - editTopic, toggleForum, fetchTopicById, createTopic, toggleParticipantsHidden, checkChatlistInvite, - joinChatlistInvite, createChalistInvite, editChatlistInvite, deleteChatlistInvite, fetchChatlistInvites, - fetchLeaveChatlistSuggestions, leaveChatlist, togglePeerTranslations, setViewForumAsMessages, - fetchChannelRecommendations, fetchSavedChats, toggleSavedDialogPinned, reportSponsoredMessage, -} from './chats'; - -export { - fetchMessages, fetchMessage, sendMessage, pinMessage, unpinAllMessages, deleteMessages, deleteHistory, - markMessageListRead, markMessagesRead, searchMessagesInChat, searchMessagesGlobal, searchHashtagPosts, - fetchWebPagePreview, editMessage, forwardMessages, loadPollOptionResults, sendPollVote, findFirstMessageIdAfterDate, - fetchPinnedMessages, fetchScheduledHistory, sendScheduledMessages, rescheduleMessage, deleteScheduledMessages, - reportMessages, sendMessageAction, fetchSeenBy, fetchSponsoredMessages, viewSponsoredMessage, fetchSendAs, - saveDefaultSendAs, fetchUnreadReactions, readAllReactions, fetchUnreadMentions, readAllMentions, transcribeAudio, - closePoll, fetchExtendedMedia, translateText, fetchMessageViews, fetchDiscussionMessage, clickSponsoredMessage, - fetchOutboxReadDate, exportMessageLink, fetchQuickReplies, sendQuickReply, fetchFactChecks, - deleteSavedHistory, -} from './messages'; - -export { - fetchFullUser, fetchNearestCountry, fetchTopUsers, fetchContactList, fetchUsers, - updateContact, importContact, deleteContact, fetchProfilePhotos, fetchCommonChats, reportSpam, updateEmojiStatus, - saveCloseFriends, -} from './users'; - -export { - fetchStickerSets, fetchRecentStickers, fetchFavoriteStickers, fetchFeaturedStickers, fetchRecentEmojiStatuses, - faveSticker, fetchStickers, fetchSavedGifs, saveGif, searchStickers, installStickerSet, uninstallStickerSet, - searchGifs, fetchAnimatedEmojis, fetchStickersForEmoji, fetchEmojiKeywords, fetchAnimatedEmojiEffects, - removeRecentSticker, clearRecentStickers, fetchCustomEmoji, fetchPremiumGifts, fetchCustomEmojiSets, - fetchFeaturedEmojiStickers, fetchGenericEmojiEffects, fetchDefaultTopicIcons, fetchDefaultStatusEmojis, -} from './symbols'; - -export { - checkChatUsername, setChatUsername, updatePrivateLink, deactivateAllUsernames, - fetchExportedChatInvites, editExportedChatInvite, exportChatInvite, deleteExportedChatInvite, - deleteRevokedExportedChatInvites, fetchChatInviteImporters, hideChatJoinRequest, hideAllChatJoinRequests, - hideChatReportPanel, -} from './management'; - -export * from './settings'; - -export { - getPasswordInfo, checkPassword, clearPassword, updatePassword, updateRecoveryEmail, provideRecoveryEmailCode, -} from './twoFaSettings'; - -export { - answerCallbackButton, setBotInfo, fetchTopInlineBots, fetchPreviewMedias, fetchInlineBot, fetchInlineBotResults, - sendInlineBotResult, startBot, requestMainWebView, fetchPopularAppBots, fetchTopBotApps, - requestWebView, requestSimpleWebView, sendWebViewData, prolongWebView, loadAttachBots, toggleAttachBot, fetchBotApp, - requestBotUrlAuth, requestLinkUrlAuth, acceptBotUrlAuth, acceptLinkUrlAuth, loadAttachBot, requestAppWebView, - allowBotSendMessages, fetchBotCanSendMessage, invokeWebViewCustomMethod, -} from './bots'; - -export { - getGroupCall, joinGroupCall, discardGroupCall, createGroupCall, - editGroupCallTitle, editGroupCallParticipant, exportGroupCallInvite, fetchGroupCallParticipants, - joinGroupCallPresentation, leaveGroupCall, leaveGroupCallPresentation, toggleGroupCallStartSubscription, - requestCall, getDhConfig, confirmCall, sendSignalingData, acceptCall, discardCall, setCallRating, receivedCall, -} from './calls'; - -export * from './reactions'; - -export { - fetchChannelStatistics, fetchGroupStatistics, fetchMessageStatistics, - fetchMessagePublicForwards, fetchStatisticsAsyncGraph, fetchStoryStatistics, fetchStoryPublicForwards, - fetchChannelMonetizationStatistics, -} from './statistics'; - -export { - acceptPhoneCall, confirmPhoneCall, requestPhoneCall, decodePhoneCallData, createPhoneCallState, - destroyPhoneCallState, encodePhoneCallData, -} from './phoneCallState'; - export { broadcastLocalDbUpdateFull, } from '../localDb'; +export * from './account'; + +export * from './chats'; + +export * from './messages'; + +export * from './users'; + +export * from './symbols'; + +export * from './management'; + +export * from './settings'; + +export * from './twoFaSettings'; + +export * from './bots'; + +export * from './calls'; + +export * from './reactions'; + +export * from './statistics'; + +export * from './phoneCallState'; + export * from './stories'; -export { - validateRequestedInfo, sendPaymentForm, getPaymentForm, getReceipt, fetchPremiumPromo, fetchTemporaryPaymentPassword, - applyBoost, fetchBoostList, fetchBoostStatus, fetchGiveawayInfo, fetchMyBoosts, applyGiftCode, checkGiftCode, - getPremiumGiftCodeOptions, launchPrepaidGiveaway, fetchStarsStatus, fetchStarsTopupOptions, fetchStarsTransactions, - sendStarPaymentForm, getStarsGiftOptions, fetchStarsTransactionById, -} from './payments'; +export * from './payments'; export * from './fragment'; diff --git a/src/api/gramjs/methods/init.ts b/src/api/gramjs/methods/init.ts index 8cca9ccf1..b16eb62c7 100644 --- a/src/api/gramjs/methods/init.ts +++ b/src/api/gramjs/methods/init.ts @@ -1,49 +1,22 @@ import type { ApiInitialArgs, ApiOnProgress, - ApiUpdate, OnApiUpdate, } from '../../types'; import type { LocalDb } from '../localDb'; import type { MethodArgs, MethodResponse, Methods } from './types'; -import { API_THROTTLE_RESET_UPDATES, API_UPDATE_THROTTLE } from '../../../config'; -import { throttle, throttleWithTickEnd } from '../../../util/schedulers'; import { updateFullLocalDb } from '../localDb'; -import { init as initUpdater } from '../updates/updater'; -import { init as initAuth } from './auth'; -import { init as initBots } from './bots'; -import { init as initCalls } from './calls'; -import { init as initChats } from './chats'; +import { init as initUpdateEmitter } from '../updates/apiUpdateEmitter'; import { init as initClient } from './client'; import * as methods from './index'; -import { init as initManagement } from './management'; -import { init as initMessages } from './messages'; -import { init as initPayments } from './payments'; -import { init as initStickers } from './symbols'; -import { init as initTwoFaSettings } from './twoFaSettings'; -import { init as initUsers } from './users'; - -let onUpdate: OnApiUpdate; export function initApi(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs, initialLocalDb?: LocalDb) { - onUpdate = _onUpdate; - - initUpdater(handleUpdate); - initAuth(handleUpdate); - initChats(handleUpdate); - initMessages(handleUpdate); - initUsers(handleUpdate); - initStickers(handleUpdate); - initManagement(handleUpdate); - initTwoFaSettings(handleUpdate); - initBots(handleUpdate); - initCalls(handleUpdate); - initPayments(handleUpdate); + initUpdateEmitter(_onUpdate); if (initialLocalDb) updateFullLocalDb(initialLocalDb); - initClient(handleUpdate, initialArgs); + initClient(initialArgs); } export function callApi(fnName: T, ...args: MethodArgs): MethodResponse { @@ -54,35 +27,3 @@ export function callApi(fnName: T, ...args: MethodArgs< export function cancelApiProgress(progressCallback: ApiOnProgress) { progressCallback.isCanceled = true; } - -const flushUpdatesOnTickEnd = throttleWithTickEnd(flushUpdates); - -let flushUpdatesThrottled: typeof flushUpdatesOnTickEnd | undefined; -let currentThrottleId: number | undefined; - -let pendingUpdates: ApiUpdate[] | undefined; - -function handleUpdate(update: ApiUpdate) { - if (!pendingUpdates) { - pendingUpdates = [update]; - } else { - pendingUpdates.push(update); - } - - if (!flushUpdatesThrottled || API_THROTTLE_RESET_UPDATES.has(update['@type'])) { - flushUpdatesThrottled = throttle(flushUpdatesOnTickEnd, API_UPDATE_THROTTLE, true); - currentThrottleId = Math.random(); - } - - flushUpdatesThrottled(currentThrottleId!); -} - -function flushUpdates(throttleId: number) { - if (!pendingUpdates || throttleId !== currentThrottleId) { - return; - } - - const currentUpdates = pendingUpdates!; - pendingUpdates = undefined; - currentUpdates.forEach(onUpdate); -} diff --git a/src/api/gramjs/methods/management.ts b/src/api/gramjs/methods/management.ts index c617b6085..1858fb9a5 100644 --- a/src/api/gramjs/methods/management.ts +++ b/src/api/gramjs/methods/management.ts @@ -1,25 +1,15 @@ import { Api as GramJs } from '../../../lib/gramjs'; import type { - ApiChat, ApiError, ApiUser, ApiUsername, OnApiUpdate, + ApiChat, ApiError, ApiUser, ApiUsername, } from '../../types'; -import { USERNAME_PURCHASE_ERROR } from '../../../config'; -import { buildCollectionByKey } from '../../../util/iteratees'; +import { ACCEPTABLE_USERNAME_ERRORS } from '../../../config'; import { buildApiExportedInvite, buildChatInviteImporter } from '../apiBuilders/chats'; -import { buildApiUser } from '../apiBuilders/users'; import { buildInputEntity, buildInputPeer } from '../gramjsBuilders'; -import { addEntitiesToLocalDb } from '../helpers'; +import { sendApiUpdate } from '../updates/apiUpdateEmitter'; import { invokeRequest } from './client'; -let onUpdate: OnApiUpdate; - -export const ACCEPTABLE_USERNAME_ERRORS = new Set([USERNAME_PURCHASE_ERROR, 'USERNAME_INVALID']); - -export function init(_onUpdate: OnApiUpdate) { - onUpdate = _onUpdate; -} - export async function checkChatUsername({ username }: { username: string }) { try { const result = await invokeRequest(new GramJs.channels.CheckUsername({ @@ -59,7 +49,7 @@ export async function setChatUsername( } if (result) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: chat.id, chat: { usernames: usernames.length ? usernames : undefined }, @@ -82,7 +72,7 @@ export async function deactivateAllUsernames({ chat }: { chat: ApiChat }) { .filter((u) => u.username) : undefined; - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: chat.id, chat: { usernames }, @@ -105,7 +95,7 @@ export async function updatePrivateLink({ if (!(result instanceof GramJs.ChatInviteExported)) return undefined; - onUpdate({ + sendApiUpdate({ '@type': 'updateChatFullInfo', id: chat.id, fullInfo: { @@ -129,7 +119,6 @@ export async function fetchExportedChatInvites({ }); if (!exportedInvites) return undefined; - addEntitiesToLocalDb(exportedInvites.users); const invites = (exportedInvites.invites .filter((invite): invite is GramJs.ChatInviteExported => invite instanceof GramJs.ChatInviteExported)) @@ -137,7 +126,6 @@ export async function fetchExportedChatInvites({ return { invites, - users: exportedInvites.users.map(buildApiUser).filter(Boolean), }; } @@ -164,13 +152,11 @@ export async function editExportedChatInvite({ if (!invite) return undefined; - addEntitiesToLocalDb(invite.users); if (invite instanceof GramJs.messages.ExportedChatInvite && invite.invite instanceof GramJs.ChatInviteExported) { const replaceInvite = buildApiExportedInvite(invite.invite); return { oldInvite: replaceInvite, newInvite: replaceInvite, - users: invite.users.map(buildApiUser).filter(Boolean), }; } @@ -182,7 +168,6 @@ export async function editExportedChatInvite({ return { oldInvite, newInvite, - users: invite.users.map(buildApiUser).filter(Boolean), }; } return undefined; @@ -253,11 +238,9 @@ export async function fetchChatInviteImporters({ }); if (!result) return undefined; - const users = result.users.map((user) => buildApiUser(user)).filter(Boolean); - addEntitiesToLocalDb(result.users); + return { importers: result.importers.map((importer) => buildChatInviteImporter(importer)), - users: buildCollectionByKey(users, 'id'), }; } diff --git a/src/api/gramjs/methods/messages.ts b/src/api/gramjs/methods/messages.ts index e6d6071c2..d29bf0680 100644 --- a/src/api/gramjs/methods/messages.ts +++ b/src/api/gramjs/methods/messages.ts @@ -22,11 +22,9 @@ import type { ApiSticker, ApiStory, ApiStorySkipped, - ApiUser, ApiUserStatus, ApiVideo, MediaContent, - OnApiUpdate, } from '../../types'; import { MAIN_THREAD_ID, MESSAGE_DELETED } from '../../types'; @@ -62,7 +60,7 @@ import { buildUploadingMedia, } from '../apiBuilders/messages'; import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers'; -import { buildApiUser, buildApiUsersAndStatuses } from '../apiBuilders/users'; +import { buildApiUser, buildApiUserStatuses } from '../apiBuilders/users'; import { buildInputEntity, buildInputMediaDocument, @@ -82,13 +80,12 @@ import { getEntityTypeById, } from '../gramjsBuilders'; import { - addEntitiesToLocalDb, - addMessageToLocalDb, deserializeBytes, resolveMessageApiChatId, } from '../helpers'; +import { sendApiUpdate } from '../updates/apiUpdateEmitter'; +import { processMessageAndUpdateThreadInfo } from '../updates/entityProcessor'; import { processAffectedHistory, updateChannelState } from '../updates/updateManager'; -import { dispatchThreadInfoUpdates } from '../updates/updater'; import { requestChatUpdate } from './chats'; import { handleGramJsUpdate, invokeRequest, uploadFile } from './client'; @@ -106,21 +103,13 @@ type TranslateTextParams = ({ type SearchResults = { messages: ApiMessage[]; - users: ApiUser[]; userStatusesById: Record; - chats: ApiChat[]; totalCount: number; nextOffsetRate?: number; nextOffsetPeerId?: string; nextOffsetId?: number; }; -let onUpdate: OnApiUpdate; - -export function init(_onUpdate: OnApiUpdate) { - onUpdate = _onUpdate; -} - export async function fetchMessages({ chat, threadId, @@ -158,7 +147,7 @@ export async function fetchMessages({ }); } catch (err: any) { if (err.message === 'CHANNEL_PRIVATE') { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: chat.id, chat: { @@ -176,13 +165,10 @@ export async function fetchMessages({ return undefined; } - updateLocalDb(result); - const messages = result.messages.map(buildApiMessage).filter(Boolean); const users = result.users.map(buildApiUser).filter(Boolean); const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); const count = !(result instanceof GramJs.messages.Messages) && result.count; - dispatchThreadInfoUpdates(result.messages); return { messages, @@ -217,7 +203,7 @@ export async function fetchMessage({ chat, messageId }: { chat: ApiChat; message // When fetching messages for the bot @replies, there may be situations when the user was banned // in the comment group or this group was deleted if (message !== 'CHANNEL_PRIVATE') { - onUpdate({ + sendApiUpdate({ '@type': 'error', error: { message, @@ -245,18 +231,14 @@ export async function fetchMessage({ chat, messageId }: { chat: ApiChat; message return MESSAGE_DELETED; } - const message = mtpMessage && buildApiMessage(mtpMessage); - dispatchThreadInfoUpdates([mtpMessage]); + processMessageAndUpdateThreadInfo(mtpMessage); + const message = buildApiMessage(mtpMessage); if (!message) { return undefined; } - addMessageToLocalDb(mtpMessage); - - const users = result.users.map(buildApiUser).filter(Boolean); - - return { message, users }; + return { message }; } let mediaQueue = Promise.resolve(); @@ -326,7 +308,7 @@ export function sendMessage( effectId, ); - onUpdate({ + sendApiUpdate({ '@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage', id: localMessage.id, chatId: chat.id, @@ -337,7 +319,7 @@ export function sendMessage( // This is expected to arrive after `updateMessageSendSucceeded` which replaces the local ID, // so in most cases this will be simply ignored const timeout = setTimeout(() => { - onUpdate({ + sendApiUpdate({ '@type': localMessage.isScheduled ? 'updateScheduledMessage' : 'updateMessage', id: localMessage.id, chatId: chat.id, @@ -419,10 +401,10 @@ export function sendMessage( if (update) handleLocalMessageUpdate(localMessage, update); } catch (error: any) { if (error.message === 'PRIVACY_PREMIUM_REQUIRED') { - onUpdate({ '@type': 'updateRequestUserUpdate', id: chat.id }); + sendApiUpdate({ '@type': 'updateRequestUserUpdate', id: chat.id }); } - onUpdate({ + sendApiUpdate({ '@type': 'updateMessageSendFailed', chatId: chat.id, localId: localMessage.id, @@ -624,7 +606,7 @@ export async function editMessage({ isInvertedMedia, }; - onUpdate({ + sendApiUpdate({ '@type': isScheduled ? 'updateScheduledMessage' : 'updateMessage', id: message.id, chatId: chat.id, @@ -657,7 +639,7 @@ export async function editMessage({ const { message: messageErr } = err as Error; - onUpdate({ + sendApiUpdate({ '@type': 'error', error: { message: messageErr, @@ -666,7 +648,7 @@ export async function editMessage({ }); // Rollback changes - onUpdate({ + sendApiUpdate({ '@type': isScheduled ? 'updateScheduledMessage' : 'updateMessage', id: message.id, chatId: chat.id, @@ -823,7 +805,7 @@ export async function deleteMessages({ processAffectedHistory(chat, result); - onUpdate({ + sendApiUpdate({ '@type': 'deleteMessages', ids: messageIds, ...(isChannel && { chatId: chat.id }), @@ -872,7 +854,7 @@ export async function deleteHistory({ } } - onUpdate({ + sendApiUpdate({ '@type': 'deleteHistory', chatId: chat.id, }); @@ -898,7 +880,7 @@ export async function deleteSavedHistory({ return; } - onUpdate({ + sendApiUpdate({ '@type': 'deleteSavedHistory', chatId: chat.id, }); @@ -984,7 +966,7 @@ export async function markMessageListRead({ if (threadId === MAIN_THREAD_ID) { void requestChatUpdate({ chat, noLastMessage: true }); } else if (chat.isForum) { - onUpdate({ + sendApiUpdate({ '@type': 'updateTopic', chatId: chat.id, topicId: Number(threadId), @@ -1018,7 +1000,7 @@ export async function markMessagesRead({ processAffectedHistory(chat, result); } - onUpdate({ + sendApiUpdate({ ...(isChannel ? { '@type': 'updateChannelMessages', channelId: chat.id, @@ -1052,8 +1034,6 @@ export async function fetchMessageViews({ if (!results || results.some((result) => !result)) return undefined; const viewsList = results.flatMap((result) => result!.views); - const users = results.flatMap((result) => result!.users); - const chats = results.flatMap((result) => result!.chats); const viewsInfo = ids.map((id, index) => { const { views, forwards, replies } = viewsList[index]; @@ -1067,8 +1047,6 @@ export async function fetchMessageViews({ return { viewsInfo, - users: users.map(buildApiUser).filter(Boolean), - chats: chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean), }; } @@ -1116,27 +1094,17 @@ export async function fetchDiscussionMessage({ if (!result || !replies) return undefined; - updateLocalDb(result); - - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean) - .concat(replies.chats); - const users = result.users.map(buildApiUser).filter(Boolean) - .concat(replies.users); const topMessages = result.messages.map(buildApiMessage).filter(Boolean); const messages = topMessages.concat(replies.messages); const threadId = result.messages[result.messages.length - 1]?.id; if (!threadId) return undefined; - dispatchThreadInfoUpdates(result.messages); - const { unreadCount, maxId, readInboxMaxId, readOutboxMaxId, } = result; return { - chats, - users, messages, topMessages, unreadCount, @@ -1215,12 +1183,8 @@ export async function searchMessagesInChat({ return undefined; } - updateLocalDb(result); - - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); - const { users, userStatusesById } = buildApiUsersAndStatuses(result.users); + const userStatusesById = buildApiUserStatuses(result.users); const messages = result.messages.map(buildApiMessage).filter(Boolean); - dispatchThreadInfoUpdates(result.messages); let totalCount = messages.length; let nextOffsetId: number | undefined; @@ -1233,8 +1197,6 @@ export async function searchMessagesInChat({ } return { - chats, - users, userStatusesById, messages, totalCount, @@ -1304,12 +1266,8 @@ export async function searchMessagesGlobal({ return undefined; } - updateLocalDb(result); - - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); - const { users, userStatusesById } = buildApiUsersAndStatuses(result.users); + const userStatusesById = buildApiUserStatuses(result.users); const messages = result.messages.map(buildApiMessage).filter(Boolean); - dispatchThreadInfoUpdates(result.messages); let totalCount = messages.length; if (result instanceof GramJs.messages.MessagesSlice || result instanceof GramJs.messages.ChannelMessages) { @@ -1325,9 +1283,7 @@ export async function searchMessagesGlobal({ return { messages, - users, userStatusesById, - chats, totalCount, nextOffsetRate, nextOffsetPeerId, @@ -1357,12 +1313,8 @@ export async function searchHashtagPosts({ return undefined; } - updateLocalDb(result); - - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); - const { users, userStatusesById } = buildApiUsersAndStatuses(result.users); + const userStatusesById = buildApiUserStatuses(result.users); const messages = result.messages.map(buildApiMessage).filter(Boolean); - dispatchThreadInfoUpdates(result.messages); let totalCount = messages.length; if (result instanceof GramJs.messages.MessagesSlice || result instanceof GramJs.messages.ChannelMessages) { @@ -1378,9 +1330,7 @@ export async function searchHashtagPosts({ return { messages, - users, userStatusesById, - chats, totalCount, nextOffsetRate, nextOffsetPeerId, @@ -1458,14 +1408,6 @@ export async function loadPollOptionResults({ return undefined; } - updateLocalDb({ - chats: result.chats, - users: result.users, - messages: [] as GramJs.Message[], - } as GramJs.messages.Messages); - - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); const votes = result.votes.map((vote) => ({ peerId: getApiChatIdFromMtpPeer(vote.peer), date: vote.date, @@ -1474,8 +1416,6 @@ export async function loadPollOptionResults({ return { count: result.count, votes, - chats, - users, nextOffset: result.nextOffset, shouldResetVoters, }; @@ -1539,7 +1479,7 @@ export async function forwardMessages({ }); localMessages[randomIds[index].toString()] = localMessage; - onUpdate({ + sendApiUpdate({ '@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage', id: localMessage.id, chatId: toChat.id, @@ -1568,7 +1508,7 @@ export async function forwardMessages({ if (update) handleMultipleLocalMessagesUpdate(localMessages, update); } catch (error: any) { Object.values(localMessages).forEach((localMessage) => { - onUpdate({ + sendApiUpdate({ '@type': 'updateMessageSendFailed', chatId: toChat.id, localId: localMessage.id, @@ -1620,10 +1560,7 @@ export async function fetchScheduledHistory({ chat }: { chat: ApiChat }) { return undefined; } - updateLocalDb(result); - const messages = result.messages.map(buildApiMessage).filter(Boolean); - dispatchThreadInfoUpdates(result.messages); return { messages, @@ -1639,18 +1576,6 @@ export async function sendScheduledMessages({ chat, ids }: { chat: ApiChat; ids: })); } -function updateLocalDb(result: ( - GramJs.messages.MessagesSlice | GramJs.messages.Messages | GramJs.messages.ChannelMessages | - GramJs.messages.DiscussionMessage | GramJs.messages.SponsoredMessages | GramJs.messages.QuickReplies -)) { - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - - result.messages.forEach((message) => { - addMessageToLocalDb(message); - }); -} - export async function fetchPinnedMessages({ chat, threadId }: { chat: ApiChat; threadId: ThreadId }) { const result = await invokeRequest(new GramJs.messages.Search( { @@ -1673,17 +1598,10 @@ export async function fetchPinnedMessages({ chat, threadId }: { chat: ApiChat; t return undefined; } - updateLocalDb(result); - - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); - const users = result.users.map(buildApiUser).filter(Boolean); const messages = result.messages.map(buildApiMessage).filter(Boolean); - dispatchThreadInfoUpdates(result.messages); return { messages, - users, - chats, }; } @@ -1718,15 +1636,7 @@ export async function fetchSendAs({ return undefined; } - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); - return { - users, - chats, sendAs: result.peers.map(buildApiSendAsPeerId), }; } @@ -1751,16 +1661,10 @@ export async function fetchSponsoredMessages({ chat }: { chat: ApiChat }) { return undefined; } - updateLocalDb(result); - const messages = result.messages.map(buildApiSponsoredMessage).filter(Boolean); - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); return { messages, - users, - chats, }; } @@ -1837,17 +1741,10 @@ export async function fetchUnreadMentions({ return undefined; } - updateLocalDb(result); - const messages = result.messages.map(buildApiMessage).filter(Boolean); - dispatchThreadInfoUpdates(result.messages); - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); return { messages, - users, - chats, }; } @@ -1874,17 +1771,10 @@ export async function fetchUnreadReactions({ return undefined; } - updateLocalDb(result); - const messages = result.messages.map(buildApiMessage).filter(Boolean); - dispatchThreadInfoUpdates(result.messages); - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); return { messages, - users, - chats, }; } @@ -1900,7 +1790,7 @@ export async function transcribeAudio({ if (!result) return undefined; - onUpdate({ + sendApiUpdate({ '@type': 'updateTranscribedAudio', isPending: result.pending, transcriptionId: result.transcriptionId.toString(), @@ -1933,7 +1823,7 @@ export async function translateText(params: TranslateTextParams) { const formattedText = result.result.map((r) => buildApiFormattedText(r)); if (isMessageTranslation) { - onUpdate({ + sendApiUpdate({ '@type': 'updateMessageTranslations', chatId: params.chat.id, messageIds: params.messageIds, @@ -1993,13 +1883,13 @@ function handleLocalMessageUpdate(localMessage: ApiMessage, update: GramJs.TypeU } const mtpMessage = buildMessageFromUpdate(messageUpdate.id, localMessage.chatId, messageUpdate); - addMessageToLocalDb(mtpMessage); + processMessageAndUpdateThreadInfo(mtpMessage); } // Edge case for "Send When Online" const isSentBefore = 'date' in messageUpdate && messageUpdate.date * 1000 < Date.now() + getServerTimeOffset() * 1000; - onUpdate({ + sendApiUpdate({ '@type': localMessage.isScheduled && !isSentBefore ? 'updateScheduledMessageSendSucceeded' : 'updateMessageSendSucceeded', @@ -2040,20 +1930,12 @@ export async function fetchQuickReplies() { const result = await invokeRequest(new GramJs.messages.GetQuickReplies({})); if (!result || result instanceof GramJs.messages.QuickRepliesNotModified) return undefined; - updateLocalDb(result); - const messages = result.messages.map(buildApiMessage).filter(Boolean); - dispatchThreadInfoUpdates(result.messages); - - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); - const users = result.users.map(buildApiUser).filter(Boolean); const quickReplies = result.quickReplies.map(buildApiQuickReply); return { messages, - chats, - users, quickReplies, }; } diff --git a/src/api/gramjs/methods/payments.ts b/src/api/gramjs/methods/payments.ts index cbe09421c..438365c09 100644 --- a/src/api/gramjs/methods/payments.ts +++ b/src/api/gramjs/methods/payments.ts @@ -4,11 +4,9 @@ import { Api as GramJs } from '../../../lib/gramjs'; import type { ApiChat, ApiInputStorePaymentPurpose, ApiPeer, ApiRequestInputInvoice, ApiThemeParameters, - OnApiUpdate, } from '../../types'; import { DEBUG } from '../../../config'; -import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { buildApiBoost, buildApiBoostsStatus, @@ -24,26 +22,19 @@ import { buildApiStarTopupOption, buildShippingOptions, } from '../apiBuilders/payments'; -import { buildApiUser } from '../apiBuilders/users'; import { buildInputInvoice, buildInputPeer, buildInputStorePaymentPurpose, buildInputThemeParams, buildShippingInfo, } from '../gramjsBuilders'; import { - addEntitiesToLocalDb, addWebDocumentToLocalDb, deserializeBytes, serializeBytes, } from '../helpers'; import localDb from '../localDb'; +import { sendApiUpdate } from '../updates/apiUpdateEmitter'; import { handleGramJsUpdate, invokeRequest } from './client'; import { getTemporaryPaymentPassword } from './twoFaSettings'; -let onUpdate: OnApiUpdate; - -export function init(_onUpdate: OnApiUpdate) { - onUpdate = _onUpdate; -} - export async function validateRequestedInfo({ inputInvoice, requestInfo, @@ -116,7 +107,7 @@ export async function sendPaymentForm({ if (!result) return false; if (result instanceof GramJs.payments.PaymentVerificationNeeded) { - onUpdate({ + sendApiUpdate({ '@type': 'updatePaymentVerificationNeeded', url: result.url, }); @@ -171,12 +162,9 @@ export async function getPaymentForm(inputInvoice: ApiRequestInputInvoice, theme addWebDocumentToLocalDb(result.photo); } - addEntitiesToLocalDb(result.users); - return { form: buildApiPaymentForm(result), invoice: buildApiInvoiceFromForm(result), - users: result.users.map(buildApiUser).filter(Boolean), }; } @@ -190,11 +178,8 @@ export async function getReceipt(chat: ApiChat, msgId: number) { return undefined; } - addEntitiesToLocalDb(result.users); - return { receipt: buildApiReceipt(result), - users: result.users.map(buildApiUser).filter(Boolean), }; } @@ -202,9 +187,6 @@ export async function fetchPremiumPromo() { const result = await invokeRequest(new GramJs.help.GetPremiumPromo()); if (!result) return undefined; - addEntitiesToLocalDb(result.users); - - const users = result.users.map(buildApiUser).filter(Boolean); result.videos.forEach((video) => { if (video instanceof GramJs.Document) { localDb.documents[video.id.toString()] = video; @@ -213,7 +195,6 @@ export async function fetchPremiumPromo() { return { promo: buildApiPremiumPromo(result), - users, }; } @@ -239,16 +220,9 @@ export async function fetchMyBoosts() { if (!result) return undefined; - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); const boosts = result.myBoosts.map(buildApiMyBoost); return { - users, - chats, boosts, }; } @@ -267,16 +241,9 @@ export async function applyBoost({ if (!result) return undefined; - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); const boosts = result.myBoosts.map(buildApiMyBoost); return { - users, - chats, boosts, }; } @@ -319,16 +286,11 @@ export async function fetchBoostList({ return undefined; } - addEntitiesToLocalDb(result.users); - - const users = result.users.map(buildApiUser).filter(Boolean); - const boostList = result.boosts.map(buildApiBoost); return { count: result.count, boostList, - users, nextOffset: result.nextOffset, }; } @@ -365,13 +327,8 @@ export async function checkGiftCode({ return undefined; } - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - return { code: buildApiCheckedGiftCode(result), - users: result.users.map(buildApiUser).filter(Boolean), - chats: result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean), }; } @@ -446,12 +403,7 @@ export async function fetchStarsStatus() { return undefined; } - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); - return { - users, - chats, nextOffset: result.nextOffset, history: result.history?.map(buildApiStarsTransaction), balance: result.balance.toJSNumber(), @@ -481,12 +433,7 @@ export async function fetchStarsTransactions({ return undefined; } - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); - return { - users, - chats, nextOffset: result.nextOffset, history: result.history?.map(buildApiStarsTransaction), balance: result.balance.toJSNumber(), @@ -511,12 +458,7 @@ export async function fetchStarsTransactionById({ return undefined; } - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); - return { - users, - chats, transaction: buildApiStarsTransaction(result.history[0]), }; } diff --git a/src/api/gramjs/methods/phoneCallState.ts b/src/api/gramjs/methods/phoneCallState.ts index f9e508bcf..5eb27b15a 100644 --- a/src/api/gramjs/methods/phoneCallState.ts +++ b/src/api/gramjs/methods/phoneCallState.ts @@ -129,7 +129,7 @@ function computeEmojiIndex(bytes: Uint8Array) { .or((BigInt(bytes[7]))); } -export async function generateEmojiFingerprint( +async function generateEmojiFingerprint( authKey: Uint8Array, gA: Uint8Array, emojiData: Uint16Array, emojiOffsets: number[], ) { const hash = await Helpers.sha256(Buffer.concat([new Uint8Array(authKey), new Uint8Array(gA)])); diff --git a/src/api/gramjs/methods/reactions.ts b/src/api/gramjs/methods/reactions.ts index 6d2743ca8..1f54596e4 100644 --- a/src/api/gramjs/methods/reactions.ts +++ b/src/api/gramjs/methods/reactions.ts @@ -12,7 +12,6 @@ import { TOP_REACTIONS_LIMIT, } from '../../../config'; import { split } from '../../../util/iteratees'; -import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { buildApiAvailableEffect, buildApiAvailableReaction, @@ -21,9 +20,7 @@ import { buildMessagePeerReaction, } from '../apiBuilders/reactions'; import { buildStickerFromDocument } from '../apiBuilders/symbols'; -import { buildApiUser } from '../apiBuilders/users'; import { buildInputPeer, buildInputReaction } from '../gramjsBuilders'; -import { addEntitiesToLocalDb } from '../helpers'; import localDb from '../localDb'; import { invokeRequest } from './client'; @@ -187,14 +184,9 @@ export async function fetchMessageReactionsList({ return undefined; } - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - const { nextOffset, reactions, count } = result; return { - users: result.users.map(buildApiUser).filter(Boolean), - chats: result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean), nextOffset, reactions: reactions.map(buildMessagePeerReaction).filter(Boolean), count, diff --git a/src/api/gramjs/methods/settings.ts b/src/api/gramjs/methods/settings.ts index 91bdadf8f..2163c2cbb 100644 --- a/src/api/gramjs/methods/settings.ts +++ b/src/api/gramjs/methods/settings.ts @@ -13,11 +13,12 @@ import type { ApiUser, } from '../../types'; -import { BLOCKED_LIST_LIMIT, DEFAULT_LANG_PACK, MAX_INT_32 } from '../../../config'; +import { + ACCEPTABLE_USERNAME_ERRORS, BLOCKED_LIST_LIMIT, DEFAULT_LANG_PACK, MAX_INT_32, +} from '../../../config'; import { buildCollectionByKey } from '../../../util/iteratees'; import { getServerTime } from '../../../util/serverTime'; import { buildAppConfig } from '../apiBuilders/appConfig'; -import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { buildApiPhoto, buildPrivacyRules } from '../apiBuilders/common'; import { buildApiConfig, @@ -34,16 +35,14 @@ import { oldBuildLangPackString, } from '../apiBuilders/misc'; import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers'; -import { buildApiUser } from '../apiBuilders/users'; import { buildInputEntity, buildInputPeer, buildInputPhoto, buildInputPrivacyKey, buildInputPrivacyRules, } from '../gramjsBuilders'; -import { addEntitiesToLocalDb, addPhotoToLocalDb } from '../helpers'; +import { addPhotoToLocalDb } from '../helpers'; import localDb from '../localDb'; import { getClient, invokeRequest, uploadFile } from './client'; -import { ACCEPTABLE_USERNAME_ERRORS } from './management'; const BETA_LANG_CODES = ['ar', 'fa', 'id', 'ko', 'uz', 'en']; @@ -102,11 +101,9 @@ export async function updateProfilePhoto(photo?: ApiPhoto, isFallback?: boolean) })); if (!result) return undefined; - addEntitiesToLocalDb(result.users); if (result.photo instanceof GramJs.Photo) { addPhotoToLocalDb(result.photo); return { - users: result.users.map(buildApiUser).filter(Boolean), photo: buildApiPhoto(result.photo), }; } @@ -125,11 +122,9 @@ export async function uploadProfilePhoto( if (!result) return undefined; - addEntitiesToLocalDb(result.users); if (result.photo instanceof GramJs.Photo) { addPhotoToLocalDb(result.photo); return { - users: result.users.map(buildApiUser).filter(Boolean), photo: buildApiPhoto(result.photo), }; } @@ -152,20 +147,14 @@ export async function uploadContactProfilePhoto({ if (!result) return undefined; - addEntitiesToLocalDb(result.users); - - const users = result.users.map(buildApiUser).filter(Boolean); - if (result.photo instanceof GramJs.Photo) { addPhotoToLocalDb(result.photo); return { - users, photo: buildApiPhoto(result.photo), }; } return { - users, photo: undefined, }; } @@ -246,11 +235,7 @@ export async function fetchBlockedUsers({ return undefined; } - updateLocalDb(result); - return { - users: result.users.map(buildApiUser).filter(Boolean), - chats: result.chats.map((chat) => buildApiChatFromPreview(chat)).filter(Boolean), blockedIds: result.blocked.map((blocked) => getApiChatIdFromMtpPeer(blocked.peerId)), totalCount: result instanceof GramJs.contacts.BlockedSlice ? result.count : result.blocked.length, }; @@ -307,10 +292,8 @@ export async function fetchWebAuthorizations() { if (!result) { return undefined; } - addEntitiesToLocalDb(result.users); return { - users: result.users.map(buildApiUser).filter(Boolean), webAuthorizations: buildCollectionByKey(result.authorizations.map(buildApiWebSession), 'hash'), }; } @@ -334,8 +317,6 @@ export async function fetchNotificationExceptions() { return undefined; } - updateLocalDb(result); - return result.updates.reduce((acc, update) => { if (!(update instanceof GramJs.UpdateNotifySettings && update.peer instanceof GramJs.NotifyPeer)) { return acc; @@ -555,10 +536,7 @@ export async function fetchPrivacySettings(privacyKey: ApiPrivacyKey) { return undefined; } - updateLocalDb(result); - return { - users: result.users.map(buildApiUser).filter(Boolean), rules: buildPrivacyRules(result.rules), }; } @@ -595,10 +573,7 @@ export async function setPrivacySettings( return undefined; } - updateLocalDb(result); - return { - users: result.users.map(buildApiUser).filter(Boolean), rules: buildPrivacyRules(result.rules), }; } @@ -671,16 +646,6 @@ export async function fetchTimezones(hash?: number) { }; } -function updateLocalDb( - result: ( - GramJs.account.PrivacyRules | GramJs.contacts.Blocked | GramJs.contacts.BlockedSlice | - GramJs.Updates | GramJs.UpdatesCombined - ), -) { - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); -} - export async function fetchCountryList({ langCode = 'en' }: { langCode?: LangCode }) { const countryList = await invokeRequest(new GramJs.help.GetCountriesList({ langCode, diff --git a/src/api/gramjs/methods/statistics.ts b/src/api/gramjs/methods/statistics.ts index 758a606f5..610db9cb1 100644 --- a/src/api/gramjs/methods/statistics.ts +++ b/src/api/gramjs/methods/statistics.ts @@ -2,11 +2,10 @@ import BigInt from 'big-integer'; import { Api as GramJs } from '../../../lib/gramjs'; import type { - ApiChat, ApiMessagePublicForward, ApiPostStatistics, ApiStoryPublicForward, ApiUser, StatisticsGraph, + ApiChat, ApiMessagePublicForward, ApiPostStatistics, ApiStoryPublicForward, StatisticsGraph, } from '../../types'; import { STATISTICS_PUBLIC_FORWARDS_LIMIT } from '../../../config'; -import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { buildChannelMonetizationStatistics, buildChannelStatistics, @@ -16,9 +15,7 @@ import { buildPostsStatistics, buildStoryPublicForwards, } from '../apiBuilders/statistics'; -import { buildApiUser } from '../apiBuilders/users'; import { buildInputEntity, buildInputPeer } from '../gramjsBuilders'; -import { addEntitiesToLocalDb } from '../helpers'; import { invokeRequest } from './client'; export async function fetchChannelStatistics({ @@ -69,10 +66,7 @@ export async function fetchGroupStatistics({ return undefined; } - addEntitiesToLocalDb(result.users); - return { - users: result.users.map(buildApiUser).filter(Boolean), stats: buildGroupStatistics(result), }; } @@ -114,8 +108,6 @@ export async function fetchMessagePublicForwards({ forwards?: ApiMessagePublicForward[]; count?: number; nextOffset?: string; - chats: ApiChat[]; - users: ApiUser[]; } | undefined> { const result = await invokeRequest(new GramJs.stats.GetMessagePublicForwards({ channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel, @@ -130,15 +122,10 @@ export async function fetchMessagePublicForwards({ return undefined; } - addEntitiesToLocalDb(result.chats); - addEntitiesToLocalDb(result.users); - return { forwards: buildMessagePublicForwards(result), count: result.count, nextOffset: result.nextOffset, - chats: result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean), - users: result.users.map(buildApiUser).filter(Boolean), }; } @@ -202,8 +189,6 @@ export async function fetchStoryPublicForwards({ offset?: string; }): Promise<{ publicForwards: (ApiMessagePublicForward | ApiStoryPublicForward)[] | undefined; - users: ApiUser[]; - chats: ApiChat[]; count?: number; nextOffset?: string; } | undefined> { @@ -220,13 +205,8 @@ export async function fetchStoryPublicForwards({ return undefined; } - addEntitiesToLocalDb(result.chats); - addEntitiesToLocalDb(result.users); - return { publicForwards: buildStoryPublicForwards(result), - users: result.users.map(buildApiUser).filter(Boolean), - chats: result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean), count: result.count, nextOffset: result.nextOffset, }; diff --git a/src/api/gramjs/methods/stories.ts b/src/api/gramjs/methods/stories.ts index 3f2247a8f..ae7d4feba 100644 --- a/src/api/gramjs/methods/stories.ts +++ b/src/api/gramjs/methods/stories.ts @@ -2,19 +2,16 @@ import { Api as GramJs } from '../../../lib/gramjs'; import type { ApiInputPrivacyRules } from '../../../types'; import type { - ApiChat, ApiPeer, ApiPeerStories, ApiReaction, ApiReportReason, ApiStealthMode, ApiTypeStory, - ApiUser, } from '../../types'; import { STORY_LIST_LIMIT } from '../../../config'; import { buildCollectionByCallback } from '../../../util/iteratees'; -import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers'; import { buildApiPeerStories, @@ -23,14 +20,13 @@ import { buildApiStoryView, buildApiStoryViews, } from '../apiBuilders/stories'; -import { buildApiUser } from '../apiBuilders/users'; import { buildInputPeer, buildInputPrivacyRules, buildInputReaction, buildInputReportReason, } from '../gramjsBuilders'; -import { addEntitiesToLocalDb, addStoryToLocalDb } from '../helpers'; +import { addStoryToLocalDb } from '../helpers'; import { invokeRequest } from './client'; export async function fetchAllStories({ @@ -45,8 +41,6 @@ export async function fetchAllStories({ undefined | { state: string; stealthMode: ApiStealthMode } | { - users: ApiUser[]; - chats: ApiChat[]; peerStories: Record; hasMore?: true; state: string; @@ -68,9 +62,6 @@ export async function fetchAllStories({ }; } - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - const allUserStories = result.peerStories.reduce>((acc, peerStories) => { const peerId = getApiChatIdFromMtpPeer(peerStories.peer); const stories = buildApiPeerStories(peerStories); @@ -117,8 +108,6 @@ export async function fetchAllStories({ )); return { - users: result.users.map(buildApiUser).filter(Boolean), - chats: result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean), peerStories: allUserStories, hasMore: result.hasMore, state: result.state, @@ -139,10 +128,6 @@ export async function fetchPeerStories({ return undefined; } - addEntitiesToLocalDb(result.users); - - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); const stories = buildCollectionByCallback(result.stories.stories, (story) => ( [story.id, buildApiStory(peer.id, story)] )); @@ -151,8 +136,6 @@ export async function fetchPeerStories({ result.stories.stories.forEach((story) => addStoryToLocalDb(story, peer.id)); return { - chats, - users, stories, lastReadStoryId: result.stories.maxReadId, }; @@ -201,11 +184,6 @@ export async function fetchPeerStoriesByIds({ peer, ids }: { peer: ApiPeer; ids: return undefined; } - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); const stories = ids.reduce>((acc, id) => { const story = result.stories.find(({ id: currentId }) => currentId === id); if (story) { @@ -225,8 +203,6 @@ export async function fetchPeerStoriesByIds({ peer, ids }: { peer: ApiPeer; ids: result.stories.forEach((story) => addStoryToLocalDb(story, peer.id)); return { - chats, - users, pinnedIds: result.pinnedToTop, stories, }; @@ -307,15 +283,9 @@ export async function fetchStoryViewList({ return undefined; } - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); const views = result.views.map(buildApiStoryView).filter(Boolean); return { - users, - chats, views, nextOffset: result.nextOffset, reactionsCount: result.reactionsCount, @@ -339,14 +309,10 @@ export async function fetchStoriesViews({ return undefined; } - addEntitiesToLocalDb(result.users); - const views = buildApiStoryViews(result.views[0]); - const users = result.users.map(buildApiUser).filter(Boolean); return { views, - users, }; } @@ -430,11 +396,6 @@ async function fetchCommonStoriesRequest({ method, peerId }: { return undefined; } - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - - const users = result.users.map(buildApiUser).filter(Boolean); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); const stories = buildCollectionByCallback(result.stories, (story) => ( [story.id, buildApiStory(peerId, story)] )); @@ -443,8 +404,6 @@ async function fetchCommonStoriesRequest({ method, peerId }: { result.stories.forEach((story) => addStoryToLocalDb(story, peerId)); return { - users, - chats, stories, pinnedIds: result.pinnedToTop, }; diff --git a/src/api/gramjs/methods/symbols.ts b/src/api/gramjs/methods/symbols.ts index 31192d6af..b1adc71d0 100644 --- a/src/api/gramjs/methods/symbols.ts +++ b/src/api/gramjs/methods/symbols.ts @@ -2,7 +2,7 @@ import BigInt from 'big-integer'; import { Api as GramJs } from '../../../lib/gramjs'; import type { - ApiSticker, ApiStickerSetInfo, ApiVideo, OnApiUpdate, + ApiSticker, ApiStickerSetInfo, ApiVideo, } from '../../types'; import { DEFAULT_GIF_SEARCH_BOT_USERNAME, RECENT_STATUS_LIMIT, RECENT_STICKERS_LIMIT } from '../../../config'; @@ -13,14 +13,9 @@ import { } from '../apiBuilders/symbols'; import { buildInputDocument, buildInputStickerSet, buildInputStickerSetShortName } from '../gramjsBuilders'; import localDb from '../localDb'; +import { sendApiUpdate } from '../updates/apiUpdateEmitter'; import { invokeRequest } from './client'; -let onUpdate: OnApiUpdate; - -export function init(_onUpdate: OnApiUpdate) { - onUpdate = _onUpdate; -} - export async function fetchCustomEmojiSets({ hash = '0' }: { hash?: string }) { const allStickers = await invokeRequest(new GramJs.messages.GetEmojiStickers({ hash: BigInt(hash) })); @@ -132,7 +127,7 @@ export async function faveSticker({ const result = await invokeRequest(request); if (result) { - onUpdate({ + sendApiUpdate({ '@type': 'updateFavoriteStickers', }); } @@ -325,7 +320,7 @@ export async function installStickerSet({ stickerSetId, accessHash }: { stickerS })); if (result) { - onUpdate({ + sendApiUpdate({ '@type': 'updateStickerSet', id: stickerSetId, stickerSet: { installedDate: Date.now() }, @@ -339,7 +334,7 @@ export async function uninstallStickerSet({ stickerSetId, accessHash }: { sticke })); if (result) { - onUpdate({ + sendApiUpdate({ '@type': 'updateStickerSet', id: stickerSetId, stickerSet: { installedDate: undefined }, diff --git a/src/api/gramjs/methods/twoFaSettings.ts b/src/api/gramjs/methods/twoFaSettings.ts index 5255aeacc..f4a67aa1f 100644 --- a/src/api/gramjs/methods/twoFaSettings.ts +++ b/src/api/gramjs/methods/twoFaSettings.ts @@ -1,8 +1,7 @@ import { Api as GramJs, errors } from '../../../lib/gramjs'; -import type { OnApiUpdate } from '../../types'; - import { DEBUG } from '../../../config'; +import { sendApiUpdate } from '../updates/apiUpdateEmitter'; import { getTmpPassword, invokeRequest, updateTwoFaSettings } from './client'; const ApiErrors: { [k: string]: string } = { @@ -19,12 +18,6 @@ const emailCodeController: { reject?: Function; } = {}; -let onUpdate: OnApiUpdate; - -export function init(_onUpdate: OnApiUpdate) { - onUpdate = _onUpdate; -} - export async function getPasswordInfo() { const result = await invokeRequest(new GramJs.account.GetPassword()); if (!result) { @@ -37,7 +30,7 @@ export async function getPasswordInfo() { } function onRequestEmailCode(length: number) { - onUpdate({ + sendApiUpdate({ '@type': 'updateTwoFaStateWaitCode', length, }); @@ -136,7 +129,7 @@ function onError(err: Error) { } } - onUpdate({ + sendApiUpdate({ '@type': 'updateTwoFaError', message, }); diff --git a/src/api/gramjs/methods/users.ts b/src/api/gramjs/methods/users.ts index ff657f0ff..b9e43ac3f 100644 --- a/src/api/gramjs/methods/users.ts +++ b/src/api/gramjs/methods/users.ts @@ -2,15 +2,14 @@ import BigInt from 'big-integer'; import { Api as GramJs } from '../../../lib/gramjs'; import type { - ApiChat, ApiPeer, ApiSticker, - ApiUser, OnApiUpdate, + ApiChat, ApiPeer, ApiSticker, ApiUser, } from '../../types'; import { COMMON_CHATS_LIMIT } from '../../../config'; import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { buildApiPhoto } from '../apiBuilders/common'; import { buildApiPeerId } from '../apiBuilders/peers'; -import { buildApiUser, buildApiUserFullInfo, buildApiUsersAndStatuses } from '../apiBuilders/users'; +import { buildApiUser, buildApiUserFullInfo, buildApiUserStatuses } from '../apiBuilders/users'; import { buildInputContact, buildInputEmojiStatus, @@ -19,17 +18,12 @@ import { buildMtpPeerId, getEntityTypeById, } from '../gramjsBuilders'; -import { addEntitiesToLocalDb, addPhotoToLocalDb, addUserToLocalDb } from '../helpers'; +import { addPhotoToLocalDb, addUserToLocalDb } from '../helpers'; import localDb from '../localDb'; +import { sendApiUpdate } from '../updates/apiUpdateEmitter'; import { invokeRequest } from './client'; import { searchMessagesInChat } from './messages'; -let onUpdate: OnApiUpdate; - -export function init(_onUpdate: OnApiUpdate) { - onUpdate = _onUpdate; -} - export async function fetchFullUser({ id, accessHash, @@ -48,10 +42,6 @@ export async function fetchFullUser({ return undefined; } - updateLocalDb(result); - addEntitiesToLocalDb(result.users); - addEntitiesToLocalDb(result.chats); - if (result.fullUser.profilePhoto) { addPhotoToLocalDb(result.fullUser.profilePhoto); } @@ -82,7 +72,7 @@ export async function fetchFullUser({ const user = users.find(({ id: userId }) => userId === id)!; - onUpdate({ + sendApiUpdate({ '@type': 'updateUser', id, user, @@ -108,21 +98,10 @@ export async function fetchCommonChats(id: string, accessHash?: string, maxId?: return undefined; } - updateLocalDb(commonChats); + const chats = commonChats.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); + const chatIds = chats.map(({ id: chatId }) => chatId); - const chatIds: string[] = []; - const chats: ApiChat[] = []; - - commonChats.chats.forEach((mtpChat) => { - const chat = buildApiChatFromPreview(mtpChat); - - if (chat) { - chats.push(chat); - chatIds.push(chat.id); - } - }); - - return { chats, chatIds, isFullyLoaded: chatIds.length < COMMON_CHATS_LIMIT }; + return { chatIds, isFullyLoaded: chatIds.length < COMMON_CHATS_LIMIT }; } export async function fetchNearestCountry() { @@ -144,7 +123,6 @@ export async function fetchTopUsers() { return { ids, - users, }; } @@ -154,14 +132,12 @@ export async function fetchContactList() { return undefined; } - addEntitiesToLocalDb(result.users); - - const { users, userStatusesById } = buildApiUsersAndStatuses(result.users); + const users = result.users.map(buildApiUser).filter(Boolean) as ApiUser[]; + const userStatusesById = buildApiUserStatuses(result.users); return { users, userStatusesById, - chats: result.users.map((user) => buildApiChatFromPreview(user)).filter(Boolean), }; } @@ -173,9 +149,13 @@ export async function fetchUsers({ users }: { users: ApiUser[] }) { return undefined; } - addEntitiesToLocalDb(result); + const apiUsers = result.map(buildApiUser).filter(Boolean) as ApiUser[]; + const userStatusesById = buildApiUserStatuses(result); - return buildApiUsersAndStatuses(result); + return { + users: apiUsers, + userStatusesById, + }; } export async function importContact({ @@ -246,7 +226,7 @@ export async function deleteContact({ return; } - onUpdate({ + sendApiUpdate({ '@type': 'deleteContact', id, }); @@ -277,7 +257,7 @@ export async function fetchProfilePhotos({ return undefined; } - updateLocalDb(result); + result.photos.forEach(addPhotoToLocalDb); const count = result instanceof GramJs.photos.PhotosSlice ? result.count : result.photos.length; const proposedNextOffsetId = offset + result.photos.length; @@ -288,7 +268,6 @@ export async function fetchProfilePhotos({ photos: result.photos .filter((photo): photo is GramJs.Photo => photo instanceof GramJs.Photo) .map((photo) => buildApiPhoto(photo)), - users: result.users.map(buildApiUser).filter(Boolean), nextOffsetId, }; } @@ -306,13 +285,12 @@ export async function fetchProfilePhotos({ } const { - messages, users, totalCount, nextOffsetId, + messages, totalCount, nextOffsetId, } = result; return { count: totalCount, photos: messages.map((message) => message.content.action!.photo).filter(Boolean), - users, nextOffsetId, }; } @@ -342,17 +320,3 @@ export function saveCloseFriends(userIds: string[]) { shouldReturnTrue: true, }); } - -function updateLocalDb(result: (GramJs.photos.Photos | GramJs.photos.PhotosSlice | GramJs.messages.Chats)) { - if ('chats' in result) { - addEntitiesToLocalDb(result.chats); - } - - if ('photos' in result) { - result.photos.forEach(addPhotoToLocalDb); - } - - if ('users' in result) { - addEntitiesToLocalDb(result.users); - } -} diff --git a/src/api/gramjs/updates/apiUpdateEmitter.ts b/src/api/gramjs/updates/apiUpdateEmitter.ts new file mode 100644 index 000000000..1b09eb02f --- /dev/null +++ b/src/api/gramjs/updates/apiUpdateEmitter.ts @@ -0,0 +1,50 @@ +import type { ApiUpdate, OnApiUpdate } from '../../types'; + +import { API_THROTTLE_RESET_UPDATES, API_UPDATE_THROTTLE } from '../../../config'; +import { throttle, throttleWithTickEnd } from '../../../util/schedulers'; + +let onUpdate: OnApiUpdate; + +export function init(_onUpdate: OnApiUpdate) { + onUpdate = _onUpdate; +} + +export function sendApiUpdate(update: ApiUpdate) { + queueUpdate(update); +} + +export function sendImmediateApiUpdate(update: ApiUpdate) { + onUpdate(update); +} + +const flushUpdatesOnTickEnd = throttleWithTickEnd(flushUpdates); + +let flushUpdatesThrottled: typeof flushUpdatesOnTickEnd | undefined; +let currentThrottleId: number | undefined; + +let pendingUpdates: ApiUpdate[] | undefined; + +function queueUpdate(update: ApiUpdate) { + if (!pendingUpdates) { + pendingUpdates = [update]; + } else { + pendingUpdates.push(update); + } + + if (!flushUpdatesThrottled || API_THROTTLE_RESET_UPDATES.has(update['@type'])) { + flushUpdatesThrottled = throttle(flushUpdatesOnTickEnd, API_UPDATE_THROTTLE, true); + currentThrottleId = Math.random(); + } + + flushUpdatesThrottled(currentThrottleId!); +} + +function flushUpdates(throttleId: number) { + if (!pendingUpdates || throttleId !== currentThrottleId) { + return; + } + + const currentUpdates = pendingUpdates!; + pendingUpdates = undefined; + currentUpdates.forEach(onUpdate); +} diff --git a/src/api/gramjs/updates/entityProcessor.ts b/src/api/gramjs/updates/entityProcessor.ts new file mode 100644 index 000000000..60c03fd29 --- /dev/null +++ b/src/api/gramjs/updates/entityProcessor.ts @@ -0,0 +1,69 @@ +import { Api as GramJs } from '../../../lib/gramjs'; + +import type { ApiChat, ApiThreadInfo, ApiUser } from '../../types'; + +import { buildCollectionByKey } from '../../../util/iteratees'; +import { buildApiChatFromPreview } from '../apiBuilders/chats'; +import { buildApiThreadInfoFromMessage } from '../apiBuilders/messages'; +import { buildApiUser } from '../apiBuilders/users'; +import { addChatToLocalDb, addMessageToLocalDb, addUserToLocalDb } from '../helpers'; +import { sendImmediateApiUpdate } from './apiUpdateEmitter'; + +const TYPE_USER = new Set(['User', 'UserEmpty']); +const TYPE_CHAT = new Set(['ChatEmpty', 'Chat', 'ChatForbidden', 'Channel', 'ChannelForbidden']); +const TYPE_MESSAGE = new Set(['Message', 'MessageEmpty', 'MessageService']); + +export function processAndUpdateEntities(response?: GramJs.AnyRequest['__response']) { + if (!response || typeof response !== 'object') return; + if (!('users' in response || 'chats' in response || 'messages' in response)) return; + + let userById: Record | undefined; + let chatById: Record | undefined; + let threadInfos: ApiThreadInfo[] | undefined; + + if ('users' in response && Array.isArray(response.users) && TYPE_USER.has(response.users[0]?.className)) { + const users = response.users.map((user) => { + if (user instanceof GramJs.User) { + addUserToLocalDb(user); + } + return buildApiUser(user); + }).filter(Boolean); + userById = buildCollectionByKey(users, 'id'); + } + + if ('chats' in response && Array.isArray(response.chats) && TYPE_CHAT.has(response.chats[0]?.className)) { + const chats = response.chats.map((chat) => { + if ((chat instanceof GramJs.Chat || chat instanceof GramJs.Channel)) { + addChatToLocalDb(chat); + } + return buildApiChatFromPreview(chat); + }).filter(Boolean); + chatById = buildCollectionByKey(chats, 'id'); + } + + if ('messages' in response && Array.isArray(response.messages) && TYPE_MESSAGE.has(response.messages[0]?.className)) { + threadInfos = response.messages.map((message) => { + addMessageToLocalDb(message); + return buildApiThreadInfoFromMessage(message); + }).filter(Boolean); + } + + if (!userById && !chatById && !threadInfos) return; + + sendImmediateApiUpdate({ + '@type': 'updateEntities', + users: userById, + chats: chatById, + threadInfos, + }); +} + +export function processMessageAndUpdateThreadInfo(message: GramJs.TypeMessage) { + addMessageToLocalDb(message); + const threadInfo = buildApiThreadInfoFromMessage(message); + if (!threadInfo) return; + sendImmediateApiUpdate({ + '@type': 'updateThreadInfo', + threadInfo, + }); +} diff --git a/src/api/gramjs/updates/updater.ts b/src/api/gramjs/updates/mtpUpdateHandler.ts similarity index 85% rename from src/api/gramjs/updates/updater.ts rename to src/api/gramjs/updates/mtpUpdateHandler.ts index 4f03149af..0595e422b 100644 --- a/src/api/gramjs/updates/updater.ts +++ b/src/api/gramjs/updates/mtpUpdateHandler.ts @@ -3,11 +3,13 @@ import { Api as GramJs, connection } from '../../../lib/gramjs'; import type { GroupCallConnectionData } from '../../../lib/secret-sauce'; import type { ApiMessage, ApiStory, ApiStorySkipped, - ApiUpdate, ApiUpdateConnectionStateType, OnApiUpdate, + ApiUpdateConnectionStateType, } from '../../types'; import { DEBUG, GENERAL_TOPIC_ID } from '../../../config'; -import { compact, omit, pick } from '../../../util/iteratees'; +import { + omit, pick, +} from '../../../util/iteratees'; import { getServerTimeOffset, setServerTimeOffset } from '../../../util/serverTime'; import { buildApiBotMenuButton } from '../apiBuilders/bots'; import { @@ -40,7 +42,6 @@ import { buildApiMessageFromShort, buildApiMessageFromShortChat, buildApiQuickReply, - buildApiThreadInfoFromMessage, buildMessageDraft, } from '../apiBuilders/messages'; import { @@ -64,8 +65,6 @@ import { buildMessageFromUpdate, } from '../gramjsBuilders'; import { - addEntitiesToLocalDb, - addMessageToLocalDb, addPhotoToLocalDb, addStoryToLocalDb, isChatFolder, @@ -75,6 +74,8 @@ import { } from '../helpers'; import localDb from '../localDb'; import { scheduleMutedChatUpdate, scheduleMutedTopicUpdate } from '../scheduleUnmute'; +import { sendApiUpdate } from './apiUpdateEmitter'; +import { processMessageAndUpdateThreadInfo } from './entityProcessor'; import LocalUpdatePremiumFloodWait from './UpdatePremiumFloodWait'; import { LocalUpdateChannelPts, LocalUpdatePts, type UpdatePts } from './UpdatePts'; @@ -83,68 +84,13 @@ export type Update = ( (GramJs.TypeUpdate | GramJs.TypeUpdates) & { _entities?: (GramJs.TypeUser | GramJs.TypeChat)[] } ) | typeof connection.UpdateConnectionState | UpdatePts | LocalUpdatePremiumFloodWait; -let onUpdate: OnApiUpdate; - -export function init(_onUpdate: OnApiUpdate) { - onUpdate = _onUpdate; -} - const sentMessageIds = new Set(); -export function dispatchUserAndChatUpdates(entities: (GramJs.TypeUser | GramJs.TypeChat)[]) { - entities - .filter((e) => e instanceof GramJs.User) - .map(buildApiUser) - .forEach((user) => { - if (!user) { - return; - } - - onUpdate({ - '@type': 'updateUser', - id: user.id, - user, - }); - }); - - entities - .filter((e) => ( - e instanceof GramJs.Chat || e instanceof GramJs.ChatForbidden - || e instanceof GramJs.Channel || e instanceof GramJs.ChannelForbidden - )) - .map((e) => buildApiChatFromPreview(e)) - .forEach((chat) => { - if (!chat) { - return; - } - - onUpdate({ - '@type': 'updateChat', - id: chat.id, - chat, - }); - }); -} - -export function dispatchThreadInfoUpdates(messages: (GramJs.TypeMessage | undefined)[]) { - const threadInfoUpdates = compact(messages).map(buildApiThreadInfoFromMessage).filter(Boolean); - if (!threadInfoUpdates.length) return; - - onUpdate({ - '@type': 'updateThreadInfos', - threadInfoUpdates, - }); -} - -export function sendUpdate(update: ApiUpdate) { - onUpdate(update); -} - export function updater(update: Update) { if (update instanceof connection.UpdateServerTimeOffset) { setServerTimeOffset(update.timeOffset); - onUpdate({ + sendApiUpdate({ '@type': 'updateServerTimeOffset', serverTimeOffset: update.timeOffset, }); @@ -164,7 +110,7 @@ export function updater(update: Update) { break; } - onUpdate({ + sendApiUpdate({ '@type': 'updateConnectionState', connectionState, }); @@ -180,13 +126,6 @@ export function updater(update: Update) { let message: ApiMessage | undefined; let shouldForceReply: boolean | undefined; - // eslint-disable-next-line no-underscore-dangle - const entities = update._entities; - if (entities) { - addEntitiesToLocalDb(entities); - dispatchUserAndChatUpdates(entities); - } - if (update instanceof GramJs.UpdateShortChatMessage) { message = buildApiMessageFromShortChat(update); } else if (update instanceof GramJs.UpdateShortMessage) { @@ -202,10 +141,9 @@ export function updater(update: Update) { return; } - addMessageToLocalDb(update.message); + processMessageAndUpdateThreadInfo(update.message); message = buildApiMessage(update.message)!; - dispatchThreadInfoUpdates([update.message]); shouldForceReply = 'replyMarkup' in update.message && update.message?.replyMarkup instanceof GramJs.ReplyKeyboardForceReply @@ -213,7 +151,7 @@ export function updater(update: Update) { } if (update instanceof GramJs.UpdateNewScheduledMessage) { - onUpdate({ + sendApiUpdate({ '@type': sentMessageIds.has(message.id) ? 'updateScheduledMessage' : 'newScheduledMessage', id: message.id, chatId: message.chatId, @@ -222,7 +160,7 @@ export function updater(update: Update) { } else { // We don't have preview for action or 'via bot' messages, so `newMessage` update here is required const hasLocalCopy = sentMessageIds.has(message.id) && !message.viaBotId && !message.content.action; - onUpdate({ + sendApiUpdate({ '@type': hasLocalCopy ? 'updateMessage' : 'newMessage', id: message.id, chatId: message.chatId, @@ -237,7 +175,7 @@ export function updater(update: Update) { const { action } = update.message; if (action instanceof GramJs.MessageActionChatEditTitle) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: message.chatId, chat: { @@ -256,7 +194,7 @@ export function updater(update: Update) { } addPhotoToLocalDb(action.photo); - onUpdate({ + sendApiUpdate({ '@type': 'updateNewProfilePhoto', peerId: message.chatId, photo: apiPhoto, @@ -267,7 +205,7 @@ export function updater(update: Update) { localDb.chats[localDbChatId].photo = new GramJs.ChatPhotoEmpty(); } - onUpdate({ + sendApiUpdate({ '@type': 'updateDeleteProfilePhoto', peerId: message.chatId, }); @@ -276,7 +214,7 @@ export function updater(update: Update) { if (update._entities && update._entities.some((e): e is GramJs.User => ( e instanceof GramJs.User && Boolean(e.self) && e.id === action.userId ))) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: message.chatId, chat: { @@ -290,14 +228,14 @@ export function updater(update: Update) { if (update._entities && update._entities.some((e): e is GramJs.User => ( e instanceof GramJs.User && Boolean(e.self) && action.users.includes(e.id) ))) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChatJoin', id: message.chatId, }); } } else if (action instanceof GramJs.MessageActionGroupCall) { if (!action.duration && action.call) { - onUpdate({ + sendApiUpdate({ '@type': 'updateGroupCallChatId', chatId: message.chatId, call: { @@ -315,13 +253,13 @@ export function updater(update: Update) { } = replyTo || {}; const topicId = !isTopicReply ? GENERAL_TOPIC_ID : replyToTopId || replyToMsgId || GENERAL_TOPIC_ID; - onUpdate({ + sendApiUpdate({ '@type': 'updateTopic', chatId: getApiChatIdFromMtpPeer(update.message.peerId!), topicId, }); } else if (action instanceof GramJs.MessageActionTopicCreate) { - onUpdate({ + sendApiUpdate({ '@type': 'updateTopics', chatId: getApiChatIdFromMtpPeer(update.message.peerId!), }); @@ -331,31 +269,31 @@ export function updater(update: Update) { const message = buildApiMessage(update.message); if (!message) return; - onUpdate({ + sendApiUpdate({ '@type': 'updateQuickReplyMessage', id: message.id, message, }); } else if (update instanceof GramJs.UpdateDeleteQuickReplyMessages) { - onUpdate({ + sendApiUpdate({ '@type': 'deleteQuickReplyMessages', quickReplyId: update.shortcutId, messageIds: update.messages, }); } else if (update instanceof GramJs.UpdateQuickReplies) { const quickReplies = update.quickReplies.map(buildApiQuickReply); - onUpdate({ + sendApiUpdate({ '@type': 'updateQuickReplies', quickReplies, }); } else if (update instanceof GramJs.UpdateNewQuickReply) { const quickReply = buildApiQuickReply(update.quickReply); - onUpdate({ + sendApiUpdate({ '@type': 'updateQuickReplies', quickReplies: [quickReply], }); } else if (update instanceof GramJs.UpdateDeleteQuickReply) { - onUpdate({ + sendApiUpdate({ '@type': 'deleteQuickReply', quickReplyId: update.shortcutId, }); @@ -373,20 +311,19 @@ export function updater(update: Update) { return; } - addMessageToLocalDb(update.message); + processMessageAndUpdateThreadInfo(update.message); // Workaround for a weird server behavior when own message is marked as incoming const message = omit(buildApiMessage(update.message)!, ['isOutgoing']); - dispatchThreadInfoUpdates([update.message]); - onUpdate({ + sendApiUpdate({ '@type': 'updateMessage', id: message.id, chatId: message.chatId, message, }); } else if (update instanceof GramJs.UpdateMessageReactions) { - onUpdate({ + sendApiUpdate({ '@type': 'updateMessageReactions', id: update.msgId, chatId: getApiChatIdFromMtpPeer(update.peer), @@ -400,7 +337,7 @@ export function updater(update: Update) { if (!boughtMedia?.length) return; - onUpdate({ + sendApiUpdate({ '@type': 'updateMessageExtendedMedia', id: update.msgId, chatId, @@ -417,19 +354,19 @@ export function updater(update: Update) { if (!previewMedia?.length) return; - onUpdate({ + sendApiUpdate({ '@type': 'updateMessageExtendedMedia', id: update.msgId, chatId, extendedMedia: previewMedia, }); } else if (update instanceof GramJs.UpdateDeleteMessages) { - onUpdate({ + sendApiUpdate({ '@type': 'deleteMessages', ids: update.messages, }); } else if (update instanceof GramJs.UpdateDeleteScheduledMessages) { - onUpdate({ + sendApiUpdate({ '@type': 'deleteScheduledMessages', ids: update.messages, chatId: getApiChatIdFromMtpPeer(update.peer), @@ -437,14 +374,14 @@ export function updater(update: Update) { } else if (update instanceof GramJs.UpdateDeleteChannelMessages) { const chatId = buildApiPeerId(update.channelId, 'channel'); - onUpdate({ + sendApiUpdate({ '@type': 'deleteMessages', ids: update.messages, chatId, }); } else if (update instanceof GramJs.UpdateServiceNotification) { if (update.popup) { - onUpdate({ + sendApiUpdate({ '@type': 'error', error: { message: update.message, @@ -454,9 +391,9 @@ export function updater(update: Update) { const currentDate = Date.now() / 1000 + getServerTimeOffset(); const message = buildApiMessageFromNotification(update, currentDate); - addMessageToLocalDb(buildMessageFromUpdate(message.id, message.chatId, update)); + processMessageAndUpdateThreadInfo(buildMessageFromUpdate(message.id, message.chatId, update)); - onUpdate({ + sendApiUpdate({ '@type': 'updateServiceNotification', message, }); @@ -464,7 +401,7 @@ export function updater(update: Update) { } else if (update instanceof GramJs.UpdateMessageID || update instanceof GramJs.UpdateShortSentMessage) { sentMessageIds.add(update.id); } else if (update instanceof GramJs.UpdateReadMessagesContents) { - onUpdate({ + sendApiUpdate({ '@type': 'updateCommonBoxMessages', ids: update.messages, messageUpdate: { @@ -473,7 +410,7 @@ export function updater(update: Update) { }, }); } else if (update instanceof GramJs.UpdateChannelReadMessagesContents) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChannelMessages', channelId: buildApiPeerId(update.channelId, 'channel'), ids: update.messages, @@ -487,28 +424,28 @@ export function updater(update: Update) { if (poll) { const apiPoll = buildPoll(poll, results); - onUpdate({ + sendApiUpdate({ '@type': 'updateMessagePoll', pollId: String(pollId), pollUpdate: apiPoll, }); } else { const pollResults = buildPollResults(results); - onUpdate({ + sendApiUpdate({ '@type': 'updateMessagePoll', pollId: String(pollId), pollUpdate: { results: pollResults }, }); } } else if (update instanceof GramJs.UpdateMessagePollVote) { - onUpdate({ + sendApiUpdate({ '@type': 'updateMessagePollVote', pollId: String(update.pollId), peerId: getApiChatIdFromMtpPeer(update.peer), options: update.options.map(serializeBytes), }); } else if (update instanceof GramJs.UpdateChannelMessageViews) { - onUpdate({ + sendApiUpdate({ '@type': 'updateMessage', chatId: buildApiPeerId(update.channelId, 'channel'), id: update.id, @@ -517,7 +454,7 @@ export function updater(update: Update) { // Chats } else if (update instanceof GramJs.UpdateReadHistoryInbox) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChatInbox', id: getApiChatIdFromMtpPeer(update.peer), chat: { @@ -526,7 +463,7 @@ export function updater(update: Update) { }, }); } else if (update instanceof GramJs.UpdateReadHistoryOutbox) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: getApiChatIdFromMtpPeer(update.peer), chat: { @@ -534,7 +471,7 @@ export function updater(update: Update) { }, }); } else if (update instanceof GramJs.UpdateReadChannelInbox) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: buildApiPeerId(update.channelId, 'channel'), chat: { @@ -543,7 +480,7 @@ export function updater(update: Update) { }, }); } else if (update instanceof GramJs.UpdateReadChannelOutbox) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: buildApiPeerId(update.channelId, 'channel'), chat: { @@ -551,16 +488,16 @@ export function updater(update: Update) { }, }); } else if (update instanceof GramJs.UpdateReadChannelDiscussionInbox) { - onUpdate({ - '@type': 'updateThreadInfos', - threadInfoUpdates: [{ + sendApiUpdate({ + '@type': 'updateThreadInfo', + threadInfo: { chatId: buildApiPeerId(update.channelId, 'channel'), threadId: update.topMsgId, lastReadInboxMessageId: update.readMaxId, - }], + }, }); } else if (update instanceof GramJs.UpdateReadChannelDiscussionOutbox) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: buildApiPeerId(update.channelId, 'channel'), chat: { @@ -571,7 +508,7 @@ export function updater(update: Update) { update instanceof GramJs.UpdateDialogPinned && update.peer instanceof GramJs.DialogPeer ) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChatPinned', id: getApiChatIdFromMtpPeer(update.peer.peer), isPinned: update.pinned || false, @@ -583,7 +520,7 @@ export function updater(update: Update) { .map((dp) => getApiChatIdFromMtpPeer(dp.peer)) : []; - onUpdate({ + sendApiUpdate({ '@type': 'updatePinnedChatIds', ids, folderId: update.folderId || undefined, @@ -592,7 +529,7 @@ export function updater(update: Update) { update instanceof GramJs.UpdateSavedDialogPinned && update.peer instanceof GramJs.DialogPeer ) { - onUpdate({ + sendApiUpdate({ '@type': 'updateSavedDialogPinned', id: getApiChatIdFromMtpPeer(update.peer.peer), isPinned: update.pinned || false, @@ -604,7 +541,7 @@ export function updater(update: Update) { .map((dp) => getApiChatIdFromMtpPeer(dp.peer)) : []; - onUpdate({ + sendApiUpdate({ '@type': 'updatePinnedSavedDialogIds', ids, }); @@ -612,7 +549,7 @@ export function updater(update: Update) { update.folderPeers.forEach((folderPeer) => { const { folderId, peer } = folderPeer; - onUpdate({ + sendApiUpdate({ '@type': 'updateChatListType', id: getApiChatIdFromMtpPeer(peer), folderId, @@ -622,20 +559,20 @@ export function updater(update: Update) { const { id, filter } = update; const folder = isChatFolder(filter) ? buildApiChatFolder(filter) : undefined; - onUpdate({ + sendApiUpdate({ '@type': 'updateChatFolder', id, folder, }); } else if (update instanceof GramJs.UpdateDialogFilterOrder) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChatFoldersOrder', orderedIds: update.order, }); } else if (update instanceof GramJs.UpdateChatParticipants) { const replacedMembers = buildChatMembers(update.participants); - onUpdate({ + sendApiUpdate({ '@type': 'updateChatMembers', id: buildApiPeerId(update.participants.chatId, 'chat'), replacedMembers, @@ -645,13 +582,13 @@ export function updater(update: Update) { pick(update, ['userId', 'inviterId', 'date']) as GramJs.ChatParticipant, ); - onUpdate({ + sendApiUpdate({ '@type': 'updateChatMembers', id: buildApiPeerId(update.chatId, 'chat'), addedMember, }); } else if (update instanceof GramJs.UpdateChatParticipantDelete) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChatMembers', id: buildApiPeerId(update.chatId, 'chat'), deletedMemberId: buildApiPeerId(update.userId, 'user'), @@ -664,7 +601,7 @@ export function updater(update: Update) { ? getApiChatIdFromMtpPeer(update.peer) : buildApiPeerId(update.channelId, 'channel'); - onUpdate({ + sendApiUpdate({ '@type': 'updatePinnedIds', chatId, messageIds: update.messages, @@ -675,8 +612,8 @@ export function updater(update: Update) { && update.peer instanceof GramJs.NotifyPeer ) { const payload = buildApiNotifyException(update.notifySettings, update.peer.peer); - scheduleMutedChatUpdate(payload.chatId, payload.muteUntil, onUpdate); - onUpdate({ + scheduleMutedChatUpdate(payload.chatId, payload.muteUntil, sendApiUpdate); + sendApiUpdate({ '@type': 'updateNotifyExceptions', ...payload, }); @@ -687,8 +624,8 @@ export function updater(update: Update) { const payload = buildApiNotifyExceptionTopic( update.notifySettings, update.peer.peer, update.peer.topMsgId, ); - scheduleMutedTopicUpdate(payload.chatId, payload.topicId, payload.muteUntil, onUpdate); - onUpdate({ + scheduleMutedTopicUpdate(payload.chatId, payload.topicId, payload.muteUntil, sendApiUpdate); + sendApiUpdate({ '@type': 'updateTopicNotifyExceptions', ...payload, }); @@ -701,7 +638,7 @@ export function updater(update: Update) { : buildApiPeerId(update.chatId, 'chat'); if (update.action instanceof GramJs.SendMessageEmojiInteraction) { - onUpdate({ + sendApiUpdate({ '@type': 'updateStartEmojiInteraction', id, emoji: update.action.emoticon, @@ -709,7 +646,7 @@ export function updater(update: Update) { interaction: buildApiEmojiInteraction(JSON.parse(update.action.interaction.data)), }); } else { - onUpdate({ + sendApiUpdate({ '@type': 'updateChatTypingStatus', id, typingStatus: buildChatTypingStatus(update), @@ -718,7 +655,7 @@ export function updater(update: Update) { } else if (update instanceof GramJs.UpdateChannelUserTyping) { const id = buildApiPeerId(update.channelId, 'channel'); - onUpdate({ + sendApiUpdate({ '@type': 'updateChatTypingStatus', id, threadId: update.topMsgId, @@ -738,13 +675,13 @@ export function updater(update: Update) { if (channel instanceof GramJs.Channel) { const chat = buildApiChatFromPreview(channel); if (chat) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: chat.id, chat, }); - onUpdate({ + sendApiUpdate({ '@type': chat.isNotJoined ? 'updateChatLeave' : 'updateChatJoin', id: buildApiPeerId(update.channelId, 'channel'), }); @@ -752,7 +689,7 @@ export function updater(update: Update) { } else if (channel instanceof GramJs.ChannelForbidden) { const chatId = buildApiPeerId(update.channelId, 'channel'); - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: chatId, chat: { @@ -760,14 +697,14 @@ export function updater(update: Update) { }, }); - onUpdate({ + sendApiUpdate({ '@type': 'updateChatLeave', id: chatId, }); } else if (_entities.length === 0) { // The link to the discussion group may have been changed. // No corresponding update available at this moment https://core.telegram.org/type/Updates - onUpdate({ + sendApiUpdate({ '@type': 'resetMessages', id: buildApiPeerId(update.channelId, 'channel'), }); @@ -776,7 +713,7 @@ export function updater(update: Update) { update instanceof GramJs.UpdateDialogUnreadMark && update.peer instanceof GramJs.DialogPeer ) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: getApiChatIdFromMtpPeer(update.peer.peer), chat: { @@ -784,7 +721,7 @@ export function updater(update: Update) { }, }); } else if (update instanceof GramJs.UpdateChatDefaultBannedRights) { - onUpdate({ + sendApiUpdate({ '@type': 'updateChat', id: getApiChatIdFromMtpPeer(update.peer), chat: { @@ -794,19 +731,19 @@ export function updater(update: Update) { // Users } else if (update instanceof GramJs.UpdateUserStatus) { - onUpdate({ + sendApiUpdate({ '@type': 'updateUserStatus', userId: buildApiPeerId(update.userId, 'user'), status: buildApiUserStatus(update.status), }); } else if (update instanceof GramJs.UpdateUser) { - onUpdate({ + sendApiUpdate({ '@type': 'updateRequestUserUpdate', id: buildApiPeerId(update.userId, 'user'), }); } else if (update instanceof GramJs.UpdateUserEmojiStatus) { const emojiStatus = buildApiEmojiStatus(update.emojiStatus); - onUpdate({ + sendApiUpdate({ '@type': 'updateUserEmojiStatus', userId: buildApiPeerId(update.userId, 'user'), emojiStatus, @@ -821,7 +758,7 @@ export function updater(update: Update) { const usernames = buildApiUsernames(update); - onUpdate({ + sendApiUpdate({ '@type': 'updateUser', id: apiUserId, user: { @@ -832,7 +769,7 @@ export function updater(update: Update) { } else if (update instanceof GramJs.UpdateUserPhone) { const { userId, phone } = update; - onUpdate({ + sendApiUpdate({ '@type': 'updateUser', id: buildApiPeerId(userId, 'user'), user: { phoneNumber: phone }, @@ -848,7 +785,7 @@ export function updater(update: Update) { _entities .filter((e) => e instanceof GramJs.User && !e.contact) .forEach((user) => { - onUpdate({ + sendApiUpdate({ '@type': 'deleteContact', id: buildApiPeerId(user.id, 'user'), }); @@ -862,7 +799,7 @@ export function updater(update: Update) { return; } - onUpdate({ + sendApiUpdate({ '@type': 'updateUser', id: user.id, user: { @@ -896,7 +833,7 @@ export function updater(update: Update) { return; } - onUpdate({ + sendApiUpdate({ '@type': 'updateNotifySettings', peerType, isSilent: Boolean(silent @@ -904,7 +841,7 @@ export function updater(update: Update) { shouldShowPreviews: Boolean(showPreviews), }); } else if (update instanceof GramJs.UpdatePeerBlocked) { - onUpdate({ + sendApiUpdate({ '@type': 'updatePeerBlocked', id: getApiChatIdFromMtpPeer(update.peerId), isBlocked: update.blocked, @@ -913,7 +850,7 @@ export function updater(update: Update) { } else if (update instanceof GramJs.UpdatePrivacy) { const key = buildPrivacyKey(update.key); if (key) { - onUpdate({ + sendApiUpdate({ '@type': 'updatePrivacy', key, rules: buildPrivacyRules(update.rules), @@ -922,35 +859,35 @@ export function updater(update: Update) { // Misc } else if (update instanceof GramJs.UpdateDraftMessage) { - onUpdate({ + sendApiUpdate({ '@type': 'draftMessage', chatId: getApiChatIdFromMtpPeer(update.peer), threadId: update.topMsgId, draft: buildMessageDraft(update.draft), }); } else if (update instanceof GramJs.UpdateContactsReset) { - onUpdate({ '@type': 'updateResetContactList' }); + sendApiUpdate({ '@type': 'updateResetContactList' }); } else if (update instanceof GramJs.UpdateFavedStickers) { - onUpdate({ '@type': 'updateFavoriteStickers' }); + sendApiUpdate({ '@type': 'updateFavoriteStickers' }); } else if (update instanceof GramJs.UpdateRecentStickers) { - onUpdate({ '@type': 'updateRecentStickers' }); + sendApiUpdate({ '@type': 'updateRecentStickers' }); } else if (update instanceof GramJs.UpdateRecentReactions) { - onUpdate({ '@type': 'updateRecentReactions' }); + sendApiUpdate({ '@type': 'updateRecentReactions' }); } else if (update instanceof GramJs.UpdateSavedReactionTags) { - onUpdate({ '@type': 'updateSavedReactionTags' }); + sendApiUpdate({ '@type': 'updateSavedReactionTags' }); } else if (update instanceof GramJs.UpdateMoveStickerSetToTop) { if (!update.masks) { - onUpdate({ + sendApiUpdate({ '@type': 'updateMoveStickerSetToTop', isCustomEmoji: update.emojis, id: update.stickerset.toString(), }); } } else if (update instanceof GramJs.UpdateStickerSets) { - onUpdate({ '@type': 'updateStickerSets' }); + sendApiUpdate({ '@type': 'updateStickerSets' }); } else if (update instanceof GramJs.UpdateStickerSetsOrder) { if (!update.masks) { - onUpdate({ + sendApiUpdate({ '@type': 'updateStickerSetsOrder', order: update.order.map((n) => n.toString()), isCustomEmoji: update.emojis, @@ -959,73 +896,45 @@ export function updater(update: Update) { } else if (update instanceof GramJs.UpdateNewStickerSet) { if (update.stickerset instanceof GramJs.messages.StickerSet) { const stickerSet = buildStickerSet(update.stickerset.set); - onUpdate({ + sendApiUpdate({ '@type': 'updateStickerSet', id: stickerSet.id, stickerSet, }); } } else if (update instanceof GramJs.UpdateSavedGifs) { - onUpdate({ '@type': 'updateSavedGifs' }); + sendApiUpdate({ '@type': 'updateSavedGifs' }); } else if (update instanceof GramJs.UpdateGroupCall) { - // eslint-disable-next-line no-underscore-dangle - const entities = update._entities; - if (entities) { - addEntitiesToLocalDb(entities); - dispatchUserAndChatUpdates(entities); - } - - onUpdate({ + sendApiUpdate({ '@type': 'updateGroupCall', call: buildApiGroupCall(update.call), }); } else if (update instanceof GramJs.UpdateGroupCallConnection) { - onUpdate({ + sendApiUpdate({ '@type': 'updateGroupCallConnection', data: JSON.parse(update.params.data) as GroupCallConnectionData, presentation: Boolean(update.presentation), }); } else if (update instanceof GramJs.UpdateGroupCallParticipants) { - // eslint-disable-next-line no-underscore-dangle - const entities = update._entities; - if (entities) { - addEntitiesToLocalDb(entities); - dispatchUserAndChatUpdates(entities); - } - - onUpdate({ + sendApiUpdate({ '@type': 'updateGroupCallParticipants', groupCallId: getGroupCallId(update.call), participants: update.participants.map(buildApiGroupCallParticipant), }); } else if (update instanceof GramJs.UpdatePendingJoinRequests) { - // eslint-disable-next-line no-underscore-dangle - const entities = update._entities; - if (entities) { - addEntitiesToLocalDb(entities); - dispatchUserAndChatUpdates(entities); - } - - onUpdate({ + sendApiUpdate({ '@type': 'updatePendingJoinRequests', chatId: getApiChatIdFromMtpPeer(update.peer), recentRequesterIds: update.recentRequesters.map((id) => buildApiPeerId(id, 'user')), requestsPending: update.requestsPending, }); } else if (update instanceof GramJs.UpdatePhoneCall) { - // eslint-disable-next-line no-underscore-dangle - const entities = update._entities; - if (entities) { - addEntitiesToLocalDb(entities); - dispatchUserAndChatUpdates(entities); - } - - onUpdate({ + sendApiUpdate({ '@type': 'updatePhoneCall', call: buildPhoneCall(update.phoneCall), }); } else if (update instanceof GramJs.UpdatePhoneCallSignalingData) { - onUpdate({ + sendApiUpdate({ '@type': 'updatePhoneCallSignalingData', callId: update.phoneCallId.toString(), data: Array.from(update.data), @@ -1033,7 +942,7 @@ export function updater(update: Update) { } else if (update instanceof GramJs.UpdateWebViewResultSent) { const { queryId } = update; - onUpdate({ + sendApiUpdate({ '@type': 'updateWebViewResultSent', queryId: queryId.toString(), }); @@ -1045,98 +954,78 @@ export function updater(update: Update) { const id = buildApiPeerId(botId, 'user'); - onUpdate({ + sendApiUpdate({ '@type': 'updateBotMenuButton', botId: id, button: buildApiBotMenuButton(button), }); } else if (update instanceof GramJs.UpdateTranscribedAudio) { - // eslint-disable-next-line no-underscore-dangle - const entities = update._entities; - if (entities) { - addEntitiesToLocalDb(entities); - dispatchUserAndChatUpdates(entities); - } - - onUpdate({ + sendApiUpdate({ '@type': 'updateTranscribedAudio', transcriptionId: update.transcriptionId.toString(), text: update.text, isPending: update.pending, }); } else if (update instanceof GramJs.UpdateConfig) { - // eslint-disable-next-line no-underscore-dangle - const entities = update._entities; - if (entities) { - addEntitiesToLocalDb(entities); - dispatchUserAndChatUpdates(entities); - } - onUpdate({ '@type': 'updateConfig' }); + sendApiUpdate({ '@type': 'updateConfig' }); } else if (update instanceof GramJs.UpdateChannelPinnedTopic) { - onUpdate({ + sendApiUpdate({ '@type': 'updatePinnedTopic', chatId: buildApiPeerId(update.channelId, 'channel'), topicId: update.topicId, isPinned: Boolean(update.pinned), }); } else if (update instanceof GramJs.UpdateChannelPinnedTopics) { - onUpdate({ + sendApiUpdate({ '@type': 'updatePinnedTopicsOrder', chatId: buildApiPeerId(update.channelId, 'channel'), order: update.order || [], }); } else if (update instanceof GramJs.UpdateRecentEmojiStatuses) { - onUpdate({ '@type': 'updateRecentEmojiStatuses' }); + sendApiUpdate({ '@type': 'updateRecentEmojiStatuses' }); } else if (update instanceof GramJs.UpdateStory) { - // eslint-disable-next-line no-underscore-dangle - const entities = update._entities; - if (entities) { - addEntitiesToLocalDb(entities); - dispatchUserAndChatUpdates(entities); - } - const { story } = update; const peerId = getApiChatIdFromMtpPeer(update.peer); const apiStory = buildApiStory(peerId, story) as ApiStory | ApiStorySkipped; addStoryToLocalDb(story, peerId); // Add after building to prevent repair info overwrite if (story instanceof GramJs.StoryItemDeleted) { - onUpdate({ + sendApiUpdate({ '@type': 'deleteStory', peerId, storyId: story.id, }); } else { - onUpdate({ + sendApiUpdate({ '@type': 'updateStory', peerId, story: apiStory, }); } } else if (update instanceof GramJs.UpdateReadStories) { - onUpdate({ + sendApiUpdate({ '@type': 'updateReadStories', peerId: getApiChatIdFromMtpPeer(update.peer), lastReadId: update.maxId, }); } else if (update instanceof GramJs.UpdateSentStoryReaction) { - onUpdate({ + sendApiUpdate({ '@type': 'updateSentStoryReaction', peerId: getApiChatIdFromMtpPeer(update.peer), storyId: update.storyId, reaction: buildApiReaction(update.reaction), }); } else if (update instanceof GramJs.UpdateStoriesStealthMode) { - onUpdate({ + sendApiUpdate({ '@type': 'updateStealthMode', stealthMode: buildApiStealthMode(update.stealthMode), }); } else if (update instanceof GramJs.UpdateAttachMenuBots) { - onUpdate({ + sendApiUpdate({ '@type': 'updateAttachMenuBots', }); } else if (update instanceof GramJs.UpdateNewAuthorization) { - onUpdate({ + sendApiUpdate({ '@type': 'updateNewAuthorization', hash: update.hash.toString(), date: update.date, @@ -1145,18 +1034,18 @@ export function updater(update: Update) { isUnconfirmed: update.unconfirmed, }); } else if (update instanceof GramJs.UpdateChannelViewForumAsMessages) { - onUpdate({ + sendApiUpdate({ '@type': 'updateViewForumAsMessages', chatId: buildApiPeerId(update.channelId, 'channel'), isEnabled: update.enabled ? true : undefined, }); } else if (update instanceof GramJs.UpdateStarsBalance) { - onUpdate({ + sendApiUpdate({ '@type': 'updateStarsBalance', balance: update.balance.toJSNumber(), }); } else if (update instanceof LocalUpdatePremiumFloodWait) { - onUpdate({ + sendApiUpdate({ '@type': 'updatePremiumFloodWait', isUpload: update.isUpload, }); diff --git a/src/api/gramjs/updates/updateManager.ts b/src/api/gramjs/updates/updateManager.ts index 8589932e1..2b4478b8d 100644 --- a/src/api/gramjs/updates/updateManager.ts +++ b/src/api/gramjs/updates/updateManager.ts @@ -3,15 +3,15 @@ import { UpdateConnectionState, UpdateServerTimeOffset } from '../../../lib/gram import type { ApiChat } from '../../types'; import type { invokeRequest } from '../methods/client'; -import type { Update } from './updater'; import { DEBUG } from '../../../config'; import SortedQueue from '../../../util/SortedQueue'; import { buildApiPeerId } from '../apiBuilders/peers'; import { buildInputEntity, buildMtpPeerId } from '../gramjsBuilders'; -import { addEntitiesToLocalDb } from '../helpers'; import localDb from '../localDb'; -import { dispatchUserAndChatUpdates, sendUpdate, updater } from './updater'; +import { sendApiUpdate } from './apiUpdateEmitter'; +import { processAndUpdateEntities } from './entityProcessor'; +import { type Update, updater } from './mtpUpdateHandler'; import { buildLocalUpdatePts, type UpdatePts } from './UpdatePts'; @@ -139,6 +139,7 @@ function applyUpdate(updateObject: SeqUpdate | PtsUpdate) { } if (updateObject instanceof GramJs.UpdatesCombined || updateObject instanceof GramJs.Updates) { + processAndUpdateEntities(updateObject); const entities = updateObject.users.concat(updateObject.chats); updateObject.updates.forEach((update) => { @@ -277,7 +278,7 @@ export async function getDifference() { return; } - sendUpdate({ + sendApiUpdate({ '@type': 'updateFetchingDifference', isFetching: true, }); @@ -296,7 +297,7 @@ export async function getDifference() { if (response instanceof GramJs.updates.DifferenceEmpty) { localDb.commonBoxState.seq = response.seq; localDb.commonBoxState.date = response.date; - sendUpdate({ + sendApiUpdate({ '@type': 'updateFetchingDifference', isFetching: false, }); @@ -313,7 +314,7 @@ export async function getDifference() { return; } - sendUpdate({ + sendApiUpdate({ '@type': 'updateFetchingDifference', isFetching: false, }); @@ -366,7 +367,7 @@ async function getChannelDifference(channelId: string) { function forceSync() { reset(); - sendUpdate({ + sendApiUpdate({ '@type': 'requestSync', }); @@ -425,11 +426,7 @@ function processDifference( })); }); - addEntitiesToLocalDb(difference.users); - addEntitiesToLocalDb(difference.chats); - - dispatchUserAndChatUpdates(difference.users); - dispatchUserAndChatUpdates(difference.chats); + processAndUpdateEntities(difference); // Ignore `pts`/`seq` holes when applying updates from difference // BUT, if we got an `UpdateChannelTooLong`, make sure to process other updates after receiving `ChannelDifference` diff --git a/src/api/gramjs/worker/types.ts b/src/api/gramjs/worker/types.ts index ed784cd6c..1f4c03bcc 100644 --- a/src/api/gramjs/worker/types.ts +++ b/src/api/gramjs/worker/types.ts @@ -1,5 +1,7 @@ import type { DebugLevel } from '../../../util/debugConsole'; -import type { ApiInitialArgs, ApiUpdate } from '../../types'; +import type { + ApiInitialArgs, ApiUpdate, +} from '../../types'; import type { LocalDb } from '../localDb'; import type { MethodArgs, MethodResponse, Methods } from '../methods/types'; diff --git a/src/api/gramjs/worker/worker.ts b/src/api/gramjs/worker/worker.ts index bf92afb23..533274d88 100644 --- a/src/api/gramjs/worker/worker.ts +++ b/src/api/gramjs/worker/worker.ts @@ -41,6 +41,7 @@ handleErrors(); let pendingPayloads: WorkerPayload[] = []; let pendingTransferables: Transferable[] = []; +let pendingUpdates: ApiUpdate[] = []; const callbackState = new Map(); @@ -158,31 +159,18 @@ function handleErrors() { }); } -let pendingUpdates: ApiUpdate[] = []; - -const sendUpdatesOnTickEnd = throttleWithTickEnd(() => { - const currentUpdates = pendingUpdates; - pendingUpdates = []; - - sendToOrigin({ - type: 'updates', - updates: currentUpdates, - }); -}); - -function onUpdate(update: ApiUpdate) { - if (DEBUG && update['@type'] !== 'updateUserStatus' && update['@type'] !== 'updateServerTimeOffset') { - log('UPDATE', update['@type'], update); +const sendToOriginOnTickEnd = throttleWithTickEnd(() => { + if (pendingUpdates.length) { + pendingPayloads.unshift({ + type: 'updates', + updates: pendingUpdates, + }); } - pendingUpdates.push(update); - sendUpdatesOnTickEnd(); -} - -const sendToOriginOnTickEnd = throttleWithTickEnd(() => { const data = { payloads: pendingPayloads }; const transferables = pendingTransferables; + pendingUpdates = []; pendingPayloads = []; pendingTransferables = []; @@ -202,3 +190,12 @@ function sendToOrigin(payload: WorkerPayload, transferable?: Transferable) { sendToOriginOnTickEnd(); } + +function onUpdate(update: ApiUpdate) { + if (DEBUG && update['@type'] !== 'updateUserStatus' && update['@type'] !== 'updateServerTimeOffset') { + log('UPDATE', update['@type'], update); + } + + pendingUpdates.push(update); + sendToOriginOnTickEnd(); +} diff --git a/src/api/types/messages.ts b/src/api/types/messages.ts index fd18cee29..d0c605d18 100644 --- a/src/api/types/messages.ts +++ b/src/api/types/messages.ts @@ -745,7 +745,7 @@ interface ApiBaseThreadInfo { export interface ApiCommentsInfo extends ApiBaseThreadInfo { isCommentsInfo: true; - threadId?: ThreadId; + threadId?: never; originChannelId: string; originMessageId: number; } diff --git a/src/api/types/updates.ts b/src/api/types/updates.ts index b94385424..51bd25f96 100644 --- a/src/api/types/updates.ts +++ b/src/api/types/updates.ts @@ -264,9 +264,9 @@ export type ApiUpdatePinnedMessageIds = { messageIds: number[]; }; -export type ApiUpdateThreadInfos = { - '@type': 'updateThreadInfos'; - threadInfoUpdates: Partial[]; +export type ApiUpdateThreadInfo = { + '@type': 'updateThreadInfo'; + threadInfo: Partial; }; export type ApiUpdateScheduledMessageSendSucceeded = { @@ -753,13 +753,20 @@ export type ApiUpdateNewProfilePhoto = { photo: ApiPhoto; }; +export type ApiUpdateEntities = { + '@type': 'updateEntities'; + users?: Record; + chats?: Record; + threadInfos?: ApiThreadInfo[]; +}; + export type ApiUpdate = ( ApiUpdateReady | ApiUpdateSession | ApiUpdateWebAuthTokenFailed | ApiUpdateRequestUserUpdate | ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser | ApiUpdateChat | ApiUpdateChatInbox | ApiUpdateChatTypingStatus | ApiUpdateChatFullInfo | ApiUpdatePinnedChatIds | ApiUpdateChatMembers | ApiUpdateChatJoin | ApiUpdateChatLeave | ApiUpdateChatPinned | ApiUpdatePinnedMessageIds | ApiUpdateChatListType | ApiUpdateChatFolder | ApiUpdateChatFoldersOrder | ApiUpdateRecommendedChatFolders | - ApiUpdateNewMessage | ApiUpdateMessage | ApiUpdateThreadInfos | ApiUpdateCommonBoxMessages | + ApiUpdateNewMessage | ApiUpdateMessage | ApiUpdateThreadInfo | ApiUpdateCommonBoxMessages | ApiUpdateDeleteMessages | ApiUpdateMessagePoll | ApiUpdateMessagePollVote | ApiUpdateDeleteHistory | ApiUpdateMessageSendSucceeded | ApiUpdateMessageSendFailed | ApiUpdateServiceNotification | ApiDeleteContact | ApiUpdateUser | ApiUpdateUserStatus | ApiUpdateUserFullInfo | @@ -785,7 +792,7 @@ export type ApiUpdate = ( ApiUpdateViewForumAsMessages | ApiUpdateSavedDialogPinned | ApiUpdatePinnedSavedDialogIds | ApiUpdateChatLastMessage | ApiUpdateDeleteSavedHistory | ApiUpdatePremiumFloodWait | ApiUpdateStarsBalance | ApiUpdateQuickReplyMessage | ApiUpdateQuickReplies | ApiDeleteQuickReply | ApiUpdateDeleteQuickReplyMessages | - ApiUpdateDeleteProfilePhoto | ApiUpdateNewProfilePhoto + ApiUpdateDeleteProfilePhoto | ApiUpdateNewProfilePhoto | ApiUpdateEntities ); export type OnApiUpdate = (update: ApiUpdate) => void; diff --git a/src/components/common/profile/ChatExtra.tsx b/src/components/common/profile/ChatExtra.tsx index dbff7483d..8dbd63763 100644 --- a/src/components/common/profile/ChatExtra.tsx +++ b/src/components/common/profile/ChatExtra.tsx @@ -450,7 +450,7 @@ export default memo(withGlobal( const isForum = chat?.isForum; const isMuted = chat && selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global)); const { threadId } = selectCurrentMessageList(global) || {}; - const topicId = isForum ? Number(threadId) : undefined; + const topicId = isForum && threadId ? Number(threadId) : undefined; const chatFullInfo = chat && selectChatFullInfo(global, chat.id); const userFullInfo = user && selectUserFullInfo(global, user.id); diff --git a/src/config.ts b/src/config.ts index 532c6e0a3..1b7b4f3b4 100644 --- a/src/config.ts +++ b/src/config.ts @@ -178,7 +178,6 @@ export const FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE = 300; // px export const API_UPDATE_THROTTLE = Math.round((FAST_SMOOTH_MIN_DURATION + FAST_SMOOTH_MAX_DURATION) / 2); export const API_THROTTLE_RESET_UPDATES = new Set([ 'newMessage', 'newScheduledMessage', 'deleteMessages', 'deleteScheduledMessages', 'deleteHistory', - 'updateThreadInfos', ]); export const LOCK_SCREEN_ANIMATION_DURATION_MS = 200; @@ -287,6 +286,7 @@ export const TME_LINK_PREFIX = 'https://t.me/'; export const BOT_FATHER_USERNAME = 'botfather'; export const USERNAME_PURCHASE_ERROR = 'USERNAME_PURCHASE_AVAILABLE'; export const PURCHASE_USERNAME = 'auction'; +export const ACCEPTABLE_USERNAME_ERRORS = new Set([USERNAME_PURCHASE_ERROR, 'USERNAME_INVALID']); export const TME_WEB_DOMAINS = new Set(['t.me', 'web.t.me', 'a.t.me', 'k.t.me', 'z.t.me']); export const WEB_APP_PLATFORM = 'weba'; diff --git a/src/global/actions/api/accounts.ts b/src/global/actions/api/accounts.ts index d5c61187d..0b8a1d1ac 100644 --- a/src/global/actions/api/accounts.ts +++ b/src/global/actions/api/accounts.ts @@ -1,9 +1,7 @@ import { getCurrentTabId } from '../../../util/establishMultitabRole'; -import { buildCollectionByKey } from '../../../util/iteratees'; import { oldTranslate } from '../../../util/oldLangProvider'; import { callApi } from '../../../api/gramjs'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; -import { addUsers } from '../../reducers'; import { selectChat } from '../../selectors'; addActionHandler('reportPeer', async (global, actions, payload): Promise => { @@ -193,11 +191,9 @@ addActionHandler('loadWebAuthorizations', async (global): Promise => { if (!result) { return; } - const { users, webAuthorizations } = result; + const { webAuthorizations } = result; global = getGlobal(); - global = addUsers(global, buildCollectionByKey(users, 'id')); - global = { ...global, activeWebSessions: { diff --git a/src/global/actions/api/bots.ts b/src/global/actions/api/bots.ts index 99c672768..e7fa75714 100644 --- a/src/global/actions/api/bots.ts +++ b/src/global/actions/api/bots.ts @@ -9,7 +9,6 @@ import { ManagementProgress } from '../../../types'; import { BOT_FATHER_USERNAME, GENERAL_REFETCH_INTERVAL } from '../../../config'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; -import { buildCollectionByKey } from '../../../util/iteratees'; import { oldTranslate } from '../../../util/oldLangProvider'; import PopupManager from '../../../util/PopupManager'; import requestActionTimeout from '../../../util/requestActionTimeout'; @@ -21,7 +20,7 @@ import { addActionHandler, getGlobal, setGlobal, } from '../../index'; import { - addChats, addUsers, removeBlockedUser, updateManagementProgress, updateUser, updateUserFullInfo, + removeBlockedUser, updateManagementProgress, updateUser, updateUserFullInfo, } from '../../reducers'; import { replaceInlineBotSettings, replaceInlineBotsIsLoading } from '../../reducers/bots'; import { updateTabState } from '../../reducers/tabs'; @@ -224,10 +223,9 @@ addActionHandler('loadTopInlineBots', async (global): Promise => { return; } - const { ids, users } = result; + const { ids } = result; global = getGlobal(); - global = addUsers(global, buildCollectionByKey(users, 'id')); global = { ...global, topInlineBots: { @@ -250,10 +248,9 @@ addActionHandler('loadTopBotApps', async (global): Promise => { return; } - const { ids, users } = result; + const { ids } = result; global = getGlobal(); - global = addUsers(global, buildCollectionByKey(users, 'id')); global = { ...global, topBotApps: { @@ -285,8 +282,6 @@ addActionHandler('queryInlineBot', async (global, actions, payload): Promise(global: T, hash?: string) { } global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); global = { ...global, attachMenu: { diff --git a/src/global/actions/api/calls.async.ts b/src/global/actions/api/calls.async.ts index 3a40cb72c..58613432c 100644 --- a/src/global/actions/api/calls.async.ts +++ b/src/global/actions/api/calls.async.ts @@ -10,10 +10,8 @@ import { toggleStream, } from '../../../lib/secret-sauce'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; -import { buildCollectionByKey } from '../../../util/iteratees'; import { callApi } from '../../../api/gramjs'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; -import { addUsers } from '../../reducers'; import { removeGroupCall, updateActiveGroupCall, @@ -263,11 +261,7 @@ addActionHandler('connectToActivePhoneCall', async (global, actions): Promise => { @@ -281,13 +275,7 @@ addActionHandler('acceptCall', async (global): Promise => { await callApi('createPhoneCallState', [false]); const gB = await callApi('acceptPhoneCall', [dhConfig])!; - const result = await callApi('acceptCall', { call: phoneCall, gB }); - if (!result) { - return; - } - global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - setGlobal(global); + await callApi('acceptCall', { call: phoneCall, gB }); }); addActionHandler('sendSignalingData', (global, actions, payload): ActionReturnType => { diff --git a/src/global/actions/api/chats.ts b/src/global/actions/api/chats.ts index 470dec615..944482642 100644 --- a/src/global/actions/api/chats.ts +++ b/src/global/actions/api/chats.ts @@ -1,6 +1,6 @@ import type { ApiChat, ApiChatFolder, ApiChatlistExportedInvite, - ApiChatMember, ApiError, ApiMissingInvitedUser, ApiUser, + ApiChatMember, ApiError, ApiMissingInvitedUser, } from '../../../api/types'; import type { RequiredGlobalActions } from '../../index'; import type { @@ -21,7 +21,6 @@ import { ARCHIVED_FOLDER_ID, CHAT_LIST_LOAD_SLICE, DEBUG, - GLOBAL_STATE_CACHE_ARCHIVED_CHAT_LIST_LIMIT, GLOBAL_SUGGESTED_CHANNELS_ID, RE_TG_LINK, SAVED_FOLDER_ID, @@ -57,10 +56,8 @@ import { } from '../../index'; import { addChatMembers, - addChats, addMessages, addSimilarChannels, - addUsers, addUserStatuses, deleteChatMessages, deletePeerPhoto, @@ -70,9 +67,7 @@ import { replaceChatFullInfo, replaceChatListIds, replaceChatListLoadingParameters, - replaceChats, replaceThreadParam, - replaceUsers, replaceUserStatuses, toggleSimilarChannels, updateChat, @@ -91,6 +86,7 @@ import { updateTopic, updateTopics, updateUser, + updateUsers, } from '../../reducers'; import { updateGroupCall } from '../../reducers/calls'; import { updateTabState } from '../../reducers/tabs'; @@ -118,7 +114,6 @@ import { selectThreadInfo, selectUser, selectUserByPhoneNumber, - selectVisibleUsers, } from '../../selectors'; import { selectGroupCall } from '../../selectors/calls'; import { selectCurrentLimit } from '../../selectors/limits'; @@ -126,13 +121,6 @@ import { selectCurrentLimit } from '../../selectors/limits'; const TOP_CHAT_MESSAGES_PRELOAD_INTERVAL = 100; const INFINITE_LOOP_MARKER = 100; -const SERVICE_NOTIFICATIONS_USER_MOCK: ApiUser = { - id: SERVICE_NOTIFICATIONS_USER_ID, - accessHash: '0', - type: 'userTypeRegular', - isMin: true, - phoneNumber: '', -}; const CHATLIST_LIMIT_ERROR_LIST = new Set([ 'FILTERS_TOO_MUCH', 'CHATLISTS_TOO_MUCH', @@ -404,8 +392,6 @@ addActionHandler('openThread', async (global, actions, payload): Promise = } global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); global = addMessages(global, result.messages); if (isComments) { global = updateThreadInfo(global, loadingChatId, loadingThreadId, { @@ -509,12 +495,12 @@ addActionHandler('openSupportChat', async (global, actions, payload): Promise => { + const { onFirstBatchDone } = payload; const listType = payload.listType; - const { onReplace } = payload; - let { shouldReplace } = payload; + let isCallbackFired = false; let i = 0; - while (shouldReplace || !global.chats.isFullyLoaded[listType]) { + while (!global.chats.isFullyLoaded[listType]) { if (i++ >= INFINITE_LOOP_MARKER) { if (DEBUG) { // eslint-disable-next-line no-console @@ -532,13 +518,12 @@ addActionHandler('loadAllChats', async (global, actions, payload): Promise await loadChats( listType, - shouldReplace, true, ); - if (shouldReplace) { - onReplace?.(); - shouldReplace = false; + if (!isCallbackFired) { + onFirstBatchDone?.(); + isCallbackFired = true; } global = getGlobal(); @@ -608,8 +593,6 @@ addActionHandler('requestSavedDialogUpdate', async (global, actions, payload): P global = getGlobal(); global = addMessages(global, result.messages); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); if (result.messages.length) { global = updateChatLastMessageId(global, chatId, result.messages[0].id, 'saved'); @@ -1792,7 +1775,6 @@ addActionHandler('loadGroupsForDiscussion', async (global): Promise => { }, {} as Record); global = getGlobal(); - global = addChats(global, addedById); global = { ...global, chats: { @@ -1900,13 +1882,12 @@ addActionHandler('loadMoreMembers', async (global, actions, payload): Promise = if (!result) return; global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); global = addMessages(global, result.messages); global = updateTopics(global, chatId, result.count, result.topics); global = updateListedTopicIds(global, chatId, result.topics.map((topic) => topic.id)); @@ -2149,8 +2127,6 @@ addActionHandler('loadTopicById', async (global, actions, payload): Promise selectChat(global, chatId)) - .filter(Boolean); - const visibleChats = tabStates.flatMap(({ id: tabId }) => { - const currentChat = selectCurrentChat(global, tabId); - return currentChat ? [currentChat] : []; - }); - const chatsToSave = visibleChats.concat(topArchivedChats || []); - - const visibleUsers = tabStates.flatMap(({ id: tabId }) => { - return selectVisibleUsers(global, tabId) || []; - }); - - if (global.currentUserId && global.users.byId[global.currentUserId]) { - visibleUsers.push(global.users.byId[global.currentUserId]); - } - - global = replaceUsers(global, buildCollectionByKey(visibleUsers.concat(result.users), 'id')); - global = replaceUserStatuses(global, result.userStatusesById); - global = replaceChats(global, buildCollectionByKey(chatsToSave.concat(result.chats), 'id')); - global = replaceChatListIds(global, listType, chatIds); - } else { - // Archived and Saved - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addUserStatuses(global, result.userStatusesById); - global = updateChats(global, buildCollectionByKey(result.chats, 'id')); - global = replaceChatListIds(global, listType, chatIds); - } + global = updateUsers(global, buildCollectionByKey(result.users, 'id')); + global = updateChats(global, newChats); + if (isFirstBatch) { + global = replaceChatListIds(global, listType, chatIds); + global = replaceUserStatuses(global, result.userStatusesById); } else { - const newChats = buildCollectionByKey(result.chats, 'id'); - - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addUserStatuses(global, result.userStatusesById); - global = updateChats(global, newChats); global = updateChatListIds(global, listType, chatIds); + global = addUserStatuses(global, result.userStatusesById); } global = updateChatListSecondaryInfo(global, listType, result); @@ -2860,11 +2777,10 @@ export async function loadFullChat( } const { - chats, users, userStatusesById, fullInfo, groupCall, membersCount, isForumAsMessages, + chats, userStatusesById, fullInfo, groupCall, membersCount, isForumAsMessages, } = result; global = getGlobal(); - global = addUsers(global, buildCollectionByKey(users, 'id')); global = updateChats(global, buildCollectionByKey(chats, 'id')); if (userStatusesById) { @@ -3024,8 +2940,6 @@ async function getAttachBotOrNotify( return undefined; } - - global = addUsers(global, buildCollectionByKey(result.users, 'id')); setGlobal(global); return result.bot; diff --git a/src/global/actions/api/globalSearch.ts b/src/global/actions/api/globalSearch.ts index cb508b814..dd42ad896 100644 --- a/src/global/actions/api/globalSearch.ts +++ b/src/global/actions/api/globalSearch.ts @@ -1,5 +1,5 @@ import type { - ApiChat, ApiGlobalMessageSearchType, ApiMessage, ApiTopic, ApiUser, + ApiChat, ApiGlobalMessageSearchType, ApiMessage, ApiTopic, ApiUserStatus, } from '../../../api/types'; import type { ActionReturnType, GlobalState, TabArgs } from '../../types'; @@ -8,15 +8,12 @@ import { GLOBAL_SEARCH_SLICE, GLOBAL_TOPIC_SEARCH_SLICE } from '../../../config' import { timestampPlusDay } from '../../../util/dates/dateFormat'; import { isDeepLink, tryParseDeepLink } from '../../../util/deepLinkParser'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; -import { buildCollectionByKey } from '../../../util/iteratees'; import { throttle } from '../../../util/schedulers'; import { callApi } from '../../../api/gramjs'; import { isChatChannel, isChatGroup, toChannelId } from '../../helpers/chats'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { - addChats, addMessages, - addUsers, addUserStatuses, updateGlobalSearch, updateGlobalSearchFetchingStatus, @@ -46,13 +43,9 @@ addActionHandler('setGlobalSearchQuery', (global, actions, payload): ActionRetur } const { - accountResultIds, globalResultIds, users, chats, + accountResultIds, globalResultIds, } = result; - global = addChats(global, buildCollectionByKey(chats, 'id')); - - global = addUsers(global, buildCollectionByKey(users, 'id')); - global = updateGlobalSearchFetchingStatus(global, { chats: false }, tabId); global = updateGlobalSearch(global, { localResults: { @@ -137,12 +130,9 @@ addActionHandler('searchPopularBotApps', async (global, actions, payload): Promi return; } - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); - const peerIds = result.users.map(({ id }) => id); global = updateGlobalSearch(global, { popularBotApps: { - peerIds: [...(popularBotApps?.peerIds || []), ...peerIds], + peerIds: [...(popularBotApps?.peerIds || []), ...result.peerIds], nextOffset: result.nextOffset, }, }, tabId); @@ -167,9 +157,7 @@ async function searchMessagesGlobal(global: T, params: { } = params; let result: { messages: ApiMessage[]; - users: ApiUser[]; userStatusesById?: Record; - chats: ApiChat[]; topics?: ApiTopic[]; totalTopicsCount?: number; totalCount: number; @@ -200,7 +188,7 @@ async function searchMessagesGlobal(global: T, params: { if (inChatResult) { const { - messages, users, totalCount, nextOffsetId, + messages, totalCount, nextOffsetId, } = inChatResult; const { topics: localTopics, count } = topics || {}; @@ -209,8 +197,6 @@ async function searchMessagesGlobal(global: T, params: { topics: localTopics, totalTopicsCount: count, messages, - users, - chats: [], totalCount, nextOffsetId, }; @@ -249,17 +235,9 @@ async function searchMessagesGlobal(global: T, params: { } const { - messages, users, chats, userStatusesById, totalCount, nextOffsetRate, nextOffsetId, nextOffsetPeerId, + messages, userStatusesById, totalCount, nextOffsetRate, nextOffsetId, nextOffsetPeerId, } = result; - if (chats.length) { - global = addChats(global, buildCollectionByKey(chats, 'id')); - } - - if (users.length) { - global = addUsers(global, buildCollectionByKey(users, 'id')); - } - if (userStatusesById) { global = addUserStatuses(global, userStatusesById); } diff --git a/src/global/actions/api/initial.ts b/src/global/actions/api/initial.ts index a674ca1d8..927c0b871 100644 --- a/src/global/actions/api/initial.ts +++ b/src/global/actions/api/initial.ts @@ -13,7 +13,6 @@ import { updateAppBadge } from '../../../util/appBadge'; import { MAIN_IDB_STORE, PASSCODE_IDB_STORE } from '../../../util/browser/idb'; import * as cacheApi from '../../../util/cacheApi'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; -import { buildCollectionByKey } from '../../../util/iteratees'; import { unsubscribe } from '../../../util/notifications'; import { clearEncryptedSession, encryptSession, forgetPasscode } from '../../../util/passcode'; import { parseInitialLocationHash, resetInitialLocationHash, resetLocationHash } from '../../../util/routing'; @@ -35,7 +34,7 @@ import { addActionHandler, getGlobal, setGlobal, } from '../../index'; import { - addUsers, clearGlobalForLockScreen, updateManagementProgress, updatePasscodeSettings, + clearGlobalForLockScreen, updateManagementProgress, updatePasscodeSettings, } from '../../reducers'; addActionHandler('initApi', (global, actions): ActionReturnType => { @@ -109,7 +108,6 @@ addActionHandler('uploadProfilePhoto', async (global, actions, payload): Promise if (!result) return; global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); global = updateManagementProgress(global, ManagementProgress.Complete, tabId); setGlobal(global); diff --git a/src/global/actions/api/management.ts b/src/global/actions/api/management.ts index 5d59d53bc..255e22857 100644 --- a/src/global/actions/api/management.ts +++ b/src/global/actions/api/management.ts @@ -2,13 +2,12 @@ import type { ActionReturnType } from '../../types'; import { ManagementProgress } from '../../../types'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; -import { buildCollectionByKey } from '../../../util/iteratees'; import * as langProvider from '../../../util/oldLangProvider'; import { callApi } from '../../../api/gramjs'; import { getUserFirstOrLastName } from '../../helpers'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { - addUsers, updateChat, updateChatFullInfo, updateManagement, updateManagementProgress, + updateChat, updateChatFullInfo, updateManagement, updateManagementProgress, } from '../../reducers'; import { selectChat, selectCurrentMessageList, selectTabState, selectUser, @@ -124,9 +123,7 @@ addActionHandler('loadExportedChatInvites', async (global, actions, payload): Pr return; } global = getGlobal(); - const { invites, users } = result; - - global = addUsers(global, buildCollectionByKey(users, 'id')); + const { invites } = result; const update = isRevoked ? { revokedInvites: invites } : { invites }; global = updateManagement(global, chatId, update, tabId); @@ -153,7 +150,7 @@ addActionHandler('editExportedChatInvite', async (global, actions, payload): Pro return; } - const { oldInvite, newInvite, users } = result; + const { oldInvite, newInvite } = result; global = getGlobal(); const { management } = selectTabState(global, tabId); @@ -167,8 +164,6 @@ addActionHandler('editExportedChatInvite', async (global, actions, payload): Pro invites.push(newInvite); } - global = addUsers(global, buildCollectionByKey(users, 'id')); - global = updateManagement(global, chatId, { invites, revokedInvites, @@ -269,7 +264,7 @@ addActionHandler('loadChatInviteImporters', async ( if (!result) { return; } - const { importers, users } = result; + const { importers } = result; global = getGlobal(); const currentInviteInfo = selectTabState(global, tabId).management.byChatId[chatId]?.inviteInfo; @@ -283,7 +278,6 @@ addActionHandler('loadChatInviteImporters', async ( importers, }, }, tabId); - global = addUsers(global, users); setGlobal(global); }); @@ -308,7 +302,7 @@ addActionHandler('loadChatInviteRequesters', async ( if (!result) { return; } - const { importers, users } = result; + const { importers } = result; global = getGlobal(); const currentInviteInfo = selectTabState(global, tabId).management.byChatId[chatId]?.inviteInfo; @@ -321,7 +315,6 @@ addActionHandler('loadChatInviteRequesters', async ( requesters: importers, }, }, tabId); - global = addUsers(global, users); setGlobal(global); }); @@ -343,11 +336,10 @@ addActionHandler('loadChatJoinRequests', async (global, actions, payload): Promi if (!result) { return; } - const { importers, users } = result; + const { importers } = result; global = getGlobal(); global = updateChat(global, chatId, { joinRequests: importers }); - global = addUsers(global, users); setGlobal(global); }); @@ -443,7 +435,6 @@ addActionHandler('uploadContactProfilePhoto', async (global, actions, payload): } global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); setGlobal(global); const { id, accessHash } = user; diff --git a/src/global/actions/api/messages.ts b/src/global/actions/api/messages.ts index bc1513275..94537592b 100644 --- a/src/global/actions/api/messages.ts +++ b/src/global/actions/api/messages.ts @@ -66,9 +66,7 @@ import { } from '../../index'; import { addChatMessagesById, - addChats, addUnreadMentions, - addUsers, deleteSponsoredMessage, removeOutlyingList, removeRequestedMessageTranslation, @@ -81,7 +79,6 @@ import { updateChat, updateChatFullInfo, updateChatMessage, - updateChats, updateListedIds, updateMessageTranslation, updateOutlyingLists, @@ -95,7 +92,6 @@ import { updateTopic, updateUploadByMessageKey, updateUserFullInfo, - updateUsers, } from '../../reducers'; import { updateTabState } from '../../reducers/tabs'; import { @@ -1014,9 +1010,6 @@ addActionHandler('loadPollOptionResults', async (global, actions, payload): Prom global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); - const tabState = selectTabState(global, tabId); const { pollResults } = tabState; const { voters } = tabState.pollResults; @@ -1301,7 +1294,7 @@ async function loadViewportMessages( } const { - messages, users, chats, count, + messages, count, } = result; global = getGlobal(); @@ -1325,9 +1318,6 @@ async function loadViewportMessages( ? updateOutlyingLists(global, chatId, threadId, ids) : updateListedIds(global, chatId, threadId, ids); - global = addUsers(global, buildCollectionByKey(users, 'id')); - global = addChats(global, buildCollectionByKey(chats, 'id')); - let listedIds = selectListedIds(global, chatId, threadId); const outlyingList = offsetId ? selectOutlyingListByMessageId(global, chatId, threadId, offsetId) : undefined; @@ -1382,7 +1372,6 @@ async function loadMessage( global = getGlobal(); global = updateChatMessage(global, chat.id, messageId, result.message); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); setGlobal(global); return result.message; @@ -1498,7 +1487,7 @@ addActionHandler('loadPinnedMessages', async (global, actions, payload): Promise return; } - const { messages, chats, users } = result; + const { messages } = result; const byId = buildCollectionByKey(messages, 'id'); const ids = Object.keys(byId).map(Number).sort((a, b) => b - a); @@ -1506,8 +1495,6 @@ addActionHandler('loadPinnedMessages', async (global, actions, payload): Promise global = getGlobal(); global = addChatMessagesById(global, chat.id, byId); global = safeReplacePinnedIds(global, chat.id, threadId, ids); - global = addUsers(global, buildCollectionByKey(users, 'id')); - global = addChats(global, buildCollectionByKey(chats, 'id')); setGlobal(global); }); @@ -1562,8 +1549,6 @@ addActionHandler('loadSendAs', async (global, actions, payload): Promise = } global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); global = updateChat(global, chatId, { sendAsPeerIds: result.sendAs }); setGlobal(global); }); @@ -1582,8 +1567,6 @@ addActionHandler('loadSponsoredMessages', async (global, actions, payload): Prom global = getGlobal(); global = updateSponsoredMessage(global, chatId, result.messages[0]); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); setGlobal(global); }); @@ -1695,15 +1678,13 @@ async function fetchUnreadMentions(global: T, chatId: str if (!result) return; - const { messages, chats, users } = result; + const { messages } = result; const byId = buildCollectionByKey(messages, 'id'); const ids = Object.keys(byId).map(Number); global = getGlobal(); global = addChatMessagesById(global, chat.id, byId); - global = addUsers(global, buildCollectionByKey(users, 'id')); - global = addChats(global, buildCollectionByKey(chats, 'id')); global = addUnreadMentions(global, chatId, chat, ids); setGlobal(global); @@ -2073,8 +2054,6 @@ addActionHandler('loadMessageViews', async (global, actions, payload): Promise { global = updateChatMessage(global, chatId, update.id, { viewsCount: update.views, @@ -2155,8 +2134,6 @@ addActionHandler('loadQuickReplies', async (global): Promise => { if (!result) return; global = getGlobal(); - global = updateUsers(global, buildCollectionByKey(result.users, 'id')); - global = updateChats(global, buildCollectionByKey(result.chats, 'id')); global = updateQuickReplyMessages(global, buildCollectionByKey(result.messages, 'id')); global = updateQuickReplies(global, result.quickReplies); diff --git a/src/global/actions/api/middleSearch.ts b/src/global/actions/api/middleSearch.ts index 44d86e56e..5ede615cd 100644 --- a/src/global/actions/api/middleSearch.ts +++ b/src/global/actions/api/middleSearch.ts @@ -18,9 +18,7 @@ import { } from '../../index'; import { addChatMessagesById, - addChats, addMessages, - addUsers, addUserStatuses, initializeChatMediaSearchResults, mergeWithChatMediaSearchSegment, @@ -126,7 +124,7 @@ addActionHandler('performMiddleSearch', async (global, actions, payload): Promis } const { - chats, users, userStatusesById, messages, totalCount, nextOffsetId, nextOffsetRate, nextOffsetPeerId, + userStatusesById, messages, totalCount, nextOffsetId, nextOffsetRate, nextOffsetPeerId, } = result; const newFoundIds = messages.map(getSearchResultKey); @@ -142,8 +140,6 @@ addActionHandler('performMiddleSearch', async (global, actions, payload): Promis const resultChatId = isSavedDialog ? currentUserId : chat.id; - global = addChats(global, buildCollectionByKey(chats, 'id')); - global = addUsers(global, buildCollectionByKey(users, 'id')); global = addUserStatuses(global, userStatusesById); global = addMessages(global, messages); global = updateMiddleSearch(global, resultChatId, threadId, { @@ -300,7 +296,7 @@ async function searchSharedMedia( } const { - chats, users, messages, totalCount, nextOffsetId, + userStatusesById, messages, totalCount, nextOffsetId, } = result; const byId = buildCollectionByKey(messages, 'id'); @@ -313,8 +309,7 @@ async function searchSharedMedia( return; } - global = addChats(global, buildCollectionByKey(chats, 'id')); - global = addUsers(global, buildCollectionByKey(users, 'id')); + global = addUserStatuses(global, userStatusesById); global = addChatMessagesById(global, resultChatId, byId); global = updateSharedMediaSearchResults( global, resultChatId, threadId, type, newFoundIds, totalCount, nextOffsetId, tabId, @@ -469,14 +464,13 @@ async function searchChatMedia( } const { - chats, users, messages, + messages, userStatusesById, } = result; const byId = buildCollectionByKey(messages, 'id'); const newFoundIds = Object.keys(byId).map(Number); - global = addChats(global, buildCollectionByKey(chats, 'id')); - global = addUsers(global, buildCollectionByKey(users, 'id')); + global = addUserStatuses(global, userStatusesById); global = addChatMessagesById(global, resultChatId, byId); const loadingState = calcLoadingState(direction, limit, newFoundIds.length, currentSegment); diff --git a/src/global/actions/api/payments.ts b/src/global/actions/api/payments.ts index 56988d3ca..2ce1da692 100644 --- a/src/global/actions/api/payments.ts +++ b/src/global/actions/api/payments.ts @@ -5,7 +5,6 @@ import { PaymentStep } from '../../../types'; import { DEBUG_PAYMENT_SMART_GLOCAL } from '../../../config'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; -import { buildCollectionByKey } from '../../../util/iteratees'; import * as langProvider from '../../../util/oldLangProvider'; import { getStripeError } from '../../../util/payments/stripe'; import { buildQueryString } from '../../../util/requestQuery'; @@ -15,8 +14,7 @@ import { isChatChannel, isChatSuperGroup } from '../../helpers'; import { getRequestInputInvoice, getStarsTransactionFromGift } from '../../helpers/payments'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { - addChats, - addUsers, appendStarsTransactions, closeInvoice, + appendStarsTransactions, closeInvoice, openStarsTransactionFromReceipt, openStarsTransactionModal, setInvoiceInfo, setPaymentForm, @@ -105,12 +103,11 @@ async function getPaymentForm( } const { - form, invoice, users, + form, invoice, } = result; global = getGlobal(); - global = addUsers(global, buildCollectionByKey(users, 'id')); global = setPaymentForm(global, form, tabId); global = setPaymentStep(global, PaymentStep.Checkout, tabId); setGlobal(global); @@ -133,7 +130,6 @@ addActionHandler('getReceipt', async (global, actions, payload): Promise = } global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); if (result.receipt.type === 'stars') { global = openStarsTransactionFromReceipt(global, result.receipt, tabId); } else { @@ -430,7 +426,6 @@ addActionHandler('openPremiumModal', async (global, actions, payload): Promise = } tabState = selectTabState(global, tabId); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); if (oldChatFullInfo) { global = updateChatFullInfo(global, chatId, { boostsApplied: oldBoostsApplied + slots.length, @@ -930,8 +917,6 @@ addActionHandler('checkGiftCode', async (global, actions, payload): Promise => { } global = getGlobal(); - global = addChats(global, buildCollectionByKey(status.chats, 'id')); - global = addUsers(global, buildCollectionByKey(status.users, 'id')); global = { ...global, @@ -1043,8 +1026,6 @@ addActionHandler('loadStarsTransactions', async (global, actions, payload): Prom } global = getGlobal(); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); global = updateStarsBalance(global, result.balance); if (result.history) { diff --git a/src/global/actions/api/reactions.ts b/src/global/actions/api/reactions.ts index bb1ef79cf..bac4d785c 100644 --- a/src/global/actions/api/reactions.ts +++ b/src/global/actions/api/reactions.ts @@ -20,7 +20,7 @@ import { } from '../../helpers'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { - addChatMessagesById, addChats, addUsers, updateChat, updateChatMessage, + addChatMessagesById, updateChat, updateChatMessage, } from '../../reducers'; import { addMessageReaction, subtractXForEmojiInteraction, updateUnreadReactions } from '../../reducers/reactions'; import { updateTabState } from '../../reducers/tabs'; @@ -336,10 +336,6 @@ addActionHandler('loadReactors', async (global, actions, payload): Promise } global = getGlobal(); - - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); - global = updateChatMessage(global, chatId, messageId, { reactors: result, }); @@ -409,15 +405,13 @@ addActionHandler('fetchUnreadReactions', async (global, actions, payload): Promi return; } - const { messages, chats, users } = result; + const { messages } = result; const byId = buildCollectionByKey(messages, 'id'); const ids = Object.keys(byId).map(Number); global = getGlobal(); global = addChatMessagesById(global, chat.id, byId); - global = addUsers(global, buildCollectionByKey(users, 'id')); - global = addChats(global, buildCollectionByKey(chats, 'id')); global = updateUnreadReactions(global, chatId, { unreadReactions: unique([...(chat.unreadReactions || []), ...ids]).sort((a, b) => b - a), }); diff --git a/src/global/actions/api/settings.ts b/src/global/actions/api/settings.ts index bd729de0b..6a31cf1e6 100644 --- a/src/global/actions/api/settings.ts +++ b/src/global/actions/api/settings.ts @@ -1,4 +1,4 @@ -import type { ApiUser, ApiUsername } from '../../../api/types'; +import type { ApiUsername } from '../../../api/types'; import type { ApiPrivacySettings, } from '../../../types'; @@ -19,8 +19,8 @@ import { callApi } from '../../../api/gramjs'; import { buildApiInputPrivacyRules } from '../../helpers'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { - addBlockedUser, addNotifyExceptions, addUsers, deletePeerPhoto, - removeBlockedUser, replaceSettings, updateChat, updateChats, + addBlockedUser, addNotifyExceptions, deletePeerPhoto, + removeBlockedUser, replaceSettings, updateChat, updateNotifySettings, updateUser, updateUserFullInfo, } from '../../reducers'; import { updateTabState } from '../../reducers/tabs'; @@ -47,12 +47,7 @@ addActionHandler('updateProfile', async (global, actions, payload): Promise => { global = getGlobal(); - if (result.users?.length) { - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - } - if (result.chats?.length) { - global = updateChats(global, buildCollectionByKey(result.chats, 'id')); - } - global = { ...global, blocked: { @@ -425,14 +409,10 @@ addActionHandler('loadPrivacySettings', async (global): Promise => { bioSettings, birthdaySettings, ] = result as { - users: ApiUser[]; rules: ApiPrivacySettings; }[]; - const allUsers = result.flatMap((e) => e!.users); - global = getGlobal(); - global = addUsers(global, buildCollectionByKey(allUsers, 'id')); global = { ...global, settings: { @@ -466,7 +446,6 @@ addActionHandler('setPrivacyVisibility', async (global, actions, payload): Promi } global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); global = { ...global, settings: { @@ -502,7 +481,6 @@ addActionHandler('setPrivacyVisibility', async (global, actions, payload): Promi onSuccess?.(); global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); global = { ...global, settings: { @@ -542,7 +520,6 @@ addActionHandler('setPrivacySettings', async (global, actions, payload): Promise } global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); global = { ...global, settings: { diff --git a/src/global/actions/api/statistics.ts b/src/global/actions/api/statistics.ts index da836e7e7..ac70412b9 100644 --- a/src/global/actions/api/statistics.ts +++ b/src/global/actions/api/statistics.ts @@ -1,11 +1,8 @@ import { areDeepEqual } from '../../../util/areDeepEqual'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; -import { buildCollectionByKey } from '../../../util/iteratees'; import { callApi } from '../../../api/gramjs'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { - addChats, - addUsers, updateChannelMonetizationStatistics, updateMessageStatistics, updateStatistics, @@ -36,10 +33,8 @@ addActionHandler('loadStatistics', async (global, actions, payload): Promise => { global.stories.stateHash = result.state; if ('peerStories' in result) { - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); global = addStories(global, result.peerStories); global = updatePeersWithStories(global, result.peerStories); global = updateStealthMode(global, result.stealthMode); @@ -112,8 +107,6 @@ addActionHandler('loadAllHiddenStories', async (global): Promise => { global.stories.archiveStateHash = result.state; if ('peerStories' in result) { - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); global = addStories(global, result.peerStories); global = updatePeersWithStories(global, result.peerStories); global = updateStealthMode(global, result.stealthMode); @@ -153,8 +146,6 @@ addActionHandler('loadPeerSkippedStories', async (global, actions, payload): Pro } global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); global = addStoriesForPeer(global, peerId, result.stories, result.pinnedIds); setGlobal(global); }); @@ -295,8 +286,6 @@ addActionHandler('loadPeerStories', async (global, actions, payload): Promise { loadAllChats({ listType: 'active', - shouldReplace: true, - onReplace: async () => { + onFirstBatchDone: async () => { await loadAndReplaceMessages(global, actions); global = getGlobal(); @@ -90,8 +89,8 @@ addActionHandler('sync', (global, actions): ActionReturnType => { console.log('>>> FINISH SYNC'); } - loadAllChats({ listType: 'archived', shouldReplace: true }); - loadAllChats({ listType: 'saved', shouldReplace: true }); + loadAllChats({ listType: 'archived' }); + loadAllChats({ listType: 'saved' }); preloadTopChatMessages(); loadAllStories(); loadAllHiddenStories(); diff --git a/src/global/actions/api/users.ts b/src/global/actions/api/users.ts index b9d2864d5..39f45f8ad 100644 --- a/src/global/actions/api/users.ts +++ b/src/global/actions/api/users.ts @@ -15,8 +15,6 @@ import { setGlobal, } from '../../index'; import { - addChats, - addUsers, addUserStatuses, closeNewContactDialog, replaceUserStatuses, @@ -113,10 +111,9 @@ addActionHandler('loadTopUsers', async (global): Promise => { return; } - const { ids, users } = result; + const { ids } = result; global = getGlobal(); - global = addUsers(global, buildCollectionByKey(users, 'id')); global = { ...global, topPeers: { @@ -135,8 +132,6 @@ addActionHandler('loadContactList', async (global): Promise => { } global = getGlobal(); - global = addUsers(global, buildCollectionByKey(contactList.users, 'id')); - global = addChats(global, buildCollectionByKey(contactList.chats, 'id')); global = addUserStatuses(global, contactList.userStatusesById); // Sort contact list by Last Name (or First Name), with latin names being placed first @@ -174,12 +169,9 @@ addActionHandler('loadCommonChats', async (global, actions, payload): Promise { }; setGlobal(global); - const result = await callApi('confirmCall', { + callApi('confirmCall', { call, gA, keyFingerprint, }); - if (result) { - global = getGlobal(); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - setGlobal(global); - } })(); } else if (state === 'active' && connections && phoneCall?.state !== 'active') { if (!isOutgoing) { diff --git a/src/global/actions/apiUpdaters/chats.ts b/src/global/actions/apiUpdaters/chats.ts index 3c74098ce..768861a14 100644 --- a/src/global/actions/apiUpdaters/chats.ts +++ b/src/global/actions/apiUpdaters/chats.ts @@ -45,7 +45,8 @@ const TYPING_STATUS_CLEAR_DELAY = 6000; // 6 seconds addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { switch (update['@type']) { case 'updateChat': { - const { isForum: prevIsForum, lastReadOutboxMessageId } = selectChat(global, update.id) || {}; + const localChat = selectChat(global, update.id); + const { isForum: prevIsForum, lastReadOutboxMessageId } = localChat || {}; if (update.chat.lastReadOutboxMessageId && lastReadOutboxMessageId && update.chat.lastReadOutboxMessageId < lastReadOutboxMessageId) { @@ -55,8 +56,6 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { }; } - const localChat = selectChat(global, update.id); - global = updateChat(global, update.id, update.chat); if (localChat?.areStoriesHidden !== update.chat.areStoriesHidden) { @@ -65,8 +64,10 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { setGlobal(global); - if (!update.noTopChatsRequest && !selectIsChatListed(global, update.id)) { - // Chat can appear in dialogs list. + const updatedChat = selectChat(global, update.id); + if (!update.noTopChatsRequest && updatedChat && !selectIsChatListed(global, update.id) + && !updatedChat.isNotJoined) { + // Reload top chats to update chat listing actions.loadTopChats(); } diff --git a/src/global/actions/apiUpdaters/messages.ts b/src/global/actions/apiUpdaters/messages.ts index 0ae9a67ed..c2d0744fd 100644 --- a/src/global/actions/apiUpdaters/messages.ts +++ b/src/global/actions/apiUpdaters/messages.ts @@ -423,33 +423,31 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { break; } - case 'updateThreadInfos': { + case 'updateThreadInfo': { const { - threadInfoUpdates, + threadInfo, } = update; - global = updateThreadInfos(global, threadInfoUpdates); - threadInfoUpdates.forEach((threadInfo) => { - const { chatId, threadId } = threadInfo; - if (!chatId || !threadId) return; + global = updateThreadInfos(global, [threadInfo]); + const { chatId, threadId } = threadInfo; + if (!chatId || !threadId) return; - const chat = selectChat(global, chatId); - const currentThreadInfo = selectThreadInfo(global, chatId, threadId); - if (chat?.isForum && threadInfo.lastReadInboxMessageId !== currentThreadInfo?.lastReadInboxMessageId) { - actions.loadTopicById({ chatId, topicId: Number(threadId) }); - } + const chat = selectChat(global, chatId); + const currentThreadInfo = selectThreadInfo(global, chatId, threadId); + if (chat?.isForum && threadInfo.lastReadInboxMessageId !== currentThreadInfo?.lastReadInboxMessageId) { + actions.loadTopicById({ chatId, topicId: Number(threadId) }); + } - // Update reply thread last read message id if already read in main thread - if (!chat?.isForum) { - const lastReadInboxMessageId = chat?.lastReadInboxMessageId; - const lastReadInboxMessageIdInThread = threadInfo.lastReadInboxMessageId || lastReadInboxMessageId; - if (lastReadInboxMessageId && lastReadInboxMessageIdInThread) { - global = updateThreadInfo(global, chatId, threadId, { - lastReadInboxMessageId: Math.max(lastReadInboxMessageIdInThread, lastReadInboxMessageId), - }); - } + // Update reply thread last read message id if already read in main thread + if (!chat?.isForum) { + const lastReadInboxMessageId = chat?.lastReadInboxMessageId; + const lastReadInboxMessageIdInThread = threadInfo.lastReadInboxMessageId || lastReadInboxMessageId; + if (lastReadInboxMessageId && lastReadInboxMessageIdInThread) { + global = updateThreadInfo(global, chatId, threadId, { + lastReadInboxMessageId: Math.max(lastReadInboxMessageIdInThread, lastReadInboxMessageId), + }); } - }); + } setGlobal(global); break; diff --git a/src/global/actions/apiUpdaters/misc.ts b/src/global/actions/apiUpdaters/misc.ts index 339b670cc..8b4979a1b 100644 --- a/src/global/actions/apiUpdaters/misc.ts +++ b/src/global/actions/apiUpdaters/misc.ts @@ -4,7 +4,9 @@ import { PaymentStep } from '../../../types'; import { addActionHandler, setGlobal } from '../../index'; import { addBlockedUser, + addChats, addStoriesForPeer, + addUsers, removeBlockedUser, removePeerStory, setConfirmPaymentUrl, @@ -13,11 +15,21 @@ import { updatePeerStory, updatePeersWithStories, updateStealthMode, + updateThreadInfos, } from '../../reducers'; import { selectPeerStories, selectPeerStory } from '../../selectors'; addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { switch (update['@type']) { + case 'updateEntities': { + const { users, chats, threadInfos } = update; + if (users) global = addUsers(global, users); + if (chats) global = addChats(global, chats); + if (threadInfos) global = updateThreadInfos(global, threadInfos); + setGlobal(global); + break; + } + case 'updatePeerBlocked': if (update.isBlocked) { return addBlockedUser(global, update.id); diff --git a/src/global/actions/ui/calls.ts b/src/global/actions/ui/calls.ts index 1857b8680..e47b13088 100644 --- a/src/global/actions/ui/calls.ts +++ b/src/global/actions/ui/calls.ts @@ -7,7 +7,7 @@ import type { import { requestNextMutation } from '../../../lib/fasterdom/fasterdom'; import { copyTextToClipboard } from '../../../util/clipboard'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; -import { buildCollectionByKey, omit } from '../../../util/iteratees'; +import { omit } from '../../../util/iteratees'; import * as langProvider from '../../../util/oldLangProvider'; import safePlay from '../../../util/safePlay'; import { ARE_CALLS_SUPPORTED } from '../../../util/windowEnvironment'; @@ -17,7 +17,6 @@ import { addActionHandler, getGlobal, setGlobal, } from '../../index'; -import { addChats, addUsers } from '../../reducers'; import { updateGroupCall } from '../../reducers/calls'; import { updateTabState } from '../../reducers/tabs'; import { @@ -107,31 +106,19 @@ async function fetchGroupCall(global: T, groupCall: Parti undefined, existingGroupCall?.isLoaded ? undefined : result.groupCall.participantsCount, ); - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); setGlobal(global); return result.groupCall; } -async function fetchGroupCallParticipants( - global: T, +function requestGroupCallParticipants( groupCall: Partial, nextOffset?: string, ) { - const result = await callApi('fetchGroupCallParticipants', { + return callApi('fetchGroupCallParticipants', { call: groupCall as ApiGroupCall, offset: nextOffset, }); - - if (!result) return; - - global = getGlobal(); - - global = addUsers(global, buildCollectionByKey(result.users, 'id')); - global = addChats(global, buildCollectionByKey(result.chats, 'id')); - - setGlobal(global); } addActionHandler('toggleGroupCallPanel', (global, actions, payload): ActionReturnType => { @@ -150,7 +137,7 @@ addActionHandler('subscribeToGroupCallUpdates', async (global, actions, payload) if (subscribed) { await fetchGroupCall(global, groupCall); global = getGlobal(); - await fetchGroupCallParticipants(global, groupCall); + await requestGroupCallParticipants(groupCall); } await callApi('toggleGroupCallStartSubscription', { @@ -373,7 +360,7 @@ addActionHandler('loadMoreGroupCallParticipants', (global): ActionReturnType => return; } - void fetchGroupCallParticipants(global, groupCall, groupCall.nextOffset); + void requestGroupCallParticipants(groupCall, groupCall.nextOffset); }); addActionHandler('requestMasterAndRequestCall', (global, actions, payload): ActionReturnType => { diff --git a/src/global/actions/ui/stories.ts b/src/global/actions/ui/stories.ts index aaa4c4282..b74c1ce22 100644 --- a/src/global/actions/ui/stories.ts +++ b/src/global/actions/ui/stories.ts @@ -2,11 +2,11 @@ import type { ActionReturnType } from '../../types'; import { copyTextToClipboard } from '../../../util/clipboard'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; -import { buildCollectionByKey, omit } from '../../../util/iteratees'; +import { omit } from '../../../util/iteratees'; import * as langProvider from '../../../util/oldLangProvider'; import { callApi } from '../../../api/gramjs'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; -import { addChats, addStoriesForPeer, addUsers } from '../../reducers'; +import { addStoriesForPeer } from '../../reducers'; import { updateTabState } from '../../reducers/tabs'; import { selectCurrentViewedStory, @@ -39,8 +39,6 @@ addActionHandler('openStoryViewer', async (global, actions, payload): Promise( ...update, } as ApiThreadInfo; - if (!doNotUpdateLinked) { + if (!doNotUpdateLinked && !newThreadInfo.isCommentsInfo) { const linkedUpdate = pick(newThreadInfo, ['messagesCount', 'lastMessageId', 'lastReadInboxMessageId']); - if (newThreadInfo.isCommentsInfo) { - if (newThreadInfo.threadId) { - global = updateThreadInfo( - global, newThreadInfo.chatId, newThreadInfo.threadId, linkedUpdate, true, - ); - } - } else if (newThreadInfo.fromChannelId && newThreadInfo.fromMessageId) { + if (newThreadInfo.fromChannelId && newThreadInfo.fromMessageId) { global = updateThreadInfo( global, newThreadInfo.fromChannelId, newThreadInfo.fromMessageId, linkedUpdate, true, ); diff --git a/src/global/reducers/users.ts b/src/global/reducers/users.ts index 60bd739b0..f7e50142a 100644 --- a/src/global/reducers/users.ts +++ b/src/global/reducers/users.ts @@ -5,7 +5,7 @@ import type { GlobalState, TabArgs, TabState } from '../types'; import { areDeepEqual } from '../../util/areDeepEqual'; import { getCurrentTabId } from '../../util/establishMultitabRole'; -import { omit, pick } from '../../util/iteratees'; +import { omit, unique } from '../../util/iteratees'; import { MEMO_EMPTY_ARRAY } from '../../util/memo'; import { selectTabState } from '../selectors'; import { updateChat } from './chats'; @@ -26,19 +26,19 @@ function updateContactList(global: T, updatedUsers: ApiUs if (!contactUserIds) return global; - const newContactUserIds = updatedUsers - .filter((user) => user?.isContact && !contactUserIds.includes(user.id)) + const contactUserIdsFromUpdate = updatedUsers + .filter((user) => user?.isContact) .map((user) => user.id); - if (newContactUserIds.length === 0) return global; + if (contactUserIdsFromUpdate.length === 0) return global; return { ...global, contactList: { - userIds: [ - ...newContactUserIds, + userIds: unique([ + ...contactUserIdsFromUpdate, ...contactUserIds, - ], + ]), }, }; } @@ -244,14 +244,9 @@ export function updateUserFullInfo( export function addUserStatuses(global: T, newById: Record): T { const { statusesById } = global.users; - const newKeys = Object.keys(newById).filter((id) => !statusesById[id]); - if (!newKeys.length) { - return global; - } - global = replaceUserStatuses(global, { ...statusesById, - ...pick(newById, newKeys), + ...newById, }); return global; diff --git a/src/global/types.ts b/src/global/types.ts index b69f501b9..826722187 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -1437,8 +1437,7 @@ export interface ActionPayloads { preloadTopChatMessages: undefined; loadAllChats: { listType: ChatListType; - onReplace?: VoidFunction; - shouldReplace?: boolean; + onFirstBatchDone?: VoidFunction; }; openChatWithInfo: ActionPayloads['openChat'] & { profileTab?: ProfileTabType;