[Refactoring] Updater: Process chats, users and messages lists in one place (#4948)

This commit is contained in:
zubiden 2024-09-19 20:43:09 +02:00 committed by Alexander Zinchuk
parent d12dae8dda
commit 7d73682aa7
56 changed files with 592 additions and 1536 deletions

View File

@ -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<string, ApiUserStatus> = {}; const userStatusesById: Record<string, ApiUserStatus> = {};
const usersById: Record<string, ApiUser> = {};
mtpUsers.forEach((mtpUser) => { 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) { 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 { export function buildApiPremiumGiftOption(option: GramJs.TypePremiumGiftOption): ApiPremiumGiftOption {

View File

@ -199,16 +199,6 @@ export function addUserToLocalDb(user: GramJs.User) {
localDb.users[id] = 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) { export function addWebDocumentToLocalDb(webDocument: GramJs.TypeWebDocument) {
localDb.webDocuments[webDocument.url] = webDocument; localDb.webDocuments[webDocument.url] = webDocument;
} }

View File

@ -5,9 +5,7 @@ import type {
ApiPeer, ApiPhoto, ApiReportReason, ApiPeer, ApiPhoto, ApiReportReason,
} from '../../types'; } from '../../types';
import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { buildApiChatLink } from '../apiBuilders/misc'; import { buildApiChatLink } from '../apiBuilders/misc';
import { buildApiUser } from '../apiBuilders/users';
import { buildInputPeer, buildInputPhoto, buildInputReportReason } from '../gramjsBuilders'; import { buildInputPeer, buildInputPhoto, buildInputReportReason } from '../gramjsBuilders';
import { invokeRequest } from './client'; import { invokeRequest } from './client';
@ -83,14 +81,9 @@ export async function resolveBusinessChatLink({ slug } : { slug: string }) {
}); });
if (!result) return undefined; 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); const chatLink = buildApiChatLink(result);
return { return {
users,
chats,
chatLink, chatLink,
}; };
} }

View File

@ -5,10 +5,10 @@ import type {
ApiUpdateAuthorizationStateType, ApiUpdateAuthorizationStateType,
ApiUser, ApiUser,
ApiUserFullInfo, ApiUserFullInfo,
OnApiUpdate,
} from '../../types'; } from '../../types';
import { DEBUG } from '../../../config'; import { DEBUG } from '../../../config';
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
const ApiErrors: { [k: string]: string } = { const ApiErrors: { [k: string]: string } = {
PHONE_NUMBER_INVALID: 'Invalid phone number.', PHONE_NUMBER_INVALID: 'Invalid phone number.',
@ -23,20 +23,14 @@ const authController: {
reject?: Function; reject?: Function;
} = {}; } = {};
let onUpdate: OnApiUpdate;
export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
export function onWebAuthTokenFailed() { export function onWebAuthTokenFailed() {
onUpdate({ sendApiUpdate({
'@type': 'updateWebAuthTokenFailed', '@type': 'updateWebAuthTokenFailed',
}); });
} }
export function onRequestPhoneNumber() { export function onRequestPhoneNumber() {
onUpdate(buildAuthStateUpdate('authorizationStateWaitPhoneNumber')); sendApiUpdate(buildAuthStateUpdate('authorizationStateWaitPhoneNumber'));
return new Promise<string>((resolve, reject) => { return new Promise<string>((resolve, reject) => {
authController.resolve = resolve; authController.resolve = resolve;
@ -45,7 +39,7 @@ export function onRequestPhoneNumber() {
} }
export function onRequestCode(isCodeViaApp = false) { export function onRequestCode(isCodeViaApp = false) {
onUpdate({ sendApiUpdate({
...buildAuthStateUpdate('authorizationStateWaitCode'), ...buildAuthStateUpdate('authorizationStateWaitCode'),
isCodeViaApp, isCodeViaApp,
}); });
@ -57,7 +51,7 @@ export function onRequestCode(isCodeViaApp = false) {
} }
export function onRequestPassword(hint?: string, noReset?: boolean) { export function onRequestPassword(hint?: string, noReset?: boolean) {
onUpdate({ sendApiUpdate({
...buildAuthStateUpdate('authorizationStateWaitPassword'), ...buildAuthStateUpdate('authorizationStateWaitPassword'),
hint, hint,
noReset, noReset,
@ -69,7 +63,7 @@ export function onRequestPassword(hint?: string, noReset?: boolean) {
} }
export function onRequestRegistration() { export function onRequestRegistration() {
onUpdate(buildAuthStateUpdate('authorizationStateWaitRegistration')); sendApiUpdate(buildAuthStateUpdate('authorizationStateWaitRegistration'));
return new Promise<[string, string?]>((resolve) => { return new Promise<[string, string?]>((resolve) => {
authController.resolve = resolve; authController.resolve = resolve;
@ -77,7 +71,7 @@ export function onRequestRegistration() {
} }
export function onRequestQrCode(qrCode: { token: Buffer; expires: number }) { export function onRequestQrCode(qrCode: { token: Buffer; expires: number }) {
onUpdate({ sendApiUpdate({
...buildAuthStateUpdate('authorizationStateWaitQrCode'), ...buildAuthStateUpdate('authorizationStateWaitQrCode'),
qrCode: { qrCode: {
token: btoa(String.fromCharCode(...qrCode.token)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), token: btoa(String.fromCharCode(...qrCode.token)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''),
@ -109,18 +103,18 @@ export function onAuthError(err: Error) {
} }
} }
onUpdate({ sendApiUpdate({
'@type': 'updateAuthorizationError', '@type': 'updateAuthorizationError',
message, message,
}); });
} }
export function onAuthReady() { export function onAuthReady() {
onUpdate(buildAuthStateUpdate('authorizationStateReady')); sendApiUpdate(buildAuthStateUpdate('authorizationStateReady'));
} }
export function onCurrentUserUpdate(currentUser: ApiUser, currentUserFullInfo: ApiUserFullInfo) { export function onCurrentUserUpdate(currentUser: ApiUser, currentUserFullInfo: ApiUserFullInfo) {
onUpdate({ sendApiUpdate({
'@type': 'updateCurrentUser', '@type': 'updateCurrentUser',
currentUser, currentUser,
currentUserFullInfo, currentUserFullInfo,

View File

@ -9,7 +9,6 @@ import type {
ApiPeer, ApiPeer,
ApiThemeParameters, ApiThemeParameters,
ApiUser, ApiUser,
OnApiUpdate,
} from '../../types'; } from '../../types';
import { WEB_APP_PLATFORM } from '../../../config'; import { WEB_APP_PLATFORM } from '../../../config';
@ -37,20 +36,14 @@ import {
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { import {
addDocumentToLocalDb, addDocumentToLocalDb,
addEntitiesToLocalDb,
addPhotoToLocalDb, addPhotoToLocalDb,
addUserToLocalDb, addUserToLocalDb,
addWebDocumentToLocalDb, addWebDocumentToLocalDb,
deserializeBytes, deserializeBytes,
} from '../helpers'; } from '../helpers';
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { invokeRequest } from './client'; import { invokeRequest } from './client';
let onUpdate: OnApiUpdate;
export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
export async function answerCallbackButton({ export async function answerCallbackButton({
chatId, accessHash, messageId, data, isGame, chatId, accessHash, messageId, data, isGame,
}: { }: {
@ -80,7 +73,6 @@ export async function fetchTopInlineBots() {
return { return {
ids, ids,
users,
}; };
} }
@ -98,7 +90,6 @@ export async function fetchTopBotApps() {
return { return {
ids, ids,
users,
}; };
} }
@ -140,15 +131,12 @@ export async function fetchInlineBotResults({
return undefined; return undefined;
} }
addEntitiesToLocalDb(result.users);
return { return {
isGallery: Boolean(result.gallery), isGallery: Boolean(result.gallery),
help: bot.botPlaceholder, help: bot.botPlaceholder,
nextOffset: getInlineBotResultsNextOffset(bot.usernames![0].username, result.nextOffset), nextOffset: getInlineBotResultsNextOffset(bot.usernames![0].username, result.nextOffset),
switchPm: buildBotSwitchPm(result.switchPm), switchPm: buildBotSwitchPm(result.switchPm),
switchWebview: buildBotSwitchWebview(result.switchWebview), switchWebview: buildBotSwitchWebview(result.switchWebview),
users: result.users.map(buildApiUser).filter(Boolean),
results: processInlineBotResult(String(result.queryId), result.results), results: processInlineBotResult(String(result.queryId), result.results),
cacheTime: result.cacheTime, cacheTime: result.cacheTime,
}; };
@ -394,11 +382,9 @@ export async function loadAttachBots({
})); }));
if (result instanceof GramJs.AttachMenuBots) { if (result instanceof GramJs.AttachMenuBots) {
addEntitiesToLocalDb(result.users);
return { return {
hash: result.hash.toString(), hash: result.hash.toString(),
bots: buildCollectionByKey(result.bots.map(buildApiAttachBot), 'id'), bots: buildCollectionByKey(result.bots.map(buildApiAttachBot), 'id'),
users: result.users.map(buildApiUser).filter(Boolean),
}; };
} }
return undefined; return undefined;
@ -414,10 +400,8 @@ export async function loadAttachBot({
})); }));
if (result instanceof GramJs.AttachMenuBotsBot) { if (result instanceof GramJs.AttachMenuBotsBot) {
addEntitiesToLocalDb(result.users);
return { return {
bot: buildApiAttachBot(result.bot), bot: buildApiAttachBot(result.bot),
users: result.users.map(buildApiUser).filter(Boolean),
}; };
} }
return undefined; return undefined;
@ -456,7 +440,7 @@ export async function requestBotUrlAuth({
const authResult = buildApiUrlAuthResult(result); const authResult = buildApiUrlAuthResult(result);
if (authResult?.type === 'request') { if (authResult?.type === 'request') {
onUpdate({ sendApiUpdate({
'@type': 'updateUser', '@type': 'updateUser',
id: authResult.bot.id, id: authResult.bot.id,
user: authResult.bot, user: authResult.bot,
@ -487,7 +471,7 @@ export async function acceptBotUrlAuth({
const authResult = buildApiUrlAuthResult(result); const authResult = buildApiUrlAuthResult(result);
if (authResult?.type === 'request') { if (authResult?.type === 'request') {
onUpdate({ sendApiUpdate({
'@type': 'updateUser', '@type': 'updateUser',
id: authResult.bot.id, id: authResult.bot.id,
user: authResult.bot, user: authResult.bot,
@ -505,7 +489,7 @@ export async function requestLinkUrlAuth({ url }: { url: string }) {
const authResult = buildApiUrlAuthResult(result); const authResult = buildApiUrlAuthResult(result);
if (authResult?.type === 'request') { if (authResult?.type === 'request') {
onUpdate({ sendApiUpdate({
'@type': 'updateUser', '@type': 'updateUser',
id: authResult.bot.id, id: authResult.bot.id,
user: authResult.bot, user: authResult.bot,
@ -524,7 +508,7 @@ export async function acceptLinkUrlAuth({ url, isWriteAllowed }: { url: string;
const authResult = buildApiUrlAuthResult(result); const authResult = buildApiUrlAuthResult(result);
if (authResult?.type === 'request') { if (authResult?.type === 'request') {
onUpdate({ sendApiUpdate({
'@type': 'updateUser', '@type': 'updateUser',
id: authResult.bot.id, id: authResult.bot.id,
user: authResult.bot, user: authResult.bot,
@ -663,14 +647,11 @@ export async function fetchPopularAppBots({
return undefined; return undefined;
} }
addEntitiesToLocalDb(result.users);
const users = result.users.map(buildApiUser).filter(Boolean); 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 { return {
users, peerIds,
chats,
nextOffset: result.nextOffset, nextOffset: result.nextOffset,
}; };
} }

View File

@ -3,8 +3,7 @@ import { Api as GramJs } from '../../../lib/gramjs';
import type { JoinGroupCallPayload } from '../../../lib/secret-sauce'; import type { JoinGroupCallPayload } from '../../../lib/secret-sauce';
import type { import type {
ApiChat, ApiGroupCall, ApiPhoneCall, ApiChat, ApiGroupCall, ApiPhoneCall, ApiUser,
ApiUser, OnApiUpdate,
} from '../../types'; } from '../../types';
import { GROUP_CALL_PARTICIPANTS_LIMIT } from '../../../config'; import { GROUP_CALL_PARTICIPANTS_LIMIT } from '../../../config';
@ -13,20 +12,12 @@ import {
buildApiGroupCallParticipant, buildCallProtocol, buildApiGroupCallParticipant, buildCallProtocol,
buildPhoneCall, buildPhoneCall,
} from '../apiBuilders/calls'; } from '../apiBuilders/calls';
import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { buildApiUser } from '../apiBuilders/users';
import { import {
buildInputGroupCall, buildInputPeer, buildInputPhoneCall, generateRandomInt, buildInputGroupCall, buildInputPeer, buildInputPhoneCall, generateRandomInt,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { addEntitiesToLocalDb } from '../helpers'; import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { invokeRequest, invokeRequestBeacon } from './client'; import { invokeRequest, invokeRequestBeacon } from './client';
let onUpdate: OnApiUpdate;
export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
export async function getGroupCall({ export async function getGroupCall({
call, call,
}: { }: {
@ -40,16 +31,8 @@ export async function getGroupCall({
return undefined; 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 { return {
groupCall: buildApiGroupCall(result.call), groupCall: buildApiGroupCall(result.call),
users,
chats,
}; };
} }
@ -130,25 +113,15 @@ export async function fetchGroupCallParticipants({
})); }));
if (!result) { if (!result) {
return undefined; return;
} }
addEntitiesToLocalDb(result.users); sendApiUpdate({
addEntitiesToLocalDb(result.chats);
const users = result.users.map(buildApiUser).filter(Boolean);
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
onUpdate({
'@type': 'updateGroupCallParticipants', '@type': 'updateGroupCallParticipants',
groupCallId: call.id, groupCallId: call.id,
participants: result.participants.map(buildApiGroupCallParticipant), participants: result.participants.map(buildApiGroupCallParticipant),
nextOffset: result.nextOffset, nextOffset: result.nextOffset,
}); });
return {
users, chats,
};
} }
export function leaveGroupCall({ export function leaveGroupCall({
@ -316,16 +289,12 @@ export async function requestCall({
const call = buildPhoneCall(result.phoneCall); const call = buildPhoneCall(result.phoneCall);
onUpdate({ sendApiUpdate({
'@type': 'updatePhoneCall', '@type': 'updatePhoneCall',
call, call,
}); });
addEntitiesToLocalDb(result.users); return true;
return {
users: result.users.map(buildApiUser).filter(Boolean),
};
} }
export function setCallRating({ export function setCallRating({
@ -369,16 +338,12 @@ export async function acceptCall({
call = buildPhoneCall(result.phoneCall); call = buildPhoneCall(result.phoneCall);
onUpdate({ sendApiUpdate({
'@type': 'updatePhoneCall', '@type': 'updatePhoneCall',
call, call,
}); });
addEntitiesToLocalDb(result.users); return true;
return {
users: result.users.map(buildApiUser).filter(Boolean),
};
} }
export async function confirmCall({ export async function confirmCall({
@ -399,16 +364,12 @@ export async function confirmCall({
call = buildPhoneCall(result.phoneCall); call = buildPhoneCall(result.phoneCall);
onUpdate({ sendApiUpdate({
'@type': 'updatePhoneCall', '@type': 'updatePhoneCall',
call, call,
}); });
addEntitiesToLocalDb(result.users); return true;
return {
users: result.users.map(buildApiUser).filter(Boolean),
};
} }
export function sendSignalingData({ export function sendSignalingData({

View File

@ -17,7 +17,6 @@ import type {
ApiTopic, ApiTopic,
ApiUser, ApiUser,
ApiUserStatus, ApiUserStatus,
OnApiUpdate,
} from '../../types'; } from '../../types';
import { import {
@ -54,7 +53,7 @@ import { buildApiPhoto } from '../apiBuilders/common';
import { buildApiMessage, buildMessageDraft } from '../apiBuilders/messages'; import { buildApiMessage, buildMessageDraft } from '../apiBuilders/messages';
import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers'; import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
import { buildStickerSet } from '../apiBuilders/symbols'; import { buildStickerSet } from '../apiBuilders/symbols';
import { buildApiUser, buildApiUsersAndStatuses } from '../apiBuilders/users'; import { buildApiUser, buildApiUserStatuses } from '../apiBuilders/users';
import { import {
buildChatAdminRights, buildChatAdminRights,
buildChatBannedRights, buildChatBannedRights,
@ -68,22 +67,19 @@ import {
generateRandomBigInt, generateRandomBigInt,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { import {
addEntitiesToLocalDb,
addMessageToLocalDb,
addPhotoToLocalDb, addPhotoToLocalDb,
deserializeBytes, deserializeBytes,
isChatFolder, isChatFolder,
} from '../helpers'; } from '../helpers';
import { scheduleMutedChatUpdate } from '../scheduleUnmute'; import { scheduleMutedChatUpdate } from '../scheduleUnmute';
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { import {
applyState, processAffectedHistory, updateChannelState, applyState, processAffectedHistory, updateChannelState,
} from '../updates/updateManager'; } from '../updates/updateManager';
import { dispatchThreadInfoUpdates } from '../updates/updater';
import { handleGramJsUpdate, invokeRequest, uploadFile } from './client'; import { handleGramJsUpdate, invokeRequest, uploadFile } from './client';
type FullChatData = { type FullChatData = {
fullInfo: ApiChatFullInfo; fullInfo: ApiChatFullInfo;
users: ApiUser[];
chats: ApiChat[]; chats: ApiChat[];
userStatusesById: { [userId: string]: ApiUserStatus }; userStatusesById: { [userId: string]: ApiUserStatus };
groupCall?: Partial<ApiGroupCall>; groupCall?: Partial<ApiGroupCall>;
@ -106,12 +102,6 @@ type ChatListData = {
nextOffsetDate?: number; nextOffsetDate?: number;
}; };
let onUpdate: OnApiUpdate;
export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
export async function fetchChats({ export async function fetchChats({
limit, limit,
offsetDate, offsetDate,
@ -147,13 +137,6 @@ export async function fetchChats({
return undefined; return undefined;
} }
if (resultPinned) {
dispatchThreadInfoUpdates(resultPinned.messages);
updateLocalDb(resultPinned);
}
dispatchThreadInfoUpdates(result.messages);
updateLocalDb(result);
const messages = (resultPinned ? resultPinned.messages : []) const messages = (resultPinned ? resultPinned.messages : [])
.concat(result.messages) .concat(result.messages)
.map(buildApiMessage) .map(buildApiMessage)
@ -167,7 +150,7 @@ export async function fetchChats({
const chats: ApiChat[] = []; const chats: ApiChat[] = [];
const draftsById: Record<string, ApiDraft> = {}; const draftsById: Record<string, ApiDraft> = {};
const dialogs = (resultPinned ? resultPinned.dialogs : []).concat(result.dialogs); const dialogs = (resultPinned?.dialogs || []).concat(result.dialogs);
const orderedPinnedIds: string[] = []; const orderedPinnedIds: string[] = [];
const lastMessageByChatId: Record<string, number> = {}; const lastMessageByChatId: Record<string, number> = {};
@ -202,7 +185,7 @@ export async function fetchChats({
chats.push(chat); chats.push(chat);
scheduleMutedChatUpdate(chat.id, chat.muteUntil, onUpdate); scheduleMutedChatUpdate(chat.id, chat.muteUntil, sendApiUpdate);
if (withPinned && dialog.pinned) { if (withPinned && dialog.pinned) {
orderedPinnedIds.push(chat.id); orderedPinnedIds.push(chat.id);
@ -218,7 +201,8 @@ export async function fetchChats({
const chatIds = chats.map((chat) => chat.id); 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; let totalChatCount: number;
if (result instanceof GramJs.messages.DialogsSlice) { if (result instanceof GramJs.messages.DialogsSlice) {
@ -281,13 +265,6 @@ export async function fetchSavedChats({
const hasPinned = resultPinned && !(resultPinned instanceof GramJs.messages.SavedDialogsNotModified); const hasPinned = resultPinned && !(resultPinned instanceof GramJs.messages.SavedDialogsNotModified);
if (hasPinned) {
updateLocalDb(resultPinned);
}
updateLocalDb(result);
dispatchThreadInfoUpdates(result.messages);
const messages = (hasPinned ? resultPinned.messages : []) const messages = (hasPinned ? resultPinned.messages : [])
.concat(result.messages) .concat(result.messages)
.map(buildApiMessage) .map(buildApiMessage)
@ -321,7 +298,8 @@ export async function fetchSavedChats({
chats.push(chat); 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)); .concat(result.users));
let totalChatCount: number; let totalChatCount: number;
@ -377,10 +355,7 @@ export async function fetchChatSettings(chat: ApiChat) {
return undefined; return undefined;
} }
addEntitiesToLocalDb(result.users);
return { return {
users: result.users.map(buildApiUser).filter(Boolean),
settings: buildApiChatSettings(result.settings), settings: buildApiChatSettings(result.settings),
}; };
} }
@ -391,22 +366,13 @@ export async function searchChats({ query }: { query: string }) {
return undefined; return undefined;
} }
updateLocalDb(result);
const accountPeerIds = result.myResults.map(getApiChatIdFromMtpPeer); const accountPeerIds = result.myResults.map(getApiChatIdFromMtpPeer);
const globalPeerIds = result.results.map(getApiChatIdFromMtpPeer) const globalPeerIds = result.results.map(getApiChatIdFromMtpPeer)
.filter((id) => !accountPeerIds.includes(id)); .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 { return {
accountResultIds: accountPeerIds, accountResultIds: accountPeerIds,
globalResultIds: globalPeerIds, globalResultIds: globalPeerIds,
chats,
users,
}; };
} }
@ -444,7 +410,7 @@ export async function fetchChat({
return undefined; return undefined;
} }
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: chat.id, id: chat.id,
chat, chat,
@ -483,10 +449,7 @@ export async function requestChatUpdate({
return; return;
} }
updateLocalDb(result);
const lastRemoteMessage = buildApiMessage(result.messages[0]); const lastRemoteMessage = buildApiMessage(result.messages[0]);
dispatchThreadInfoUpdates(result.messages);
const lastMessage = lastLocalMessage && (!lastRemoteMessage || (lastLocalMessage.date > lastRemoteMessage.date)) const lastMessage = lastLocalMessage && (!lastRemoteMessage || (lastLocalMessage.date > lastRemoteMessage.date))
? lastLocalMessage ? lastLocalMessage
@ -494,14 +457,14 @@ export async function requestChatUpdate({
const chatUpdate = buildApiChatFromDialog(dialog, peerEntity); const chatUpdate = buildApiChatFromDialog(dialog, peerEntity);
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id, id,
chat: chatUpdate, chat: chatUpdate,
}); });
if (!noLastMessage && lastMessage) { if (!noLastMessage && lastMessage) {
onUpdate({ sendApiUpdate({
'@type': 'updateChatLastMessage', '@type': 'updateChatLastMessage',
id, id,
lastMessage, lastMessage,
@ -510,7 +473,7 @@ export async function requestChatUpdate({
applyState(result.state); applyState(result.state);
scheduleMutedChatUpdate(chatUpdate.id, chatUpdate.muteUntil, onUpdate); scheduleMutedChatUpdate(chatUpdate.id, chatUpdate.muteUntil, sendApiUpdate);
} }
export function saveDraft({ export function saveDraft({
@ -537,8 +500,6 @@ async function getFullChatInfo(chatId: string): Promise<FullChatData | undefined
return undefined; return undefined;
} }
updateLocalDb(result);
const { const {
about, about,
participants, participants,
@ -561,7 +522,7 @@ async function getFullChatInfo(chatId: string): Promise<FullChatData | undefined
const adminMembers = members ? members.filter(({ isAdmin, isOwner }) => isAdmin || isOwner) : undefined; const adminMembers = members ? members.filter(({ isAdmin, isOwner }) => isAdmin || isOwner) : undefined;
const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined; const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined;
const inviteLink = exportedInvite instanceof GramJs.ChatInviteExported ? exportedInvite.link : 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); const chats = result.chats.map((chat) => buildApiChatFromPreview(chat)).filter(Boolean);
return { return {
@ -581,7 +542,6 @@ async function getFullChatInfo(chatId: string): Promise<FullChatData | undefined
isTranslationDisabled: translationsDisabled, isTranslationDisabled: translationsDisabled,
isPreHistoryHidden: true, isPreHistoryHidden: true,
}, },
users,
chats, chats,
userStatusesById, userStatusesById,
groupCall: call ? { groupCall: call ? {
@ -652,11 +612,11 @@ async function getFullChannelInfo(
? exportedInvite.link ? exportedInvite.link
: undefined; : undefined;
const { members, users, userStatusesById } = (canViewParticipants && await fetchMembers(id, accessHash)) || {}; const { members, userStatusesById } = (canViewParticipants && await fetchMembers(id, accessHash)) || {};
const { members: kickedMembers, users: bannedUsers, userStatusesById: bannedStatusesById } = ( const { members: kickedMembers, userStatusesById: bannedStatusesById } = (
canViewParticipants && adminRights && await fetchMembers(id, accessHash, 'kicked') canViewParticipants && adminRights && await fetchMembers(id, accessHash, 'kicked')
) || {}; ) || {};
const { members: adminMembers, users: adminUsers, userStatusesById: adminStatusesById } = ( const { members: adminMembers, userStatusesById: adminStatusesById } = (
canViewParticipants && await fetchMembers(id, accessHash, 'admin') canViewParticipants && await fetchMembers(id, accessHash, 'admin')
) || {}; ) || {};
const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined; const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined;
@ -672,12 +632,10 @@ async function getFullChannelInfo(
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
if (result?.chats?.length > 1) { if (result?.chats?.length > 1) {
updateLocalDb(result);
const [, mtpLinkedChat] = result.chats; const [, mtpLinkedChat] = result.chats;
const linkedChat = buildApiChatFromPreview(mtpLinkedChat); const linkedChat = buildApiChatFromPreview(mtpLinkedChat);
if (linkedChat) { if (linkedChat) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: linkedChat.id, id: linkedChat.id,
chat: linkedChat, chat: linkedChat,
@ -734,7 +692,6 @@ async function getFullChannelInfo(
boostsApplied, boostsApplied,
boostsToUnrestrict: boostsUnrestrict, boostsToUnrestrict: boostsUnrestrict,
}, },
users: [...(users || []), ...(bannedUsers || []), ...(adminUsers || [])],
chats, chats,
userStatusesById: statusesById, userStatusesById: statusesById,
groupCall: call ? { groupCall: call ? {
@ -767,7 +724,7 @@ export async function updateChatMutedState({
settings: new GramJs.InputPeerNotifySettings({ muteUntil }), settings: new GramJs.InputPeerNotifySettings({ muteUntil }),
})); }));
onUpdate({ sendApiUpdate({
'@type': 'updateNotifyExceptions', '@type': 'updateNotifyExceptions',
chatId: chat.id, chatId: chat.id,
isMuted, isMuted,
@ -795,7 +752,7 @@ export async function updateTopicMutedState({
settings: new GramJs.InputPeerNotifySettings({ muteUntil }), settings: new GramJs.InputPeerNotifySettings({ muteUntil }),
})); }));
onUpdate({ sendApiUpdate({
'@type': 'updateTopicNotifyExceptions', '@type': 'updateTopicNotifyExceptions',
chatId: chat.id, chatId: chat.id,
topicId, topicId,
@ -999,7 +956,7 @@ export async function toggleChatPinned({
})); }));
if (isActionSuccessful) { if (isActionSuccessful) {
onUpdate({ sendApiUpdate({
'@type': 'updateChatPinned', '@type': 'updateChatPinned',
id: chat.id, id: chat.id,
isPinned: shouldBePinned, isPinned: shouldBePinned,
@ -1023,7 +980,7 @@ export async function toggleSavedDialogPinned({
})); }));
if (isActionSuccessful) { if (isActionSuccessful) {
onUpdate({ sendApiUpdate({
'@type': 'updateSavedDialogPinned', '@type': 'updateSavedDialogPinned',
id: chat.id, id: chat.id,
isPinned: shouldBePinned, isPinned: shouldBePinned,
@ -1101,7 +1058,7 @@ export async function editChatFolder({
})); }));
if (isActionSuccessful) { if (isActionSuccessful) {
onUpdate({ sendApiUpdate({
'@type': 'updateChatFolder', '@type': 'updateChatFolder',
id, id,
folder: folderUpdate, folder: folderUpdate,
@ -1117,14 +1074,14 @@ export async function deleteChatFolder(id: number) {
const recommendedChatFolders = await fetchRecommendedChatFolders(); const recommendedChatFolders = await fetchRecommendedChatFolders();
if (isActionSuccessful) { if (isActionSuccessful) {
onUpdate({ sendApiUpdate({
'@type': 'updateChatFolder', '@type': 'updateChatFolder',
id, id,
folder: undefined, folder: undefined,
}); });
} }
if (recommendedChatFolders) { if (recommendedChatFolders) {
onUpdate({ sendApiUpdate({
'@type': 'updateRecommendedChatFolders', '@type': 'updateRecommendedChatFolders',
folders: recommendedChatFolders, folders: recommendedChatFolders,
}); });
@ -1152,7 +1109,7 @@ export async function toggleDialogUnread({
})); }));
if (isActionSuccessful) { if (isActionSuccessful) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: chat.id, id: chat.id,
chat: { hasUnreadMark }, chat: { hasUnreadMark },
@ -1191,8 +1148,6 @@ function processResolvedPeer(result?: GramJs.contacts.TypeResolvedPeer) {
return undefined; return undefined;
} }
updateLocalDb(result);
return { return {
chat, chat,
user: buildApiUser(users[0]), user: buildApiUser(users[0]),
@ -1285,7 +1240,7 @@ export async function updateChatAbout(chat: ApiChat, about: string) {
return; return;
} }
onUpdate({ sendApiUpdate({
'@type': 'updateChatFullInfo', '@type': 'updateChatFullInfo',
id: chat.id, id: chat.id,
fullInfo: { fullInfo: {
@ -1351,12 +1306,10 @@ export async function fetchMembers(
return undefined; return undefined;
} }
updateLocalDb(result); const userStatusesById = buildApiUserStatuses(result.users);
const { users, userStatusesById } = buildApiUsersAndStatuses(result.users);
return { return {
members: buildChatMembers(result), members: buildChatMembers(result),
users,
userStatusesById, userStatusesById,
}; };
} }
@ -1381,10 +1334,7 @@ export async function fetchMember({
return undefined; return undefined;
} }
updateLocalDb(result); const userStatusesById = buildApiUserStatuses(result.users);
const { users, userStatusesById } = buildApiUsersAndStatuses(result.users);
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
const member = buildChatMember(result.participant); const member = buildChatMember(result.participant);
if (!member) { if (!member) {
@ -1393,9 +1343,7 @@ export async function fetchMember({
return { return {
member, member,
users,
userStatusesById, userStatusesById,
chats,
}; };
} }
@ -1406,8 +1354,6 @@ export async function fetchGroupsForDiscussion() {
return undefined; return undefined;
} }
updateLocalDb(result);
return result.chats.map((chat) => buildApiChatFromPreview(chat)); return result.chats.map((chat) => buildApiChatFromPreview(chat));
} }
@ -1446,8 +1392,6 @@ export async function migrateChat(chat: ApiChat) {
return undefined; return undefined;
} }
updateLocalDb(result);
const newChannelId = result.updates const newChannelId = result.updates
.find((update): update is GramJs.UpdateChannel => update instanceof GramJs.UpdateChannel)!.channelId; .find((update): update is GramJs.UpdateChannel => update instanceof GramJs.UpdateChannel)!.channelId;
@ -1476,7 +1420,7 @@ export async function openChatByInvite(hash: string) {
addPhotoToLocalDb(result.photo); addPhotoToLocalDb(result.photo);
} }
onUpdate({ sendApiUpdate({
'@type': 'showInvite', '@type': 'showInvite',
data: { data: {
title, title,
@ -1492,7 +1436,7 @@ export async function openChatByInvite(hash: string) {
chat = buildApiChatFromPreview(result.chat); chat = buildApiChatFromPreview(result.chat);
if (chat) { if (chat) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: chat.id, id: chat.id,
chat, chat,
@ -1534,7 +1478,7 @@ export async function addChatMembers(chat: ApiChat, users: ApiUser[]) {
return addChatUsersResult.flat().filter(Boolean); return addChatUsersResult.flat().filter(Boolean);
} }
} catch (err) { } catch (err) {
onUpdate({ sendApiUpdate({
'@type': 'error', '@type': 'error',
error: { error: {
message: (err as Error).message, message: (err as Error).message,
@ -1631,28 +1575,6 @@ function preparePeers(
return store; 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 }) { export async function importChatInvite({ hash }: { hash: string }) {
const updates = await invokeRequest(new GramJs.messages.ImportChatInvite({ hash })); const updates = await invokeRequest(new GramJs.messages.ImportChatInvite({ hash }));
if (!(updates instanceof GramJs.Updates) || !updates.chats.length) { if (!(updates instanceof GramJs.Updates) || !updates.chats.length) {
@ -1758,8 +1680,6 @@ export async function fetchTopics({
}): Promise<{ }): Promise<{
topics: ApiTopic[]; topics: ApiTopic[];
messages: ApiMessage[]; messages: ApiMessage[];
users: ApiUser[];
chats: ApiChat[];
count: number; count: number;
shouldOrderByCreateDate?: boolean; shouldOrderByCreateDate?: boolean;
draftsById: Record<number, ReturnType<typeof buildMessageDraft>>; draftsById: Record<number, ReturnType<typeof buildMessageDraft>>;
@ -1778,15 +1698,10 @@ export async function fetchTopics({
if (!result) return undefined; if (!result) return undefined;
updateLocalDb(result);
const { count, orderByCreateDate } = result; const { count, orderByCreateDate } = result;
const topics = result.topics.map(buildApiTopic).filter(Boolean); const topics = result.topics.map(buildApiTopic).filter(Boolean);
const messages = result.messages.map(buildApiMessage).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) => { const draftsById = result.topics.reduce((acc, topic) => {
if (topic instanceof GramJs.ForumTopic && topic.draft) { if (topic instanceof GramJs.ForumTopic && topic.draft) {
acc[topic.id] = buildMessageDraft(topic.draft); acc[topic.id] = buildMessageDraft(topic.draft);
@ -1803,8 +1718,6 @@ export async function fetchTopics({
return { return {
topics, topics,
messages, messages,
users,
chats,
// Include general topic // Include general topic
count: count + 1, count: count + 1,
shouldOrderByCreateDate: orderByCreateDate, shouldOrderByCreateDate: orderByCreateDate,
@ -1821,8 +1734,6 @@ export async function fetchTopicById({
}): Promise<{ }): Promise<{
topic: ApiTopic; topic: ApiTopic;
messages: ApiMessage[]; messages: ApiMessage[];
users: ApiUser[];
chats: ApiChat[];
} | undefined> { } | undefined> {
const { id, accessHash } = chat; const { id, accessHash } = chat;
@ -1835,18 +1746,11 @@ export async function fetchTopicById({
return undefined; return undefined;
} }
updateLocalDb(result);
const messages = result.messages.map(buildApiMessage).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);
return { return {
topic: buildApiTopic(result.topics[0])!, topic: buildApiTopic(result.topics[0])!,
messages, messages,
users,
chats,
}; };
} }
@ -1927,12 +1831,8 @@ export async function checkChatlistInvite({
if (!result || !invite) return undefined; if (!result || !invite) return undefined;
updateLocalDb(result);
return { return {
invite, 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; if (!result) return undefined;
updateLocalDb(result);
return { return {
invites: result.invites.map(buildApiChatlistExportedInvite).filter(Boolean), 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; return undefined;
} }
updateLocalDb(result);
const similarChannels = result?.chats const similarChannels = result?.chats
.map((c) => buildApiChatFromPreview(c)) .map((c) => buildApiChatFromPreview(c))
.filter(Boolean); .filter(Boolean);

View File

@ -12,7 +12,6 @@ import type {
ApiMediaFormat, ApiMediaFormat,
ApiOnProgress, ApiOnProgress,
ApiSessionData, ApiSessionData,
OnApiUpdate,
} from '../../types'; } from '../../types';
import { import {
@ -20,15 +19,20 @@ import {
DEBUG, DEBUG_GRAMJS, IS_TEST, UPLOAD_WORKERS, DEBUG, DEBUG_GRAMJS, IS_TEST, UPLOAD_WORKERS,
} from '../../../config'; } from '../../../config';
import { pause } from '../../../util/schedulers'; import { pause } from '../../../util/schedulers';
import { buildApiMessage, setMessageBuilderCurrentUserId } from '../apiBuilders/messages'; import {
buildApiMessage,
setMessageBuilderCurrentUserId,
} from '../apiBuilders/messages';
import { buildApiPeerId } from '../apiBuilders/peers'; import { buildApiPeerId } from '../apiBuilders/peers';
import { buildApiStory } from '../apiBuilders/stories'; import { buildApiStory } from '../apiBuilders/stories';
import { buildApiUser, buildApiUserFullInfo } from '../apiBuilders/users'; import { buildApiUser, buildApiUserFullInfo } from '../apiBuilders/users';
import { buildInputPeerFromLocalDb, getEntityTypeById } from '../gramjsBuilders'; import { buildInputPeerFromLocalDb, getEntityTypeById } from '../gramjsBuilders';
import { import {
addEntitiesToLocalDb, addMessageToLocalDb, addStoryToLocalDb, addUserToLocalDb, isResponseUpdate, log, addStoryToLocalDb, addUserToLocalDb, isResponseUpdate, log,
} from '../helpers'; } from '../helpers';
import localDb, { clearLocalDb, type RepairInfo } from '../localDb'; import localDb, { clearLocalDb, type RepairInfo } from '../localDb';
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { processAndUpdateEntities, processMessageAndUpdateThreadInfo } from '../updates/entityProcessor';
import { import {
getDifference, getDifference,
init as initUpdatesManager, init as initUpdatesManager,
@ -55,18 +59,15 @@ const gramJsUpdateEventBuilder = { build: (update: object) => update };
const CHAT_ABORT_CONTROLLERS = new Map<string, ChatAbortController>(); const CHAT_ABORT_CONTROLLERS = new Map<string, ChatAbortController>();
const ABORT_CONTROLLERS = new Map<string, AbortController>(); const ABORT_CONTROLLERS = new Map<string, AbortController>();
let onUpdate: OnApiUpdate;
let client: TelegramClient; let client: TelegramClient;
let currentUserId: string | undefined; let currentUserId: string | undefined;
export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) { export async function init(initialArgs: ApiInitialArgs) {
if (DEBUG) { if (DEBUG) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('>>> START INIT API'); console.log('>>> START INIT API');
} }
onUpdate = _onUpdate;
const { const {
userAgent, platform, sessionData, isTest, isWebmSupported, maxBufferSize, webAuthToken, dcId, userAgent, platform, sessionData, isTest, isWebmSupported, maxBufferSize, webAuthToken, dcId,
mockScenario, shouldForceHttpTransport, shouldAllowHttpTransport, mockScenario, shouldForceHttpTransport, shouldAllowHttpTransport,
@ -130,7 +131,7 @@ export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs)
console.error(err); console.error(err);
if (err.message !== 'Disconnect' && err.message !== 'Cannot send requests while disconnected') { if (err.message !== 'Disconnect' && err.message !== 'Cannot send requests while disconnected') {
onUpdate({ sendApiUpdate({
'@type': 'updateConnectionState', '@type': 'updateConnectionState',
connectionState: 'connectionStateBroken', connectionState: 'connectionStateBroken',
}); });
@ -147,7 +148,7 @@ export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs)
onAuthReady(); onAuthReady();
onSessionUpdate(session.getSessionData()); onSessionUpdate(session.getSessionData());
onUpdate({ '@type': 'updateApiReady' }); sendApiUpdate({ '@type': 'updateApiReady' });
initUpdatesManager(invokeRequest); initUpdatesManager(invokeRequest);
@ -191,7 +192,7 @@ export function getClient() {
} }
function onSessionUpdate(sessionData: ApiSessionData) { function onSessionUpdate(sessionData: ApiSessionData) {
onUpdate({ sendApiUpdate({
'@type': 'updateSession', '@type': 'updateSession',
sessionData, sessionData,
}); });
@ -276,6 +277,8 @@ export async function invokeRequest<T extends GramJs.AnyRequest>(
const result = await client.invoke(request, dcId, abortSignal, shouldRetryOnTimeout); const result = await client.invoke(request, dcId, abortSignal, shouldRetryOnTimeout);
processAndUpdateEntities(result);
if (DEBUG) { if (DEBUG) {
log('RESPONSE', request.className, result); log('RESPONSE', request.className, result);
} }
@ -414,7 +417,7 @@ export function dispatchErrorUpdate<T extends GramJs.AnyRequest>(err: Error, req
const { message } = err; const { message } = err;
onUpdate({ sendApiUpdate({
'@type': 'error', '@type': 'error',
error: { error: {
message, message,
@ -433,7 +436,7 @@ async function handleTerminatedSession() {
}); });
} catch (err: any) { } catch (err: any) {
if (err.message === 'AUTH_KEY_UNREGISTERED' || err.message === 'SESSION_REVOKED') { if (err.message === 'AUTH_KEY_UNREGISTERED' || err.message === 'SESSION_REVOKED') {
onUpdate({ sendApiUpdate({
'@type': 'updateConnectionState', '@type': 'updateConnectionState',
connectionState: 'connectionStateBroken', connectionState: 'connectionStateBroken',
}); });
@ -503,13 +506,12 @@ async function repairMessageMedia(peerId: string, messageId: number) {
const message = result.messages[0]; const message = result.messages[0];
if (message instanceof GramJs.MessageEmpty) return false; if (message instanceof GramJs.MessageEmpty) return false;
addEntitiesToLocalDb(result.users);
addEntitiesToLocalDb(result.chats); processMessageAndUpdateThreadInfo(message);
addMessageToLocalDb(message);
const apiMessage = buildApiMessage(message); const apiMessage = buildApiMessage(message);
if (apiMessage) { if (apiMessage) {
onUpdate({ sendApiUpdate({
'@type': 'updateMessage', '@type': 'updateMessage',
chatId: apiMessage.chatId, chatId: apiMessage.chatId,
id: apiMessage.id, id: apiMessage.id,
@ -531,13 +533,12 @@ async function repairStoryMedia(peerId: string, storyId: number) {
}); });
if (!result) return false; if (!result) return false;
addEntitiesToLocalDb(result.users);
result.stories.forEach((story) => { result.stories.forEach((story) => {
const apiStory = buildApiStory(peerId, story); const apiStory = buildApiStory(peerId, story);
if (!apiStory || 'isDeleted' in apiStory) return; if (!apiStory || 'isDeleted' in apiStory) return;
addStoryToLocalDb(story, peerId); addStoryToLocalDb(story, peerId);
onUpdate({ sendApiUpdate({
'@type': 'updateStory', '@type': 'updateStory',
peerId, peerId,
story: apiStory, story: apiStory,

View File

@ -3,106 +3,42 @@ export {
setForceHttpTransport, setShouldDebugExportedSenders, setAllowHttpTransport, requestChannelDifference, setForceHttpTransport, setShouldDebugExportedSenders, setAllowHttpTransport, requestChannelDifference,
} from './client'; } from './client';
export * from './account';
export { export {
provideAuthPhoneNumber, provideAuthCode, provideAuthPassword, provideAuthRegistration, restartAuth, restartAuthWithQr, provideAuthPhoneNumber, provideAuthCode, provideAuthPassword, provideAuthRegistration, restartAuth, restartAuthWithQr,
} from './auth'; } 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 { export {
broadcastLocalDbUpdateFull, broadcastLocalDbUpdateFull,
} from '../localDb'; } 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 * from './stories';
export { export * from './payments';
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 './fragment'; export * from './fragment';

View File

@ -1,49 +1,22 @@
import type { import type {
ApiInitialArgs, ApiInitialArgs,
ApiOnProgress, ApiOnProgress,
ApiUpdate,
OnApiUpdate, OnApiUpdate,
} from '../../types'; } from '../../types';
import type { LocalDb } from '../localDb'; import type { LocalDb } from '../localDb';
import type { MethodArgs, MethodResponse, Methods } from './types'; 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 { updateFullLocalDb } from '../localDb';
import { init as initUpdater } from '../updates/updater'; import { init as initUpdateEmitter } from '../updates/apiUpdateEmitter';
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 initClient } from './client'; import { init as initClient } from './client';
import * as methods from './index'; 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) { export function initApi(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs, initialLocalDb?: LocalDb) {
onUpdate = _onUpdate; initUpdateEmitter(_onUpdate);
initUpdater(handleUpdate);
initAuth(handleUpdate);
initChats(handleUpdate);
initMessages(handleUpdate);
initUsers(handleUpdate);
initStickers(handleUpdate);
initManagement(handleUpdate);
initTwoFaSettings(handleUpdate);
initBots(handleUpdate);
initCalls(handleUpdate);
initPayments(handleUpdate);
if (initialLocalDb) updateFullLocalDb(initialLocalDb); if (initialLocalDb) updateFullLocalDb(initialLocalDb);
initClient(handleUpdate, initialArgs); initClient(initialArgs);
} }
export function callApi<T extends keyof Methods>(fnName: T, ...args: MethodArgs<T>): MethodResponse<T> { export function callApi<T extends keyof Methods>(fnName: T, ...args: MethodArgs<T>): MethodResponse<T> {
@ -54,35 +27,3 @@ export function callApi<T extends keyof Methods>(fnName: T, ...args: MethodArgs<
export function cancelApiProgress(progressCallback: ApiOnProgress) { export function cancelApiProgress(progressCallback: ApiOnProgress) {
progressCallback.isCanceled = true; 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);
}

View File

@ -1,25 +1,15 @@
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { import type {
ApiChat, ApiError, ApiUser, ApiUsername, OnApiUpdate, ApiChat, ApiError, ApiUser, ApiUsername,
} from '../../types'; } from '../../types';
import { USERNAME_PURCHASE_ERROR } from '../../../config'; import { ACCEPTABLE_USERNAME_ERRORS } from '../../../config';
import { buildCollectionByKey } from '../../../util/iteratees';
import { buildApiExportedInvite, buildChatInviteImporter } from '../apiBuilders/chats'; import { buildApiExportedInvite, buildChatInviteImporter } from '../apiBuilders/chats';
import { buildApiUser } from '../apiBuilders/users';
import { buildInputEntity, buildInputPeer } from '../gramjsBuilders'; import { buildInputEntity, buildInputPeer } from '../gramjsBuilders';
import { addEntitiesToLocalDb } from '../helpers'; import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { invokeRequest } from './client'; 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 }) { export async function checkChatUsername({ username }: { username: string }) {
try { try {
const result = await invokeRequest(new GramJs.channels.CheckUsername({ const result = await invokeRequest(new GramJs.channels.CheckUsername({
@ -59,7 +49,7 @@ export async function setChatUsername(
} }
if (result) { if (result) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: chat.id, id: chat.id,
chat: { usernames: usernames.length ? usernames : undefined }, chat: { usernames: usernames.length ? usernames : undefined },
@ -82,7 +72,7 @@ export async function deactivateAllUsernames({ chat }: { chat: ApiChat }) {
.filter((u) => u.username) .filter((u) => u.username)
: undefined; : undefined;
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: chat.id, id: chat.id,
chat: { usernames }, chat: { usernames },
@ -105,7 +95,7 @@ export async function updatePrivateLink({
if (!(result instanceof GramJs.ChatInviteExported)) return undefined; if (!(result instanceof GramJs.ChatInviteExported)) return undefined;
onUpdate({ sendApiUpdate({
'@type': 'updateChatFullInfo', '@type': 'updateChatFullInfo',
id: chat.id, id: chat.id,
fullInfo: { fullInfo: {
@ -129,7 +119,6 @@ export async function fetchExportedChatInvites({
}); });
if (!exportedInvites) return undefined; if (!exportedInvites) return undefined;
addEntitiesToLocalDb(exportedInvites.users);
const invites = (exportedInvites.invites const invites = (exportedInvites.invites
.filter((invite): invite is GramJs.ChatInviteExported => invite instanceof GramJs.ChatInviteExported)) .filter((invite): invite is GramJs.ChatInviteExported => invite instanceof GramJs.ChatInviteExported))
@ -137,7 +126,6 @@ export async function fetchExportedChatInvites({
return { return {
invites, invites,
users: exportedInvites.users.map(buildApiUser).filter(Boolean),
}; };
} }
@ -164,13 +152,11 @@ export async function editExportedChatInvite({
if (!invite) return undefined; if (!invite) return undefined;
addEntitiesToLocalDb(invite.users);
if (invite instanceof GramJs.messages.ExportedChatInvite && invite.invite instanceof GramJs.ChatInviteExported) { if (invite instanceof GramJs.messages.ExportedChatInvite && invite.invite instanceof GramJs.ChatInviteExported) {
const replaceInvite = buildApiExportedInvite(invite.invite); const replaceInvite = buildApiExportedInvite(invite.invite);
return { return {
oldInvite: replaceInvite, oldInvite: replaceInvite,
newInvite: replaceInvite, newInvite: replaceInvite,
users: invite.users.map(buildApiUser).filter(Boolean),
}; };
} }
@ -182,7 +168,6 @@ export async function editExportedChatInvite({
return { return {
oldInvite, oldInvite,
newInvite, newInvite,
users: invite.users.map(buildApiUser).filter(Boolean),
}; };
} }
return undefined; return undefined;
@ -253,11 +238,9 @@ export async function fetchChatInviteImporters({
}); });
if (!result) return undefined; if (!result) return undefined;
const users = result.users.map((user) => buildApiUser(user)).filter(Boolean);
addEntitiesToLocalDb(result.users);
return { return {
importers: result.importers.map((importer) => buildChatInviteImporter(importer)), importers: result.importers.map((importer) => buildChatInviteImporter(importer)),
users: buildCollectionByKey(users, 'id'),
}; };
} }

View File

@ -22,11 +22,9 @@ import type {
ApiSticker, ApiSticker,
ApiStory, ApiStory,
ApiStorySkipped, ApiStorySkipped,
ApiUser,
ApiUserStatus, ApiUserStatus,
ApiVideo, ApiVideo,
MediaContent, MediaContent,
OnApiUpdate,
} from '../../types'; } from '../../types';
import { MAIN_THREAD_ID, MESSAGE_DELETED } from '../../types'; import { MAIN_THREAD_ID, MESSAGE_DELETED } from '../../types';
@ -62,7 +60,7 @@ import {
buildUploadingMedia, buildUploadingMedia,
} from '../apiBuilders/messages'; } from '../apiBuilders/messages';
import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers'; import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
import { buildApiUser, buildApiUsersAndStatuses } from '../apiBuilders/users'; import { buildApiUser, buildApiUserStatuses } from '../apiBuilders/users';
import { import {
buildInputEntity, buildInputEntity,
buildInputMediaDocument, buildInputMediaDocument,
@ -82,13 +80,12 @@ import {
getEntityTypeById, getEntityTypeById,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { import {
addEntitiesToLocalDb,
addMessageToLocalDb,
deserializeBytes, deserializeBytes,
resolveMessageApiChatId, resolveMessageApiChatId,
} from '../helpers'; } from '../helpers';
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { processMessageAndUpdateThreadInfo } from '../updates/entityProcessor';
import { processAffectedHistory, updateChannelState } from '../updates/updateManager'; import { processAffectedHistory, updateChannelState } from '../updates/updateManager';
import { dispatchThreadInfoUpdates } from '../updates/updater';
import { requestChatUpdate } from './chats'; import { requestChatUpdate } from './chats';
import { handleGramJsUpdate, invokeRequest, uploadFile } from './client'; import { handleGramJsUpdate, invokeRequest, uploadFile } from './client';
@ -106,21 +103,13 @@ type TranslateTextParams = ({
type SearchResults = { type SearchResults = {
messages: ApiMessage[]; messages: ApiMessage[];
users: ApiUser[];
userStatusesById: Record<number, ApiUserStatus>; userStatusesById: Record<number, ApiUserStatus>;
chats: ApiChat[];
totalCount: number; totalCount: number;
nextOffsetRate?: number; nextOffsetRate?: number;
nextOffsetPeerId?: string; nextOffsetPeerId?: string;
nextOffsetId?: number; nextOffsetId?: number;
}; };
let onUpdate: OnApiUpdate;
export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
export async function fetchMessages({ export async function fetchMessages({
chat, chat,
threadId, threadId,
@ -158,7 +147,7 @@ export async function fetchMessages({
}); });
} catch (err: any) { } catch (err: any) {
if (err.message === 'CHANNEL_PRIVATE') { if (err.message === 'CHANNEL_PRIVATE') {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: chat.id, id: chat.id,
chat: { chat: {
@ -176,13 +165,10 @@ export async function fetchMessages({
return undefined; return undefined;
} }
updateLocalDb(result);
const messages = result.messages.map(buildApiMessage).filter(Boolean); const messages = result.messages.map(buildApiMessage).filter(Boolean);
const users = result.users.map(buildApiUser).filter(Boolean); const users = result.users.map(buildApiUser).filter(Boolean);
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
const count = !(result instanceof GramJs.messages.Messages) && result.count; const count = !(result instanceof GramJs.messages.Messages) && result.count;
dispatchThreadInfoUpdates(result.messages);
return { return {
messages, 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 // 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 // in the comment group or this group was deleted
if (message !== 'CHANNEL_PRIVATE') { if (message !== 'CHANNEL_PRIVATE') {
onUpdate({ sendApiUpdate({
'@type': 'error', '@type': 'error',
error: { error: {
message, message,
@ -245,18 +231,14 @@ export async function fetchMessage({ chat, messageId }: { chat: ApiChat; message
return MESSAGE_DELETED; return MESSAGE_DELETED;
} }
const message = mtpMessage && buildApiMessage(mtpMessage); processMessageAndUpdateThreadInfo(mtpMessage);
dispatchThreadInfoUpdates([mtpMessage]); const message = buildApiMessage(mtpMessage);
if (!message) { if (!message) {
return undefined; return undefined;
} }
addMessageToLocalDb(mtpMessage); return { message };
const users = result.users.map(buildApiUser).filter(Boolean);
return { message, users };
} }
let mediaQueue = Promise.resolve(); let mediaQueue = Promise.resolve();
@ -326,7 +308,7 @@ export function sendMessage(
effectId, effectId,
); );
onUpdate({ sendApiUpdate({
'@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage', '@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage',
id: localMessage.id, id: localMessage.id,
chatId: chat.id, chatId: chat.id,
@ -337,7 +319,7 @@ export function sendMessage(
// This is expected to arrive after `updateMessageSendSucceeded` which replaces the local ID, // This is expected to arrive after `updateMessageSendSucceeded` which replaces the local ID,
// so in most cases this will be simply ignored // so in most cases this will be simply ignored
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
onUpdate({ sendApiUpdate({
'@type': localMessage.isScheduled ? 'updateScheduledMessage' : 'updateMessage', '@type': localMessage.isScheduled ? 'updateScheduledMessage' : 'updateMessage',
id: localMessage.id, id: localMessage.id,
chatId: chat.id, chatId: chat.id,
@ -419,10 +401,10 @@ export function sendMessage(
if (update) handleLocalMessageUpdate(localMessage, update); if (update) handleLocalMessageUpdate(localMessage, update);
} catch (error: any) { } catch (error: any) {
if (error.message === 'PRIVACY_PREMIUM_REQUIRED') { if (error.message === 'PRIVACY_PREMIUM_REQUIRED') {
onUpdate({ '@type': 'updateRequestUserUpdate', id: chat.id }); sendApiUpdate({ '@type': 'updateRequestUserUpdate', id: chat.id });
} }
onUpdate({ sendApiUpdate({
'@type': 'updateMessageSendFailed', '@type': 'updateMessageSendFailed',
chatId: chat.id, chatId: chat.id,
localId: localMessage.id, localId: localMessage.id,
@ -624,7 +606,7 @@ export async function editMessage({
isInvertedMedia, isInvertedMedia,
}; };
onUpdate({ sendApiUpdate({
'@type': isScheduled ? 'updateScheduledMessage' : 'updateMessage', '@type': isScheduled ? 'updateScheduledMessage' : 'updateMessage',
id: message.id, id: message.id,
chatId: chat.id, chatId: chat.id,
@ -657,7 +639,7 @@ export async function editMessage({
const { message: messageErr } = err as Error; const { message: messageErr } = err as Error;
onUpdate({ sendApiUpdate({
'@type': 'error', '@type': 'error',
error: { error: {
message: messageErr, message: messageErr,
@ -666,7 +648,7 @@ export async function editMessage({
}); });
// Rollback changes // Rollback changes
onUpdate({ sendApiUpdate({
'@type': isScheduled ? 'updateScheduledMessage' : 'updateMessage', '@type': isScheduled ? 'updateScheduledMessage' : 'updateMessage',
id: message.id, id: message.id,
chatId: chat.id, chatId: chat.id,
@ -823,7 +805,7 @@ export async function deleteMessages({
processAffectedHistory(chat, result); processAffectedHistory(chat, result);
onUpdate({ sendApiUpdate({
'@type': 'deleteMessages', '@type': 'deleteMessages',
ids: messageIds, ids: messageIds,
...(isChannel && { chatId: chat.id }), ...(isChannel && { chatId: chat.id }),
@ -872,7 +854,7 @@ export async function deleteHistory({
} }
} }
onUpdate({ sendApiUpdate({
'@type': 'deleteHistory', '@type': 'deleteHistory',
chatId: chat.id, chatId: chat.id,
}); });
@ -898,7 +880,7 @@ export async function deleteSavedHistory({
return; return;
} }
onUpdate({ sendApiUpdate({
'@type': 'deleteSavedHistory', '@type': 'deleteSavedHistory',
chatId: chat.id, chatId: chat.id,
}); });
@ -984,7 +966,7 @@ export async function markMessageListRead({
if (threadId === MAIN_THREAD_ID) { if (threadId === MAIN_THREAD_ID) {
void requestChatUpdate({ chat, noLastMessage: true }); void requestChatUpdate({ chat, noLastMessage: true });
} else if (chat.isForum) { } else if (chat.isForum) {
onUpdate({ sendApiUpdate({
'@type': 'updateTopic', '@type': 'updateTopic',
chatId: chat.id, chatId: chat.id,
topicId: Number(threadId), topicId: Number(threadId),
@ -1018,7 +1000,7 @@ export async function markMessagesRead({
processAffectedHistory(chat, result); processAffectedHistory(chat, result);
} }
onUpdate({ sendApiUpdate({
...(isChannel ? { ...(isChannel ? {
'@type': 'updateChannelMessages', '@type': 'updateChannelMessages',
channelId: chat.id, channelId: chat.id,
@ -1052,8 +1034,6 @@ export async function fetchMessageViews({
if (!results || results.some((result) => !result)) return undefined; if (!results || results.some((result) => !result)) return undefined;
const viewsList = results.flatMap((result) => result!.views); 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 viewsInfo = ids.map((id, index) => {
const { views, forwards, replies } = viewsList[index]; const { views, forwards, replies } = viewsList[index];
@ -1067,8 +1047,6 @@ export async function fetchMessageViews({
return { return {
viewsInfo, 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; 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 topMessages = result.messages.map(buildApiMessage).filter(Boolean);
const messages = topMessages.concat(replies.messages); const messages = topMessages.concat(replies.messages);
const threadId = result.messages[result.messages.length - 1]?.id; const threadId = result.messages[result.messages.length - 1]?.id;
if (!threadId) return undefined; if (!threadId) return undefined;
dispatchThreadInfoUpdates(result.messages);
const { const {
unreadCount, maxId, readInboxMaxId, readOutboxMaxId, unreadCount, maxId, readInboxMaxId, readOutboxMaxId,
} = result; } = result;
return { return {
chats,
users,
messages, messages,
topMessages, topMessages,
unreadCount, unreadCount,
@ -1215,12 +1183,8 @@ export async function searchMessagesInChat({
return undefined; return undefined;
} }
updateLocalDb(result); const userStatusesById = buildApiUserStatuses(result.users);
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
const { users, userStatusesById } = buildApiUsersAndStatuses(result.users);
const messages = result.messages.map(buildApiMessage).filter(Boolean); const messages = result.messages.map(buildApiMessage).filter(Boolean);
dispatchThreadInfoUpdates(result.messages);
let totalCount = messages.length; let totalCount = messages.length;
let nextOffsetId: number | undefined; let nextOffsetId: number | undefined;
@ -1233,8 +1197,6 @@ export async function searchMessagesInChat({
} }
return { return {
chats,
users,
userStatusesById, userStatusesById,
messages, messages,
totalCount, totalCount,
@ -1304,12 +1266,8 @@ export async function searchMessagesGlobal({
return undefined; return undefined;
} }
updateLocalDb(result); const userStatusesById = buildApiUserStatuses(result.users);
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
const { users, userStatusesById } = buildApiUsersAndStatuses(result.users);
const messages = result.messages.map(buildApiMessage).filter(Boolean); const messages = result.messages.map(buildApiMessage).filter(Boolean);
dispatchThreadInfoUpdates(result.messages);
let totalCount = messages.length; let totalCount = messages.length;
if (result instanceof GramJs.messages.MessagesSlice || result instanceof GramJs.messages.ChannelMessages) { if (result instanceof GramJs.messages.MessagesSlice || result instanceof GramJs.messages.ChannelMessages) {
@ -1325,9 +1283,7 @@ export async function searchMessagesGlobal({
return { return {
messages, messages,
users,
userStatusesById, userStatusesById,
chats,
totalCount, totalCount,
nextOffsetRate, nextOffsetRate,
nextOffsetPeerId, nextOffsetPeerId,
@ -1357,12 +1313,8 @@ export async function searchHashtagPosts({
return undefined; return undefined;
} }
updateLocalDb(result); const userStatusesById = buildApiUserStatuses(result.users);
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
const { users, userStatusesById } = buildApiUsersAndStatuses(result.users);
const messages = result.messages.map(buildApiMessage).filter(Boolean); const messages = result.messages.map(buildApiMessage).filter(Boolean);
dispatchThreadInfoUpdates(result.messages);
let totalCount = messages.length; let totalCount = messages.length;
if (result instanceof GramJs.messages.MessagesSlice || result instanceof GramJs.messages.ChannelMessages) { if (result instanceof GramJs.messages.MessagesSlice || result instanceof GramJs.messages.ChannelMessages) {
@ -1378,9 +1330,7 @@ export async function searchHashtagPosts({
return { return {
messages, messages,
users,
userStatusesById, userStatusesById,
chats,
totalCount, totalCount,
nextOffsetRate, nextOffsetRate,
nextOffsetPeerId, nextOffsetPeerId,
@ -1458,14 +1408,6 @@ export async function loadPollOptionResults({
return undefined; 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) => ({ const votes = result.votes.map((vote) => ({
peerId: getApiChatIdFromMtpPeer(vote.peer), peerId: getApiChatIdFromMtpPeer(vote.peer),
date: vote.date, date: vote.date,
@ -1474,8 +1416,6 @@ export async function loadPollOptionResults({
return { return {
count: result.count, count: result.count,
votes, votes,
chats,
users,
nextOffset: result.nextOffset, nextOffset: result.nextOffset,
shouldResetVoters, shouldResetVoters,
}; };
@ -1539,7 +1479,7 @@ export async function forwardMessages({
}); });
localMessages[randomIds[index].toString()] = localMessage; localMessages[randomIds[index].toString()] = localMessage;
onUpdate({ sendApiUpdate({
'@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage', '@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage',
id: localMessage.id, id: localMessage.id,
chatId: toChat.id, chatId: toChat.id,
@ -1568,7 +1508,7 @@ export async function forwardMessages({
if (update) handleMultipleLocalMessagesUpdate(localMessages, update); if (update) handleMultipleLocalMessagesUpdate(localMessages, update);
} catch (error: any) { } catch (error: any) {
Object.values(localMessages).forEach((localMessage) => { Object.values(localMessages).forEach((localMessage) => {
onUpdate({ sendApiUpdate({
'@type': 'updateMessageSendFailed', '@type': 'updateMessageSendFailed',
chatId: toChat.id, chatId: toChat.id,
localId: localMessage.id, localId: localMessage.id,
@ -1620,10 +1560,7 @@ export async function fetchScheduledHistory({ chat }: { chat: ApiChat }) {
return undefined; return undefined;
} }
updateLocalDb(result);
const messages = result.messages.map(buildApiMessage).filter(Boolean); const messages = result.messages.map(buildApiMessage).filter(Boolean);
dispatchThreadInfoUpdates(result.messages);
return { return {
messages, 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 }) { export async function fetchPinnedMessages({ chat, threadId }: { chat: ApiChat; threadId: ThreadId }) {
const result = await invokeRequest(new GramJs.messages.Search( const result = await invokeRequest(new GramJs.messages.Search(
{ {
@ -1673,17 +1598,10 @@ export async function fetchPinnedMessages({ chat, threadId }: { chat: ApiChat; t
return undefined; 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); const messages = result.messages.map(buildApiMessage).filter(Boolean);
dispatchThreadInfoUpdates(result.messages);
return { return {
messages, messages,
users,
chats,
}; };
} }
@ -1718,15 +1636,7 @@ export async function fetchSendAs({
return undefined; 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 { return {
users,
chats,
sendAs: result.peers.map(buildApiSendAsPeerId), sendAs: result.peers.map(buildApiSendAsPeerId),
}; };
} }
@ -1751,16 +1661,10 @@ export async function fetchSponsoredMessages({ chat }: { chat: ApiChat }) {
return undefined; return undefined;
} }
updateLocalDb(result);
const messages = result.messages.map(buildApiSponsoredMessage).filter(Boolean); 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 { return {
messages, messages,
users,
chats,
}; };
} }
@ -1837,17 +1741,10 @@ export async function fetchUnreadMentions({
return undefined; return undefined;
} }
updateLocalDb(result);
const messages = result.messages.map(buildApiMessage).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);
return { return {
messages, messages,
users,
chats,
}; };
} }
@ -1874,17 +1771,10 @@ export async function fetchUnreadReactions({
return undefined; return undefined;
} }
updateLocalDb(result);
const messages = result.messages.map(buildApiMessage).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);
return { return {
messages, messages,
users,
chats,
}; };
} }
@ -1900,7 +1790,7 @@ export async function transcribeAudio({
if (!result) return undefined; if (!result) return undefined;
onUpdate({ sendApiUpdate({
'@type': 'updateTranscribedAudio', '@type': 'updateTranscribedAudio',
isPending: result.pending, isPending: result.pending,
transcriptionId: result.transcriptionId.toString(), transcriptionId: result.transcriptionId.toString(),
@ -1933,7 +1823,7 @@ export async function translateText(params: TranslateTextParams) {
const formattedText = result.result.map((r) => buildApiFormattedText(r)); const formattedText = result.result.map((r) => buildApiFormattedText(r));
if (isMessageTranslation) { if (isMessageTranslation) {
onUpdate({ sendApiUpdate({
'@type': 'updateMessageTranslations', '@type': 'updateMessageTranslations',
chatId: params.chat.id, chatId: params.chat.id,
messageIds: params.messageIds, messageIds: params.messageIds,
@ -1993,13 +1883,13 @@ function handleLocalMessageUpdate(localMessage: ApiMessage, update: GramJs.TypeU
} }
const mtpMessage = buildMessageFromUpdate(messageUpdate.id, localMessage.chatId, messageUpdate); const mtpMessage = buildMessageFromUpdate(messageUpdate.id, localMessage.chatId, messageUpdate);
addMessageToLocalDb(mtpMessage); processMessageAndUpdateThreadInfo(mtpMessage);
} }
// Edge case for "Send When Online" // Edge case for "Send When Online"
const isSentBefore = 'date' in messageUpdate && messageUpdate.date * 1000 < Date.now() + getServerTimeOffset() * 1000; const isSentBefore = 'date' in messageUpdate && messageUpdate.date * 1000 < Date.now() + getServerTimeOffset() * 1000;
onUpdate({ sendApiUpdate({
'@type': localMessage.isScheduled && !isSentBefore '@type': localMessage.isScheduled && !isSentBefore
? 'updateScheduledMessageSendSucceeded' ? 'updateScheduledMessageSendSucceeded'
: 'updateMessageSendSucceeded', : 'updateMessageSendSucceeded',
@ -2040,20 +1930,12 @@ export async function fetchQuickReplies() {
const result = await invokeRequest(new GramJs.messages.GetQuickReplies({})); const result = await invokeRequest(new GramJs.messages.GetQuickReplies({}));
if (!result || result instanceof GramJs.messages.QuickRepliesNotModified) return undefined; if (!result || result instanceof GramJs.messages.QuickRepliesNotModified) return undefined;
updateLocalDb(result);
const messages = result.messages.map(buildApiMessage).filter(Boolean); 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); const quickReplies = result.quickReplies.map(buildApiQuickReply);
return { return {
messages, messages,
chats,
users,
quickReplies, quickReplies,
}; };
} }

View File

@ -4,11 +4,9 @@ import { Api as GramJs } from '../../../lib/gramjs';
import type { import type {
ApiChat, ApiInputStorePaymentPurpose, ApiPeer, ApiRequestInputInvoice, ApiChat, ApiInputStorePaymentPurpose, ApiPeer, ApiRequestInputInvoice,
ApiThemeParameters, ApiThemeParameters,
OnApiUpdate,
} from '../../types'; } from '../../types';
import { DEBUG } from '../../../config'; import { DEBUG } from '../../../config';
import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { import {
buildApiBoost, buildApiBoost,
buildApiBoostsStatus, buildApiBoostsStatus,
@ -24,26 +22,19 @@ import {
buildApiStarTopupOption, buildApiStarTopupOption,
buildShippingOptions, buildShippingOptions,
} from '../apiBuilders/payments'; } from '../apiBuilders/payments';
import { buildApiUser } from '../apiBuilders/users';
import { import {
buildInputInvoice, buildInputPeer, buildInputStorePaymentPurpose, buildInputThemeParams, buildShippingInfo, buildInputInvoice, buildInputPeer, buildInputStorePaymentPurpose, buildInputThemeParams, buildShippingInfo,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { import {
addEntitiesToLocalDb,
addWebDocumentToLocalDb, addWebDocumentToLocalDb,
deserializeBytes, deserializeBytes,
serializeBytes, serializeBytes,
} from '../helpers'; } from '../helpers';
import localDb from '../localDb'; import localDb from '../localDb';
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { handleGramJsUpdate, invokeRequest } from './client'; import { handleGramJsUpdate, invokeRequest } from './client';
import { getTemporaryPaymentPassword } from './twoFaSettings'; import { getTemporaryPaymentPassword } from './twoFaSettings';
let onUpdate: OnApiUpdate;
export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
export async function validateRequestedInfo({ export async function validateRequestedInfo({
inputInvoice, inputInvoice,
requestInfo, requestInfo,
@ -116,7 +107,7 @@ export async function sendPaymentForm({
if (!result) return false; if (!result) return false;
if (result instanceof GramJs.payments.PaymentVerificationNeeded) { if (result instanceof GramJs.payments.PaymentVerificationNeeded) {
onUpdate({ sendApiUpdate({
'@type': 'updatePaymentVerificationNeeded', '@type': 'updatePaymentVerificationNeeded',
url: result.url, url: result.url,
}); });
@ -171,12 +162,9 @@ export async function getPaymentForm(inputInvoice: ApiRequestInputInvoice, theme
addWebDocumentToLocalDb(result.photo); addWebDocumentToLocalDb(result.photo);
} }
addEntitiesToLocalDb(result.users);
return { return {
form: buildApiPaymentForm(result), form: buildApiPaymentForm(result),
invoice: buildApiInvoiceFromForm(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; return undefined;
} }
addEntitiesToLocalDb(result.users);
return { return {
receipt: buildApiReceipt(result), 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()); const result = await invokeRequest(new GramJs.help.GetPremiumPromo());
if (!result) return undefined; if (!result) return undefined;
addEntitiesToLocalDb(result.users);
const users = result.users.map(buildApiUser).filter(Boolean);
result.videos.forEach((video) => { result.videos.forEach((video) => {
if (video instanceof GramJs.Document) { if (video instanceof GramJs.Document) {
localDb.documents[video.id.toString()] = video; localDb.documents[video.id.toString()] = video;
@ -213,7 +195,6 @@ export async function fetchPremiumPromo() {
return { return {
promo: buildApiPremiumPromo(result), promo: buildApiPremiumPromo(result),
users,
}; };
} }
@ -239,16 +220,9 @@ export async function fetchMyBoosts() {
if (!result) return undefined; 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); const boosts = result.myBoosts.map(buildApiMyBoost);
return { return {
users,
chats,
boosts, boosts,
}; };
} }
@ -267,16 +241,9 @@ export async function applyBoost({
if (!result) return undefined; 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); const boosts = result.myBoosts.map(buildApiMyBoost);
return { return {
users,
chats,
boosts, boosts,
}; };
} }
@ -319,16 +286,11 @@ export async function fetchBoostList({
return undefined; return undefined;
} }
addEntitiesToLocalDb(result.users);
const users = result.users.map(buildApiUser).filter(Boolean);
const boostList = result.boosts.map(buildApiBoost); const boostList = result.boosts.map(buildApiBoost);
return { return {
count: result.count, count: result.count,
boostList, boostList,
users,
nextOffset: result.nextOffset, nextOffset: result.nextOffset,
}; };
} }
@ -365,13 +327,8 @@ export async function checkGiftCode({
return undefined; return undefined;
} }
addEntitiesToLocalDb(result.users);
addEntitiesToLocalDb(result.chats);
return { return {
code: buildApiCheckedGiftCode(result), 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; return undefined;
} }
const users = result.users.map(buildApiUser).filter(Boolean);
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
return { return {
users,
chats,
nextOffset: result.nextOffset, nextOffset: result.nextOffset,
history: result.history?.map(buildApiStarsTransaction), history: result.history?.map(buildApiStarsTransaction),
balance: result.balance.toJSNumber(), balance: result.balance.toJSNumber(),
@ -481,12 +433,7 @@ export async function fetchStarsTransactions({
return undefined; return undefined;
} }
const users = result.users.map(buildApiUser).filter(Boolean);
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
return { return {
users,
chats,
nextOffset: result.nextOffset, nextOffset: result.nextOffset,
history: result.history?.map(buildApiStarsTransaction), history: result.history?.map(buildApiStarsTransaction),
balance: result.balance.toJSNumber(), balance: result.balance.toJSNumber(),
@ -511,12 +458,7 @@ export async function fetchStarsTransactionById({
return undefined; return undefined;
} }
const users = result.users.map(buildApiUser).filter(Boolean);
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
return { return {
users,
chats,
transaction: buildApiStarsTransaction(result.history[0]), transaction: buildApiStarsTransaction(result.history[0]),
}; };
} }

View File

@ -129,7 +129,7 @@ function computeEmojiIndex(bytes: Uint8Array) {
.or((BigInt(bytes[7]))); .or((BigInt(bytes[7])));
} }
export async function generateEmojiFingerprint( async function generateEmojiFingerprint(
authKey: Uint8Array, gA: Uint8Array, emojiData: Uint16Array, emojiOffsets: number[], authKey: Uint8Array, gA: Uint8Array, emojiData: Uint16Array, emojiOffsets: number[],
) { ) {
const hash = await Helpers.sha256(Buffer.concat([new Uint8Array(authKey), new Uint8Array(gA)])); const hash = await Helpers.sha256(Buffer.concat([new Uint8Array(authKey), new Uint8Array(gA)]));

View File

@ -12,7 +12,6 @@ import {
TOP_REACTIONS_LIMIT, TOP_REACTIONS_LIMIT,
} from '../../../config'; } from '../../../config';
import { split } from '../../../util/iteratees'; import { split } from '../../../util/iteratees';
import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { import {
buildApiAvailableEffect, buildApiAvailableEffect,
buildApiAvailableReaction, buildApiAvailableReaction,
@ -21,9 +20,7 @@ import {
buildMessagePeerReaction, buildMessagePeerReaction,
} from '../apiBuilders/reactions'; } from '../apiBuilders/reactions';
import { buildStickerFromDocument } from '../apiBuilders/symbols'; import { buildStickerFromDocument } from '../apiBuilders/symbols';
import { buildApiUser } from '../apiBuilders/users';
import { buildInputPeer, buildInputReaction } from '../gramjsBuilders'; import { buildInputPeer, buildInputReaction } from '../gramjsBuilders';
import { addEntitiesToLocalDb } from '../helpers';
import localDb from '../localDb'; import localDb from '../localDb';
import { invokeRequest } from './client'; import { invokeRequest } from './client';
@ -187,14 +184,9 @@ export async function fetchMessageReactionsList({
return undefined; return undefined;
} }
addEntitiesToLocalDb(result.users);
addEntitiesToLocalDb(result.chats);
const { nextOffset, reactions, count } = result; const { nextOffset, reactions, count } = result;
return { return {
users: result.users.map(buildApiUser).filter(Boolean),
chats: result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean),
nextOffset, nextOffset,
reactions: reactions.map(buildMessagePeerReaction).filter(Boolean), reactions: reactions.map(buildMessagePeerReaction).filter(Boolean),
count, count,

View File

@ -13,11 +13,12 @@ import type {
ApiUser, ApiUser,
} from '../../types'; } 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 { buildCollectionByKey } from '../../../util/iteratees';
import { getServerTime } from '../../../util/serverTime'; import { getServerTime } from '../../../util/serverTime';
import { buildAppConfig } from '../apiBuilders/appConfig'; import { buildAppConfig } from '../apiBuilders/appConfig';
import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { buildApiPhoto, buildPrivacyRules } from '../apiBuilders/common'; import { buildApiPhoto, buildPrivacyRules } from '../apiBuilders/common';
import { import {
buildApiConfig, buildApiConfig,
@ -34,16 +35,14 @@ import {
oldBuildLangPackString, oldBuildLangPackString,
} from '../apiBuilders/misc'; } from '../apiBuilders/misc';
import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers'; import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
import { buildApiUser } from '../apiBuilders/users';
import { import {
buildInputEntity, buildInputPeer, buildInputPhoto, buildInputEntity, buildInputPeer, buildInputPhoto,
buildInputPrivacyKey, buildInputPrivacyKey,
buildInputPrivacyRules, buildInputPrivacyRules,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { addEntitiesToLocalDb, addPhotoToLocalDb } from '../helpers'; import { addPhotoToLocalDb } from '../helpers';
import localDb from '../localDb'; import localDb from '../localDb';
import { getClient, invokeRequest, uploadFile } from './client'; import { getClient, invokeRequest, uploadFile } from './client';
import { ACCEPTABLE_USERNAME_ERRORS } from './management';
const BETA_LANG_CODES = ['ar', 'fa', 'id', 'ko', 'uz', 'en']; 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; if (!result) return undefined;
addEntitiesToLocalDb(result.users);
if (result.photo instanceof GramJs.Photo) { if (result.photo instanceof GramJs.Photo) {
addPhotoToLocalDb(result.photo); addPhotoToLocalDb(result.photo);
return { return {
users: result.users.map(buildApiUser).filter(Boolean),
photo: buildApiPhoto(result.photo), photo: buildApiPhoto(result.photo),
}; };
} }
@ -125,11 +122,9 @@ export async function uploadProfilePhoto(
if (!result) return undefined; if (!result) return undefined;
addEntitiesToLocalDb(result.users);
if (result.photo instanceof GramJs.Photo) { if (result.photo instanceof GramJs.Photo) {
addPhotoToLocalDb(result.photo); addPhotoToLocalDb(result.photo);
return { return {
users: result.users.map(buildApiUser).filter(Boolean),
photo: buildApiPhoto(result.photo), photo: buildApiPhoto(result.photo),
}; };
} }
@ -152,20 +147,14 @@ export async function uploadContactProfilePhoto({
if (!result) return undefined; if (!result) return undefined;
addEntitiesToLocalDb(result.users);
const users = result.users.map(buildApiUser).filter(Boolean);
if (result.photo instanceof GramJs.Photo) { if (result.photo instanceof GramJs.Photo) {
addPhotoToLocalDb(result.photo); addPhotoToLocalDb(result.photo);
return { return {
users,
photo: buildApiPhoto(result.photo), photo: buildApiPhoto(result.photo),
}; };
} }
return { return {
users,
photo: undefined, photo: undefined,
}; };
} }
@ -246,11 +235,7 @@ export async function fetchBlockedUsers({
return undefined; return undefined;
} }
updateLocalDb(result);
return { 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)), blockedIds: result.blocked.map((blocked) => getApiChatIdFromMtpPeer(blocked.peerId)),
totalCount: result instanceof GramJs.contacts.BlockedSlice ? result.count : result.blocked.length, totalCount: result instanceof GramJs.contacts.BlockedSlice ? result.count : result.blocked.length,
}; };
@ -307,10 +292,8 @@ export async function fetchWebAuthorizations() {
if (!result) { if (!result) {
return undefined; return undefined;
} }
addEntitiesToLocalDb(result.users);
return { return {
users: result.users.map(buildApiUser).filter(Boolean),
webAuthorizations: buildCollectionByKey(result.authorizations.map(buildApiWebSession), 'hash'), webAuthorizations: buildCollectionByKey(result.authorizations.map(buildApiWebSession), 'hash'),
}; };
} }
@ -334,8 +317,6 @@ export async function fetchNotificationExceptions() {
return undefined; return undefined;
} }
updateLocalDb(result);
return result.updates.reduce((acc, update) => { return result.updates.reduce((acc, update) => {
if (!(update instanceof GramJs.UpdateNotifySettings && update.peer instanceof GramJs.NotifyPeer)) { if (!(update instanceof GramJs.UpdateNotifySettings && update.peer instanceof GramJs.NotifyPeer)) {
return acc; return acc;
@ -555,10 +536,7 @@ export async function fetchPrivacySettings(privacyKey: ApiPrivacyKey) {
return undefined; return undefined;
} }
updateLocalDb(result);
return { return {
users: result.users.map(buildApiUser).filter(Boolean),
rules: buildPrivacyRules(result.rules), rules: buildPrivacyRules(result.rules),
}; };
} }
@ -595,10 +573,7 @@ export async function setPrivacySettings(
return undefined; return undefined;
} }
updateLocalDb(result);
return { return {
users: result.users.map(buildApiUser).filter(Boolean),
rules: buildPrivacyRules(result.rules), 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 }) { export async function fetchCountryList({ langCode = 'en' }: { langCode?: LangCode }) {
const countryList = await invokeRequest(new GramJs.help.GetCountriesList({ const countryList = await invokeRequest(new GramJs.help.GetCountriesList({
langCode, langCode,

View File

@ -2,11 +2,10 @@ import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { import type {
ApiChat, ApiMessagePublicForward, ApiPostStatistics, ApiStoryPublicForward, ApiUser, StatisticsGraph, ApiChat, ApiMessagePublicForward, ApiPostStatistics, ApiStoryPublicForward, StatisticsGraph,
} from '../../types'; } from '../../types';
import { STATISTICS_PUBLIC_FORWARDS_LIMIT } from '../../../config'; import { STATISTICS_PUBLIC_FORWARDS_LIMIT } from '../../../config';
import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { import {
buildChannelMonetizationStatistics, buildChannelMonetizationStatistics,
buildChannelStatistics, buildChannelStatistics,
@ -16,9 +15,7 @@ import {
buildPostsStatistics, buildPostsStatistics,
buildStoryPublicForwards, buildStoryPublicForwards,
} from '../apiBuilders/statistics'; } from '../apiBuilders/statistics';
import { buildApiUser } from '../apiBuilders/users';
import { buildInputEntity, buildInputPeer } from '../gramjsBuilders'; import { buildInputEntity, buildInputPeer } from '../gramjsBuilders';
import { addEntitiesToLocalDb } from '../helpers';
import { invokeRequest } from './client'; import { invokeRequest } from './client';
export async function fetchChannelStatistics({ export async function fetchChannelStatistics({
@ -69,10 +66,7 @@ export async function fetchGroupStatistics({
return undefined; return undefined;
} }
addEntitiesToLocalDb(result.users);
return { return {
users: result.users.map(buildApiUser).filter(Boolean),
stats: buildGroupStatistics(result), stats: buildGroupStatistics(result),
}; };
} }
@ -114,8 +108,6 @@ export async function fetchMessagePublicForwards({
forwards?: ApiMessagePublicForward[]; forwards?: ApiMessagePublicForward[];
count?: number; count?: number;
nextOffset?: string; nextOffset?: string;
chats: ApiChat[];
users: ApiUser[];
} | undefined> { } | undefined> {
const result = await invokeRequest(new GramJs.stats.GetMessagePublicForwards({ const result = await invokeRequest(new GramJs.stats.GetMessagePublicForwards({
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel, channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
@ -130,15 +122,10 @@ export async function fetchMessagePublicForwards({
return undefined; return undefined;
} }
addEntitiesToLocalDb(result.chats);
addEntitiesToLocalDb(result.users);
return { return {
forwards: buildMessagePublicForwards(result), forwards: buildMessagePublicForwards(result),
count: result.count, count: result.count,
nextOffset: result.nextOffset, 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; offset?: string;
}): Promise<{ }): Promise<{
publicForwards: (ApiMessagePublicForward | ApiStoryPublicForward)[] | undefined; publicForwards: (ApiMessagePublicForward | ApiStoryPublicForward)[] | undefined;
users: ApiUser[];
chats: ApiChat[];
count?: number; count?: number;
nextOffset?: string; nextOffset?: string;
} | undefined> { } | undefined> {
@ -220,13 +205,8 @@ export async function fetchStoryPublicForwards({
return undefined; return undefined;
} }
addEntitiesToLocalDb(result.chats);
addEntitiesToLocalDb(result.users);
return { return {
publicForwards: buildStoryPublicForwards(result), publicForwards: buildStoryPublicForwards(result),
users: result.users.map(buildApiUser).filter(Boolean),
chats: result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean),
count: result.count, count: result.count,
nextOffset: result.nextOffset, nextOffset: result.nextOffset,
}; };

View File

@ -2,19 +2,16 @@ import { Api as GramJs } from '../../../lib/gramjs';
import type { ApiInputPrivacyRules } from '../../../types'; import type { ApiInputPrivacyRules } from '../../../types';
import type { import type {
ApiChat,
ApiPeer, ApiPeer,
ApiPeerStories, ApiPeerStories,
ApiReaction, ApiReaction,
ApiReportReason, ApiReportReason,
ApiStealthMode, ApiStealthMode,
ApiTypeStory, ApiTypeStory,
ApiUser,
} from '../../types'; } from '../../types';
import { STORY_LIST_LIMIT } from '../../../config'; import { STORY_LIST_LIMIT } from '../../../config';
import { buildCollectionByCallback } from '../../../util/iteratees'; import { buildCollectionByCallback } from '../../../util/iteratees';
import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers'; import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
import { import {
buildApiPeerStories, buildApiPeerStories,
@ -23,14 +20,13 @@ import {
buildApiStoryView, buildApiStoryView,
buildApiStoryViews, buildApiStoryViews,
} from '../apiBuilders/stories'; } from '../apiBuilders/stories';
import { buildApiUser } from '../apiBuilders/users';
import { import {
buildInputPeer, buildInputPeer,
buildInputPrivacyRules, buildInputPrivacyRules,
buildInputReaction, buildInputReaction,
buildInputReportReason, buildInputReportReason,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { addEntitiesToLocalDb, addStoryToLocalDb } from '../helpers'; import { addStoryToLocalDb } from '../helpers';
import { invokeRequest } from './client'; import { invokeRequest } from './client';
export async function fetchAllStories({ export async function fetchAllStories({
@ -45,8 +41,6 @@ export async function fetchAllStories({
undefined undefined
| { state: string; stealthMode: ApiStealthMode } | { state: string; stealthMode: ApiStealthMode }
| { | {
users: ApiUser[];
chats: ApiChat[];
peerStories: Record<string, ApiPeerStories>; peerStories: Record<string, ApiPeerStories>;
hasMore?: true; hasMore?: true;
state: string; state: string;
@ -68,9 +62,6 @@ export async function fetchAllStories({
}; };
} }
addEntitiesToLocalDb(result.users);
addEntitiesToLocalDb(result.chats);
const allUserStories = result.peerStories.reduce<Record<string, ApiPeerStories>>((acc, peerStories) => { const allUserStories = result.peerStories.reduce<Record<string, ApiPeerStories>>((acc, peerStories) => {
const peerId = getApiChatIdFromMtpPeer(peerStories.peer); const peerId = getApiChatIdFromMtpPeer(peerStories.peer);
const stories = buildApiPeerStories(peerStories); const stories = buildApiPeerStories(peerStories);
@ -117,8 +108,6 @@ export async function fetchAllStories({
)); ));
return { return {
users: result.users.map(buildApiUser).filter(Boolean),
chats: result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean),
peerStories: allUserStories, peerStories: allUserStories,
hasMore: result.hasMore, hasMore: result.hasMore,
state: result.state, state: result.state,
@ -139,10 +128,6 @@ export async function fetchPeerStories({
return undefined; 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) => ( const stories = buildCollectionByCallback(result.stories.stories, (story) => (
[story.id, buildApiStory(peer.id, story)] [story.id, buildApiStory(peer.id, story)]
)); ));
@ -151,8 +136,6 @@ export async function fetchPeerStories({
result.stories.stories.forEach((story) => addStoryToLocalDb(story, peer.id)); result.stories.stories.forEach((story) => addStoryToLocalDb(story, peer.id));
return { return {
chats,
users,
stories, stories,
lastReadStoryId: result.stories.maxReadId, lastReadStoryId: result.stories.maxReadId,
}; };
@ -201,11 +184,6 @@ export async function fetchPeerStoriesByIds({ peer, ids }: { peer: ApiPeer; ids:
return undefined; 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<Record<string, ApiTypeStory>>((acc, id) => { const stories = ids.reduce<Record<string, ApiTypeStory>>((acc, id) => {
const story = result.stories.find(({ id: currentId }) => currentId === id); const story = result.stories.find(({ id: currentId }) => currentId === id);
if (story) { if (story) {
@ -225,8 +203,6 @@ export async function fetchPeerStoriesByIds({ peer, ids }: { peer: ApiPeer; ids:
result.stories.forEach((story) => addStoryToLocalDb(story, peer.id)); result.stories.forEach((story) => addStoryToLocalDb(story, peer.id));
return { return {
chats,
users,
pinnedIds: result.pinnedToTop, pinnedIds: result.pinnedToTop,
stories, stories,
}; };
@ -307,15 +283,9 @@ export async function fetchStoryViewList({
return undefined; 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); const views = result.views.map(buildApiStoryView).filter(Boolean);
return { return {
users,
chats,
views, views,
nextOffset: result.nextOffset, nextOffset: result.nextOffset,
reactionsCount: result.reactionsCount, reactionsCount: result.reactionsCount,
@ -339,14 +309,10 @@ export async function fetchStoriesViews({
return undefined; return undefined;
} }
addEntitiesToLocalDb(result.users);
const views = buildApiStoryViews(result.views[0]); const views = buildApiStoryViews(result.views[0]);
const users = result.users.map(buildApiUser).filter(Boolean);
return { return {
views, views,
users,
}; };
} }
@ -430,11 +396,6 @@ async function fetchCommonStoriesRequest({ method, peerId }: {
return undefined; 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) => ( const stories = buildCollectionByCallback(result.stories, (story) => (
[story.id, buildApiStory(peerId, story)] [story.id, buildApiStory(peerId, story)]
)); ));
@ -443,8 +404,6 @@ async function fetchCommonStoriesRequest({ method, peerId }: {
result.stories.forEach((story) => addStoryToLocalDb(story, peerId)); result.stories.forEach((story) => addStoryToLocalDb(story, peerId));
return { return {
users,
chats,
stories, stories,
pinnedIds: result.pinnedToTop, pinnedIds: result.pinnedToTop,
}; };

View File

@ -2,7 +2,7 @@ import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { import type {
ApiSticker, ApiStickerSetInfo, ApiVideo, OnApiUpdate, ApiSticker, ApiStickerSetInfo, ApiVideo,
} from '../../types'; } from '../../types';
import { DEFAULT_GIF_SEARCH_BOT_USERNAME, RECENT_STATUS_LIMIT, RECENT_STICKERS_LIMIT } from '../../../config'; import { DEFAULT_GIF_SEARCH_BOT_USERNAME, RECENT_STATUS_LIMIT, RECENT_STICKERS_LIMIT } from '../../../config';
@ -13,14 +13,9 @@ import {
} from '../apiBuilders/symbols'; } from '../apiBuilders/symbols';
import { buildInputDocument, buildInputStickerSet, buildInputStickerSetShortName } from '../gramjsBuilders'; import { buildInputDocument, buildInputStickerSet, buildInputStickerSetShortName } from '../gramjsBuilders';
import localDb from '../localDb'; import localDb from '../localDb';
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { invokeRequest } from './client'; import { invokeRequest } from './client';
let onUpdate: OnApiUpdate;
export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
export async function fetchCustomEmojiSets({ hash = '0' }: { hash?: string }) { export async function fetchCustomEmojiSets({ hash = '0' }: { hash?: string }) {
const allStickers = await invokeRequest(new GramJs.messages.GetEmojiStickers({ hash: BigInt(hash) })); const allStickers = await invokeRequest(new GramJs.messages.GetEmojiStickers({ hash: BigInt(hash) }));
@ -132,7 +127,7 @@ export async function faveSticker({
const result = await invokeRequest(request); const result = await invokeRequest(request);
if (result) { if (result) {
onUpdate({ sendApiUpdate({
'@type': 'updateFavoriteStickers', '@type': 'updateFavoriteStickers',
}); });
} }
@ -325,7 +320,7 @@ export async function installStickerSet({ stickerSetId, accessHash }: { stickerS
})); }));
if (result) { if (result) {
onUpdate({ sendApiUpdate({
'@type': 'updateStickerSet', '@type': 'updateStickerSet',
id: stickerSetId, id: stickerSetId,
stickerSet: { installedDate: Date.now() }, stickerSet: { installedDate: Date.now() },
@ -339,7 +334,7 @@ export async function uninstallStickerSet({ stickerSetId, accessHash }: { sticke
})); }));
if (result) { if (result) {
onUpdate({ sendApiUpdate({
'@type': 'updateStickerSet', '@type': 'updateStickerSet',
id: stickerSetId, id: stickerSetId,
stickerSet: { installedDate: undefined }, stickerSet: { installedDate: undefined },

View File

@ -1,8 +1,7 @@
import { Api as GramJs, errors } from '../../../lib/gramjs'; import { Api as GramJs, errors } from '../../../lib/gramjs';
import type { OnApiUpdate } from '../../types';
import { DEBUG } from '../../../config'; import { DEBUG } from '../../../config';
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { getTmpPassword, invokeRequest, updateTwoFaSettings } from './client'; import { getTmpPassword, invokeRequest, updateTwoFaSettings } from './client';
const ApiErrors: { [k: string]: string } = { const ApiErrors: { [k: string]: string } = {
@ -19,12 +18,6 @@ const emailCodeController: {
reject?: Function; reject?: Function;
} = {}; } = {};
let onUpdate: OnApiUpdate;
export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
export async function getPasswordInfo() { export async function getPasswordInfo() {
const result = await invokeRequest(new GramJs.account.GetPassword()); const result = await invokeRequest(new GramJs.account.GetPassword());
if (!result) { if (!result) {
@ -37,7 +30,7 @@ export async function getPasswordInfo() {
} }
function onRequestEmailCode(length: number) { function onRequestEmailCode(length: number) {
onUpdate({ sendApiUpdate({
'@type': 'updateTwoFaStateWaitCode', '@type': 'updateTwoFaStateWaitCode',
length, length,
}); });
@ -136,7 +129,7 @@ function onError(err: Error) {
} }
} }
onUpdate({ sendApiUpdate({
'@type': 'updateTwoFaError', '@type': 'updateTwoFaError',
message, message,
}); });

View File

@ -2,15 +2,14 @@ import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { import type {
ApiChat, ApiPeer, ApiSticker, ApiChat, ApiPeer, ApiSticker, ApiUser,
ApiUser, OnApiUpdate,
} from '../../types'; } from '../../types';
import { COMMON_CHATS_LIMIT } from '../../../config'; import { COMMON_CHATS_LIMIT } from '../../../config';
import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { buildApiPhoto } from '../apiBuilders/common'; import { buildApiPhoto } from '../apiBuilders/common';
import { buildApiPeerId } from '../apiBuilders/peers'; import { buildApiPeerId } from '../apiBuilders/peers';
import { buildApiUser, buildApiUserFullInfo, buildApiUsersAndStatuses } from '../apiBuilders/users'; import { buildApiUser, buildApiUserFullInfo, buildApiUserStatuses } from '../apiBuilders/users';
import { import {
buildInputContact, buildInputContact,
buildInputEmojiStatus, buildInputEmojiStatus,
@ -19,17 +18,12 @@ import {
buildMtpPeerId, buildMtpPeerId,
getEntityTypeById, getEntityTypeById,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { addEntitiesToLocalDb, addPhotoToLocalDb, addUserToLocalDb } from '../helpers'; import { addPhotoToLocalDb, addUserToLocalDb } from '../helpers';
import localDb from '../localDb'; import localDb from '../localDb';
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { invokeRequest } from './client'; import { invokeRequest } from './client';
import { searchMessagesInChat } from './messages'; import { searchMessagesInChat } from './messages';
let onUpdate: OnApiUpdate;
export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
export async function fetchFullUser({ export async function fetchFullUser({
id, id,
accessHash, accessHash,
@ -48,10 +42,6 @@ export async function fetchFullUser({
return undefined; return undefined;
} }
updateLocalDb(result);
addEntitiesToLocalDb(result.users);
addEntitiesToLocalDb(result.chats);
if (result.fullUser.profilePhoto) { if (result.fullUser.profilePhoto) {
addPhotoToLocalDb(result.fullUser.profilePhoto); addPhotoToLocalDb(result.fullUser.profilePhoto);
} }
@ -82,7 +72,7 @@ export async function fetchFullUser({
const user = users.find(({ id: userId }) => userId === id)!; const user = users.find(({ id: userId }) => userId === id)!;
onUpdate({ sendApiUpdate({
'@type': 'updateUser', '@type': 'updateUser',
id, id,
user, user,
@ -108,21 +98,10 @@ export async function fetchCommonChats(id: string, accessHash?: string, maxId?:
return undefined; return undefined;
} }
updateLocalDb(commonChats); const chats = commonChats.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
const chatIds = chats.map(({ id: chatId }) => chatId);
const chatIds: string[] = []; return { chatIds, isFullyLoaded: chatIds.length < COMMON_CHATS_LIMIT };
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 };
} }
export async function fetchNearestCountry() { export async function fetchNearestCountry() {
@ -144,7 +123,6 @@ export async function fetchTopUsers() {
return { return {
ids, ids,
users,
}; };
} }
@ -154,14 +132,12 @@ export async function fetchContactList() {
return undefined; return undefined;
} }
addEntitiesToLocalDb(result.users); const users = result.users.map(buildApiUser).filter(Boolean) as ApiUser[];
const userStatusesById = buildApiUserStatuses(result.users);
const { users, userStatusesById } = buildApiUsersAndStatuses(result.users);
return { return {
users, users,
userStatusesById, userStatusesById,
chats: result.users.map((user) => buildApiChatFromPreview(user)).filter(Boolean),
}; };
} }
@ -173,9 +149,13 @@ export async function fetchUsers({ users }: { users: ApiUser[] }) {
return undefined; 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({ export async function importContact({
@ -246,7 +226,7 @@ export async function deleteContact({
return; return;
} }
onUpdate({ sendApiUpdate({
'@type': 'deleteContact', '@type': 'deleteContact',
id, id,
}); });
@ -277,7 +257,7 @@ export async function fetchProfilePhotos({
return undefined; return undefined;
} }
updateLocalDb(result); result.photos.forEach(addPhotoToLocalDb);
const count = result instanceof GramJs.photos.PhotosSlice ? result.count : result.photos.length; const count = result instanceof GramJs.photos.PhotosSlice ? result.count : result.photos.length;
const proposedNextOffsetId = offset + result.photos.length; const proposedNextOffsetId = offset + result.photos.length;
@ -288,7 +268,6 @@ export async function fetchProfilePhotos({
photos: result.photos photos: result.photos
.filter((photo): photo is GramJs.Photo => photo instanceof GramJs.Photo) .filter((photo): photo is GramJs.Photo => photo instanceof GramJs.Photo)
.map((photo) => buildApiPhoto(photo)), .map((photo) => buildApiPhoto(photo)),
users: result.users.map(buildApiUser).filter(Boolean),
nextOffsetId, nextOffsetId,
}; };
} }
@ -306,13 +285,12 @@ export async function fetchProfilePhotos({
} }
const { const {
messages, users, totalCount, nextOffsetId, messages, totalCount, nextOffsetId,
} = result; } = result;
return { return {
count: totalCount, count: totalCount,
photos: messages.map((message) => message.content.action!.photo).filter(Boolean), photos: messages.map((message) => message.content.action!.photo).filter(Boolean),
users,
nextOffsetId, nextOffsetId,
}; };
} }
@ -342,17 +320,3 @@ export function saveCloseFriends(userIds: string[]) {
shouldReturnTrue: true, 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);
}
}

View File

@ -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);
}

View File

@ -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<string, ApiUser> | undefined;
let chatById: Record<string, ApiChat> | 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,
});
}

View File

@ -3,11 +3,13 @@ import { Api as GramJs, connection } from '../../../lib/gramjs';
import type { GroupCallConnectionData } from '../../../lib/secret-sauce'; import type { GroupCallConnectionData } from '../../../lib/secret-sauce';
import type { import type {
ApiMessage, ApiStory, ApiStorySkipped, ApiMessage, ApiStory, ApiStorySkipped,
ApiUpdate, ApiUpdateConnectionStateType, OnApiUpdate, ApiUpdateConnectionStateType,
} from '../../types'; } from '../../types';
import { DEBUG, GENERAL_TOPIC_ID } from '../../../config'; 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 { getServerTimeOffset, setServerTimeOffset } from '../../../util/serverTime';
import { buildApiBotMenuButton } from '../apiBuilders/bots'; import { buildApiBotMenuButton } from '../apiBuilders/bots';
import { import {
@ -40,7 +42,6 @@ import {
buildApiMessageFromShort, buildApiMessageFromShort,
buildApiMessageFromShortChat, buildApiMessageFromShortChat,
buildApiQuickReply, buildApiQuickReply,
buildApiThreadInfoFromMessage,
buildMessageDraft, buildMessageDraft,
} from '../apiBuilders/messages'; } from '../apiBuilders/messages';
import { import {
@ -64,8 +65,6 @@ import {
buildMessageFromUpdate, buildMessageFromUpdate,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { import {
addEntitiesToLocalDb,
addMessageToLocalDb,
addPhotoToLocalDb, addPhotoToLocalDb,
addStoryToLocalDb, addStoryToLocalDb,
isChatFolder, isChatFolder,
@ -75,6 +74,8 @@ import {
} from '../helpers'; } from '../helpers';
import localDb from '../localDb'; import localDb from '../localDb';
import { scheduleMutedChatUpdate, scheduleMutedTopicUpdate } from '../scheduleUnmute'; import { scheduleMutedChatUpdate, scheduleMutedTopicUpdate } from '../scheduleUnmute';
import { sendApiUpdate } from './apiUpdateEmitter';
import { processMessageAndUpdateThreadInfo } from './entityProcessor';
import LocalUpdatePremiumFloodWait from './UpdatePremiumFloodWait'; import LocalUpdatePremiumFloodWait from './UpdatePremiumFloodWait';
import { LocalUpdateChannelPts, LocalUpdatePts, type UpdatePts } from './UpdatePts'; import { LocalUpdateChannelPts, LocalUpdatePts, type UpdatePts } from './UpdatePts';
@ -83,68 +84,13 @@ export type Update = (
(GramJs.TypeUpdate | GramJs.TypeUpdates) & { _entities?: (GramJs.TypeUser | GramJs.TypeChat)[] } (GramJs.TypeUpdate | GramJs.TypeUpdates) & { _entities?: (GramJs.TypeUser | GramJs.TypeChat)[] }
) | typeof connection.UpdateConnectionState | UpdatePts | LocalUpdatePremiumFloodWait; ) | typeof connection.UpdateConnectionState | UpdatePts | LocalUpdatePremiumFloodWait;
let onUpdate: OnApiUpdate;
export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
const sentMessageIds = new Set(); 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) { export function updater(update: Update) {
if (update instanceof connection.UpdateServerTimeOffset) { if (update instanceof connection.UpdateServerTimeOffset) {
setServerTimeOffset(update.timeOffset); setServerTimeOffset(update.timeOffset);
onUpdate({ sendApiUpdate({
'@type': 'updateServerTimeOffset', '@type': 'updateServerTimeOffset',
serverTimeOffset: update.timeOffset, serverTimeOffset: update.timeOffset,
}); });
@ -164,7 +110,7 @@ export function updater(update: Update) {
break; break;
} }
onUpdate({ sendApiUpdate({
'@type': 'updateConnectionState', '@type': 'updateConnectionState',
connectionState, connectionState,
}); });
@ -180,13 +126,6 @@ export function updater(update: Update) {
let message: ApiMessage | undefined; let message: ApiMessage | undefined;
let shouldForceReply: boolean | 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) { if (update instanceof GramJs.UpdateShortChatMessage) {
message = buildApiMessageFromShortChat(update); message = buildApiMessageFromShortChat(update);
} else if (update instanceof GramJs.UpdateShortMessage) { } else if (update instanceof GramJs.UpdateShortMessage) {
@ -202,10 +141,9 @@ export function updater(update: Update) {
return; return;
} }
addMessageToLocalDb(update.message); processMessageAndUpdateThreadInfo(update.message);
message = buildApiMessage(update.message)!; message = buildApiMessage(update.message)!;
dispatchThreadInfoUpdates([update.message]);
shouldForceReply = 'replyMarkup' in update.message shouldForceReply = 'replyMarkup' in update.message
&& update.message?.replyMarkup instanceof GramJs.ReplyKeyboardForceReply && update.message?.replyMarkup instanceof GramJs.ReplyKeyboardForceReply
@ -213,7 +151,7 @@ export function updater(update: Update) {
} }
if (update instanceof GramJs.UpdateNewScheduledMessage) { if (update instanceof GramJs.UpdateNewScheduledMessage) {
onUpdate({ sendApiUpdate({
'@type': sentMessageIds.has(message.id) ? 'updateScheduledMessage' : 'newScheduledMessage', '@type': sentMessageIds.has(message.id) ? 'updateScheduledMessage' : 'newScheduledMessage',
id: message.id, id: message.id,
chatId: message.chatId, chatId: message.chatId,
@ -222,7 +160,7 @@ export function updater(update: Update) {
} else { } else {
// We don't have preview for action or 'via bot' messages, so `newMessage` update here is required // 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; const hasLocalCopy = sentMessageIds.has(message.id) && !message.viaBotId && !message.content.action;
onUpdate({ sendApiUpdate({
'@type': hasLocalCopy ? 'updateMessage' : 'newMessage', '@type': hasLocalCopy ? 'updateMessage' : 'newMessage',
id: message.id, id: message.id,
chatId: message.chatId, chatId: message.chatId,
@ -237,7 +175,7 @@ export function updater(update: Update) {
const { action } = update.message; const { action } = update.message;
if (action instanceof GramJs.MessageActionChatEditTitle) { if (action instanceof GramJs.MessageActionChatEditTitle) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: message.chatId, id: message.chatId,
chat: { chat: {
@ -256,7 +194,7 @@ export function updater(update: Update) {
} }
addPhotoToLocalDb(action.photo); addPhotoToLocalDb(action.photo);
onUpdate({ sendApiUpdate({
'@type': 'updateNewProfilePhoto', '@type': 'updateNewProfilePhoto',
peerId: message.chatId, peerId: message.chatId,
photo: apiPhoto, photo: apiPhoto,
@ -267,7 +205,7 @@ export function updater(update: Update) {
localDb.chats[localDbChatId].photo = new GramJs.ChatPhotoEmpty(); localDb.chats[localDbChatId].photo = new GramJs.ChatPhotoEmpty();
} }
onUpdate({ sendApiUpdate({
'@type': 'updateDeleteProfilePhoto', '@type': 'updateDeleteProfilePhoto',
peerId: message.chatId, peerId: message.chatId,
}); });
@ -276,7 +214,7 @@ export function updater(update: Update) {
if (update._entities && update._entities.some((e): e is GramJs.User => ( if (update._entities && update._entities.some((e): e is GramJs.User => (
e instanceof GramJs.User && Boolean(e.self) && e.id === action.userId e instanceof GramJs.User && Boolean(e.self) && e.id === action.userId
))) { ))) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: message.chatId, id: message.chatId,
chat: { chat: {
@ -290,14 +228,14 @@ export function updater(update: Update) {
if (update._entities && update._entities.some((e): e is GramJs.User => ( if (update._entities && update._entities.some((e): e is GramJs.User => (
e instanceof GramJs.User && Boolean(e.self) && action.users.includes(e.id) e instanceof GramJs.User && Boolean(e.self) && action.users.includes(e.id)
))) { ))) {
onUpdate({ sendApiUpdate({
'@type': 'updateChatJoin', '@type': 'updateChatJoin',
id: message.chatId, id: message.chatId,
}); });
} }
} else if (action instanceof GramJs.MessageActionGroupCall) { } else if (action instanceof GramJs.MessageActionGroupCall) {
if (!action.duration && action.call) { if (!action.duration && action.call) {
onUpdate({ sendApiUpdate({
'@type': 'updateGroupCallChatId', '@type': 'updateGroupCallChatId',
chatId: message.chatId, chatId: message.chatId,
call: { call: {
@ -315,13 +253,13 @@ export function updater(update: Update) {
} = replyTo || {}; } = replyTo || {};
const topicId = !isTopicReply ? GENERAL_TOPIC_ID : replyToTopId || replyToMsgId || GENERAL_TOPIC_ID; const topicId = !isTopicReply ? GENERAL_TOPIC_ID : replyToTopId || replyToMsgId || GENERAL_TOPIC_ID;
onUpdate({ sendApiUpdate({
'@type': 'updateTopic', '@type': 'updateTopic',
chatId: getApiChatIdFromMtpPeer(update.message.peerId!), chatId: getApiChatIdFromMtpPeer(update.message.peerId!),
topicId, topicId,
}); });
} else if (action instanceof GramJs.MessageActionTopicCreate) { } else if (action instanceof GramJs.MessageActionTopicCreate) {
onUpdate({ sendApiUpdate({
'@type': 'updateTopics', '@type': 'updateTopics',
chatId: getApiChatIdFromMtpPeer(update.message.peerId!), chatId: getApiChatIdFromMtpPeer(update.message.peerId!),
}); });
@ -331,31 +269,31 @@ export function updater(update: Update) {
const message = buildApiMessage(update.message); const message = buildApiMessage(update.message);
if (!message) return; if (!message) return;
onUpdate({ sendApiUpdate({
'@type': 'updateQuickReplyMessage', '@type': 'updateQuickReplyMessage',
id: message.id, id: message.id,
message, message,
}); });
} else if (update instanceof GramJs.UpdateDeleteQuickReplyMessages) { } else if (update instanceof GramJs.UpdateDeleteQuickReplyMessages) {
onUpdate({ sendApiUpdate({
'@type': 'deleteQuickReplyMessages', '@type': 'deleteQuickReplyMessages',
quickReplyId: update.shortcutId, quickReplyId: update.shortcutId,
messageIds: update.messages, messageIds: update.messages,
}); });
} else if (update instanceof GramJs.UpdateQuickReplies) { } else if (update instanceof GramJs.UpdateQuickReplies) {
const quickReplies = update.quickReplies.map(buildApiQuickReply); const quickReplies = update.quickReplies.map(buildApiQuickReply);
onUpdate({ sendApiUpdate({
'@type': 'updateQuickReplies', '@type': 'updateQuickReplies',
quickReplies, quickReplies,
}); });
} else if (update instanceof GramJs.UpdateNewQuickReply) { } else if (update instanceof GramJs.UpdateNewQuickReply) {
const quickReply = buildApiQuickReply(update.quickReply); const quickReply = buildApiQuickReply(update.quickReply);
onUpdate({ sendApiUpdate({
'@type': 'updateQuickReplies', '@type': 'updateQuickReplies',
quickReplies: [quickReply], quickReplies: [quickReply],
}); });
} else if (update instanceof GramJs.UpdateDeleteQuickReply) { } else if (update instanceof GramJs.UpdateDeleteQuickReply) {
onUpdate({ sendApiUpdate({
'@type': 'deleteQuickReply', '@type': 'deleteQuickReply',
quickReplyId: update.shortcutId, quickReplyId: update.shortcutId,
}); });
@ -373,20 +311,19 @@ export function updater(update: Update) {
return; return;
} }
addMessageToLocalDb(update.message); processMessageAndUpdateThreadInfo(update.message);
// Workaround for a weird server behavior when own message is marked as incoming // Workaround for a weird server behavior when own message is marked as incoming
const message = omit(buildApiMessage(update.message)!, ['isOutgoing']); const message = omit(buildApiMessage(update.message)!, ['isOutgoing']);
dispatchThreadInfoUpdates([update.message]);
onUpdate({ sendApiUpdate({
'@type': 'updateMessage', '@type': 'updateMessage',
id: message.id, id: message.id,
chatId: message.chatId, chatId: message.chatId,
message, message,
}); });
} else if (update instanceof GramJs.UpdateMessageReactions) { } else if (update instanceof GramJs.UpdateMessageReactions) {
onUpdate({ sendApiUpdate({
'@type': 'updateMessageReactions', '@type': 'updateMessageReactions',
id: update.msgId, id: update.msgId,
chatId: getApiChatIdFromMtpPeer(update.peer), chatId: getApiChatIdFromMtpPeer(update.peer),
@ -400,7 +337,7 @@ export function updater(update: Update) {
if (!boughtMedia?.length) return; if (!boughtMedia?.length) return;
onUpdate({ sendApiUpdate({
'@type': 'updateMessageExtendedMedia', '@type': 'updateMessageExtendedMedia',
id: update.msgId, id: update.msgId,
chatId, chatId,
@ -417,19 +354,19 @@ export function updater(update: Update) {
if (!previewMedia?.length) return; if (!previewMedia?.length) return;
onUpdate({ sendApiUpdate({
'@type': 'updateMessageExtendedMedia', '@type': 'updateMessageExtendedMedia',
id: update.msgId, id: update.msgId,
chatId, chatId,
extendedMedia: previewMedia, extendedMedia: previewMedia,
}); });
} else if (update instanceof GramJs.UpdateDeleteMessages) { } else if (update instanceof GramJs.UpdateDeleteMessages) {
onUpdate({ sendApiUpdate({
'@type': 'deleteMessages', '@type': 'deleteMessages',
ids: update.messages, ids: update.messages,
}); });
} else if (update instanceof GramJs.UpdateDeleteScheduledMessages) { } else if (update instanceof GramJs.UpdateDeleteScheduledMessages) {
onUpdate({ sendApiUpdate({
'@type': 'deleteScheduledMessages', '@type': 'deleteScheduledMessages',
ids: update.messages, ids: update.messages,
chatId: getApiChatIdFromMtpPeer(update.peer), chatId: getApiChatIdFromMtpPeer(update.peer),
@ -437,14 +374,14 @@ export function updater(update: Update) {
} else if (update instanceof GramJs.UpdateDeleteChannelMessages) { } else if (update instanceof GramJs.UpdateDeleteChannelMessages) {
const chatId = buildApiPeerId(update.channelId, 'channel'); const chatId = buildApiPeerId(update.channelId, 'channel');
onUpdate({ sendApiUpdate({
'@type': 'deleteMessages', '@type': 'deleteMessages',
ids: update.messages, ids: update.messages,
chatId, chatId,
}); });
} else if (update instanceof GramJs.UpdateServiceNotification) { } else if (update instanceof GramJs.UpdateServiceNotification) {
if (update.popup) { if (update.popup) {
onUpdate({ sendApiUpdate({
'@type': 'error', '@type': 'error',
error: { error: {
message: update.message, message: update.message,
@ -454,9 +391,9 @@ export function updater(update: Update) {
const currentDate = Date.now() / 1000 + getServerTimeOffset(); const currentDate = Date.now() / 1000 + getServerTimeOffset();
const message = buildApiMessageFromNotification(update, currentDate); const message = buildApiMessageFromNotification(update, currentDate);
addMessageToLocalDb(buildMessageFromUpdate(message.id, message.chatId, update)); processMessageAndUpdateThreadInfo(buildMessageFromUpdate(message.id, message.chatId, update));
onUpdate({ sendApiUpdate({
'@type': 'updateServiceNotification', '@type': 'updateServiceNotification',
message, message,
}); });
@ -464,7 +401,7 @@ export function updater(update: Update) {
} else if (update instanceof GramJs.UpdateMessageID || update instanceof GramJs.UpdateShortSentMessage) { } else if (update instanceof GramJs.UpdateMessageID || update instanceof GramJs.UpdateShortSentMessage) {
sentMessageIds.add(update.id); sentMessageIds.add(update.id);
} else if (update instanceof GramJs.UpdateReadMessagesContents) { } else if (update instanceof GramJs.UpdateReadMessagesContents) {
onUpdate({ sendApiUpdate({
'@type': 'updateCommonBoxMessages', '@type': 'updateCommonBoxMessages',
ids: update.messages, ids: update.messages,
messageUpdate: { messageUpdate: {
@ -473,7 +410,7 @@ export function updater(update: Update) {
}, },
}); });
} else if (update instanceof GramJs.UpdateChannelReadMessagesContents) { } else if (update instanceof GramJs.UpdateChannelReadMessagesContents) {
onUpdate({ sendApiUpdate({
'@type': 'updateChannelMessages', '@type': 'updateChannelMessages',
channelId: buildApiPeerId(update.channelId, 'channel'), channelId: buildApiPeerId(update.channelId, 'channel'),
ids: update.messages, ids: update.messages,
@ -487,28 +424,28 @@ export function updater(update: Update) {
if (poll) { if (poll) {
const apiPoll = buildPoll(poll, results); const apiPoll = buildPoll(poll, results);
onUpdate({ sendApiUpdate({
'@type': 'updateMessagePoll', '@type': 'updateMessagePoll',
pollId: String(pollId), pollId: String(pollId),
pollUpdate: apiPoll, pollUpdate: apiPoll,
}); });
} else { } else {
const pollResults = buildPollResults(results); const pollResults = buildPollResults(results);
onUpdate({ sendApiUpdate({
'@type': 'updateMessagePoll', '@type': 'updateMessagePoll',
pollId: String(pollId), pollId: String(pollId),
pollUpdate: { results: pollResults }, pollUpdate: { results: pollResults },
}); });
} }
} else if (update instanceof GramJs.UpdateMessagePollVote) { } else if (update instanceof GramJs.UpdateMessagePollVote) {
onUpdate({ sendApiUpdate({
'@type': 'updateMessagePollVote', '@type': 'updateMessagePollVote',
pollId: String(update.pollId), pollId: String(update.pollId),
peerId: getApiChatIdFromMtpPeer(update.peer), peerId: getApiChatIdFromMtpPeer(update.peer),
options: update.options.map(serializeBytes), options: update.options.map(serializeBytes),
}); });
} else if (update instanceof GramJs.UpdateChannelMessageViews) { } else if (update instanceof GramJs.UpdateChannelMessageViews) {
onUpdate({ sendApiUpdate({
'@type': 'updateMessage', '@type': 'updateMessage',
chatId: buildApiPeerId(update.channelId, 'channel'), chatId: buildApiPeerId(update.channelId, 'channel'),
id: update.id, id: update.id,
@ -517,7 +454,7 @@ export function updater(update: Update) {
// Chats // Chats
} else if (update instanceof GramJs.UpdateReadHistoryInbox) { } else if (update instanceof GramJs.UpdateReadHistoryInbox) {
onUpdate({ sendApiUpdate({
'@type': 'updateChatInbox', '@type': 'updateChatInbox',
id: getApiChatIdFromMtpPeer(update.peer), id: getApiChatIdFromMtpPeer(update.peer),
chat: { chat: {
@ -526,7 +463,7 @@ export function updater(update: Update) {
}, },
}); });
} else if (update instanceof GramJs.UpdateReadHistoryOutbox) { } else if (update instanceof GramJs.UpdateReadHistoryOutbox) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: getApiChatIdFromMtpPeer(update.peer), id: getApiChatIdFromMtpPeer(update.peer),
chat: { chat: {
@ -534,7 +471,7 @@ export function updater(update: Update) {
}, },
}); });
} else if (update instanceof GramJs.UpdateReadChannelInbox) { } else if (update instanceof GramJs.UpdateReadChannelInbox) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: buildApiPeerId(update.channelId, 'channel'), id: buildApiPeerId(update.channelId, 'channel'),
chat: { chat: {
@ -543,7 +480,7 @@ export function updater(update: Update) {
}, },
}); });
} else if (update instanceof GramJs.UpdateReadChannelOutbox) { } else if (update instanceof GramJs.UpdateReadChannelOutbox) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: buildApiPeerId(update.channelId, 'channel'), id: buildApiPeerId(update.channelId, 'channel'),
chat: { chat: {
@ -551,16 +488,16 @@ export function updater(update: Update) {
}, },
}); });
} else if (update instanceof GramJs.UpdateReadChannelDiscussionInbox) { } else if (update instanceof GramJs.UpdateReadChannelDiscussionInbox) {
onUpdate({ sendApiUpdate({
'@type': 'updateThreadInfos', '@type': 'updateThreadInfo',
threadInfoUpdates: [{ threadInfo: {
chatId: buildApiPeerId(update.channelId, 'channel'), chatId: buildApiPeerId(update.channelId, 'channel'),
threadId: update.topMsgId, threadId: update.topMsgId,
lastReadInboxMessageId: update.readMaxId, lastReadInboxMessageId: update.readMaxId,
}], },
}); });
} else if (update instanceof GramJs.UpdateReadChannelDiscussionOutbox) { } else if (update instanceof GramJs.UpdateReadChannelDiscussionOutbox) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: buildApiPeerId(update.channelId, 'channel'), id: buildApiPeerId(update.channelId, 'channel'),
chat: { chat: {
@ -571,7 +508,7 @@ export function updater(update: Update) {
update instanceof GramJs.UpdateDialogPinned update instanceof GramJs.UpdateDialogPinned
&& update.peer instanceof GramJs.DialogPeer && update.peer instanceof GramJs.DialogPeer
) { ) {
onUpdate({ sendApiUpdate({
'@type': 'updateChatPinned', '@type': 'updateChatPinned',
id: getApiChatIdFromMtpPeer(update.peer.peer), id: getApiChatIdFromMtpPeer(update.peer.peer),
isPinned: update.pinned || false, isPinned: update.pinned || false,
@ -583,7 +520,7 @@ export function updater(update: Update) {
.map((dp) => getApiChatIdFromMtpPeer(dp.peer)) .map((dp) => getApiChatIdFromMtpPeer(dp.peer))
: []; : [];
onUpdate({ sendApiUpdate({
'@type': 'updatePinnedChatIds', '@type': 'updatePinnedChatIds',
ids, ids,
folderId: update.folderId || undefined, folderId: update.folderId || undefined,
@ -592,7 +529,7 @@ export function updater(update: Update) {
update instanceof GramJs.UpdateSavedDialogPinned update instanceof GramJs.UpdateSavedDialogPinned
&& update.peer instanceof GramJs.DialogPeer && update.peer instanceof GramJs.DialogPeer
) { ) {
onUpdate({ sendApiUpdate({
'@type': 'updateSavedDialogPinned', '@type': 'updateSavedDialogPinned',
id: getApiChatIdFromMtpPeer(update.peer.peer), id: getApiChatIdFromMtpPeer(update.peer.peer),
isPinned: update.pinned || false, isPinned: update.pinned || false,
@ -604,7 +541,7 @@ export function updater(update: Update) {
.map((dp) => getApiChatIdFromMtpPeer(dp.peer)) .map((dp) => getApiChatIdFromMtpPeer(dp.peer))
: []; : [];
onUpdate({ sendApiUpdate({
'@type': 'updatePinnedSavedDialogIds', '@type': 'updatePinnedSavedDialogIds',
ids, ids,
}); });
@ -612,7 +549,7 @@ export function updater(update: Update) {
update.folderPeers.forEach((folderPeer) => { update.folderPeers.forEach((folderPeer) => {
const { folderId, peer } = folderPeer; const { folderId, peer } = folderPeer;
onUpdate({ sendApiUpdate({
'@type': 'updateChatListType', '@type': 'updateChatListType',
id: getApiChatIdFromMtpPeer(peer), id: getApiChatIdFromMtpPeer(peer),
folderId, folderId,
@ -622,20 +559,20 @@ export function updater(update: Update) {
const { id, filter } = update; const { id, filter } = update;
const folder = isChatFolder(filter) ? buildApiChatFolder(filter) : undefined; const folder = isChatFolder(filter) ? buildApiChatFolder(filter) : undefined;
onUpdate({ sendApiUpdate({
'@type': 'updateChatFolder', '@type': 'updateChatFolder',
id, id,
folder, folder,
}); });
} else if (update instanceof GramJs.UpdateDialogFilterOrder) { } else if (update instanceof GramJs.UpdateDialogFilterOrder) {
onUpdate({ sendApiUpdate({
'@type': 'updateChatFoldersOrder', '@type': 'updateChatFoldersOrder',
orderedIds: update.order, orderedIds: update.order,
}); });
} else if (update instanceof GramJs.UpdateChatParticipants) { } else if (update instanceof GramJs.UpdateChatParticipants) {
const replacedMembers = buildChatMembers(update.participants); const replacedMembers = buildChatMembers(update.participants);
onUpdate({ sendApiUpdate({
'@type': 'updateChatMembers', '@type': 'updateChatMembers',
id: buildApiPeerId(update.participants.chatId, 'chat'), id: buildApiPeerId(update.participants.chatId, 'chat'),
replacedMembers, replacedMembers,
@ -645,13 +582,13 @@ export function updater(update: Update) {
pick(update, ['userId', 'inviterId', 'date']) as GramJs.ChatParticipant, pick(update, ['userId', 'inviterId', 'date']) as GramJs.ChatParticipant,
); );
onUpdate({ sendApiUpdate({
'@type': 'updateChatMembers', '@type': 'updateChatMembers',
id: buildApiPeerId(update.chatId, 'chat'), id: buildApiPeerId(update.chatId, 'chat'),
addedMember, addedMember,
}); });
} else if (update instanceof GramJs.UpdateChatParticipantDelete) { } else if (update instanceof GramJs.UpdateChatParticipantDelete) {
onUpdate({ sendApiUpdate({
'@type': 'updateChatMembers', '@type': 'updateChatMembers',
id: buildApiPeerId(update.chatId, 'chat'), id: buildApiPeerId(update.chatId, 'chat'),
deletedMemberId: buildApiPeerId(update.userId, 'user'), deletedMemberId: buildApiPeerId(update.userId, 'user'),
@ -664,7 +601,7 @@ export function updater(update: Update) {
? getApiChatIdFromMtpPeer(update.peer) ? getApiChatIdFromMtpPeer(update.peer)
: buildApiPeerId(update.channelId, 'channel'); : buildApiPeerId(update.channelId, 'channel');
onUpdate({ sendApiUpdate({
'@type': 'updatePinnedIds', '@type': 'updatePinnedIds',
chatId, chatId,
messageIds: update.messages, messageIds: update.messages,
@ -675,8 +612,8 @@ export function updater(update: Update) {
&& update.peer instanceof GramJs.NotifyPeer && update.peer instanceof GramJs.NotifyPeer
) { ) {
const payload = buildApiNotifyException(update.notifySettings, update.peer.peer); const payload = buildApiNotifyException(update.notifySettings, update.peer.peer);
scheduleMutedChatUpdate(payload.chatId, payload.muteUntil, onUpdate); scheduleMutedChatUpdate(payload.chatId, payload.muteUntil, sendApiUpdate);
onUpdate({ sendApiUpdate({
'@type': 'updateNotifyExceptions', '@type': 'updateNotifyExceptions',
...payload, ...payload,
}); });
@ -687,8 +624,8 @@ export function updater(update: Update) {
const payload = buildApiNotifyExceptionTopic( const payload = buildApiNotifyExceptionTopic(
update.notifySettings, update.peer.peer, update.peer.topMsgId, update.notifySettings, update.peer.peer, update.peer.topMsgId,
); );
scheduleMutedTopicUpdate(payload.chatId, payload.topicId, payload.muteUntil, onUpdate); scheduleMutedTopicUpdate(payload.chatId, payload.topicId, payload.muteUntil, sendApiUpdate);
onUpdate({ sendApiUpdate({
'@type': 'updateTopicNotifyExceptions', '@type': 'updateTopicNotifyExceptions',
...payload, ...payload,
}); });
@ -701,7 +638,7 @@ export function updater(update: Update) {
: buildApiPeerId(update.chatId, 'chat'); : buildApiPeerId(update.chatId, 'chat');
if (update.action instanceof GramJs.SendMessageEmojiInteraction) { if (update.action instanceof GramJs.SendMessageEmojiInteraction) {
onUpdate({ sendApiUpdate({
'@type': 'updateStartEmojiInteraction', '@type': 'updateStartEmojiInteraction',
id, id,
emoji: update.action.emoticon, emoji: update.action.emoticon,
@ -709,7 +646,7 @@ export function updater(update: Update) {
interaction: buildApiEmojiInteraction(JSON.parse(update.action.interaction.data)), interaction: buildApiEmojiInteraction(JSON.parse(update.action.interaction.data)),
}); });
} else { } else {
onUpdate({ sendApiUpdate({
'@type': 'updateChatTypingStatus', '@type': 'updateChatTypingStatus',
id, id,
typingStatus: buildChatTypingStatus(update), typingStatus: buildChatTypingStatus(update),
@ -718,7 +655,7 @@ export function updater(update: Update) {
} else if (update instanceof GramJs.UpdateChannelUserTyping) { } else if (update instanceof GramJs.UpdateChannelUserTyping) {
const id = buildApiPeerId(update.channelId, 'channel'); const id = buildApiPeerId(update.channelId, 'channel');
onUpdate({ sendApiUpdate({
'@type': 'updateChatTypingStatus', '@type': 'updateChatTypingStatus',
id, id,
threadId: update.topMsgId, threadId: update.topMsgId,
@ -738,13 +675,13 @@ export function updater(update: Update) {
if (channel instanceof GramJs.Channel) { if (channel instanceof GramJs.Channel) {
const chat = buildApiChatFromPreview(channel); const chat = buildApiChatFromPreview(channel);
if (chat) { if (chat) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: chat.id, id: chat.id,
chat, chat,
}); });
onUpdate({ sendApiUpdate({
'@type': chat.isNotJoined ? 'updateChatLeave' : 'updateChatJoin', '@type': chat.isNotJoined ? 'updateChatLeave' : 'updateChatJoin',
id: buildApiPeerId(update.channelId, 'channel'), id: buildApiPeerId(update.channelId, 'channel'),
}); });
@ -752,7 +689,7 @@ export function updater(update: Update) {
} else if (channel instanceof GramJs.ChannelForbidden) { } else if (channel instanceof GramJs.ChannelForbidden) {
const chatId = buildApiPeerId(update.channelId, 'channel'); const chatId = buildApiPeerId(update.channelId, 'channel');
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: chatId, id: chatId,
chat: { chat: {
@ -760,14 +697,14 @@ export function updater(update: Update) {
}, },
}); });
onUpdate({ sendApiUpdate({
'@type': 'updateChatLeave', '@type': 'updateChatLeave',
id: chatId, id: chatId,
}); });
} else if (_entities.length === 0) { } else if (_entities.length === 0) {
// The link to the discussion group may have been changed. // The link to the discussion group may have been changed.
// No corresponding update available at this moment https://core.telegram.org/type/Updates // No corresponding update available at this moment https://core.telegram.org/type/Updates
onUpdate({ sendApiUpdate({
'@type': 'resetMessages', '@type': 'resetMessages',
id: buildApiPeerId(update.channelId, 'channel'), id: buildApiPeerId(update.channelId, 'channel'),
}); });
@ -776,7 +713,7 @@ export function updater(update: Update) {
update instanceof GramJs.UpdateDialogUnreadMark update instanceof GramJs.UpdateDialogUnreadMark
&& update.peer instanceof GramJs.DialogPeer && update.peer instanceof GramJs.DialogPeer
) { ) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: getApiChatIdFromMtpPeer(update.peer.peer), id: getApiChatIdFromMtpPeer(update.peer.peer),
chat: { chat: {
@ -784,7 +721,7 @@ export function updater(update: Update) {
}, },
}); });
} else if (update instanceof GramJs.UpdateChatDefaultBannedRights) { } else if (update instanceof GramJs.UpdateChatDefaultBannedRights) {
onUpdate({ sendApiUpdate({
'@type': 'updateChat', '@type': 'updateChat',
id: getApiChatIdFromMtpPeer(update.peer), id: getApiChatIdFromMtpPeer(update.peer),
chat: { chat: {
@ -794,19 +731,19 @@ export function updater(update: Update) {
// Users // Users
} else if (update instanceof GramJs.UpdateUserStatus) { } else if (update instanceof GramJs.UpdateUserStatus) {
onUpdate({ sendApiUpdate({
'@type': 'updateUserStatus', '@type': 'updateUserStatus',
userId: buildApiPeerId(update.userId, 'user'), userId: buildApiPeerId(update.userId, 'user'),
status: buildApiUserStatus(update.status), status: buildApiUserStatus(update.status),
}); });
} else if (update instanceof GramJs.UpdateUser) { } else if (update instanceof GramJs.UpdateUser) {
onUpdate({ sendApiUpdate({
'@type': 'updateRequestUserUpdate', '@type': 'updateRequestUserUpdate',
id: buildApiPeerId(update.userId, 'user'), id: buildApiPeerId(update.userId, 'user'),
}); });
} else if (update instanceof GramJs.UpdateUserEmojiStatus) { } else if (update instanceof GramJs.UpdateUserEmojiStatus) {
const emojiStatus = buildApiEmojiStatus(update.emojiStatus); const emojiStatus = buildApiEmojiStatus(update.emojiStatus);
onUpdate({ sendApiUpdate({
'@type': 'updateUserEmojiStatus', '@type': 'updateUserEmojiStatus',
userId: buildApiPeerId(update.userId, 'user'), userId: buildApiPeerId(update.userId, 'user'),
emojiStatus, emojiStatus,
@ -821,7 +758,7 @@ export function updater(update: Update) {
const usernames = buildApiUsernames(update); const usernames = buildApiUsernames(update);
onUpdate({ sendApiUpdate({
'@type': 'updateUser', '@type': 'updateUser',
id: apiUserId, id: apiUserId,
user: { user: {
@ -832,7 +769,7 @@ export function updater(update: Update) {
} else if (update instanceof GramJs.UpdateUserPhone) { } else if (update instanceof GramJs.UpdateUserPhone) {
const { userId, phone } = update; const { userId, phone } = update;
onUpdate({ sendApiUpdate({
'@type': 'updateUser', '@type': 'updateUser',
id: buildApiPeerId(userId, 'user'), id: buildApiPeerId(userId, 'user'),
user: { phoneNumber: phone }, user: { phoneNumber: phone },
@ -848,7 +785,7 @@ export function updater(update: Update) {
_entities _entities
.filter((e) => e instanceof GramJs.User && !e.contact) .filter((e) => e instanceof GramJs.User && !e.contact)
.forEach((user) => { .forEach((user) => {
onUpdate({ sendApiUpdate({
'@type': 'deleteContact', '@type': 'deleteContact',
id: buildApiPeerId(user.id, 'user'), id: buildApiPeerId(user.id, 'user'),
}); });
@ -862,7 +799,7 @@ export function updater(update: Update) {
return; return;
} }
onUpdate({ sendApiUpdate({
'@type': 'updateUser', '@type': 'updateUser',
id: user.id, id: user.id,
user: { user: {
@ -896,7 +833,7 @@ export function updater(update: Update) {
return; return;
} }
onUpdate({ sendApiUpdate({
'@type': 'updateNotifySettings', '@type': 'updateNotifySettings',
peerType, peerType,
isSilent: Boolean(silent isSilent: Boolean(silent
@ -904,7 +841,7 @@ export function updater(update: Update) {
shouldShowPreviews: Boolean(showPreviews), shouldShowPreviews: Boolean(showPreviews),
}); });
} else if (update instanceof GramJs.UpdatePeerBlocked) { } else if (update instanceof GramJs.UpdatePeerBlocked) {
onUpdate({ sendApiUpdate({
'@type': 'updatePeerBlocked', '@type': 'updatePeerBlocked',
id: getApiChatIdFromMtpPeer(update.peerId), id: getApiChatIdFromMtpPeer(update.peerId),
isBlocked: update.blocked, isBlocked: update.blocked,
@ -913,7 +850,7 @@ export function updater(update: Update) {
} else if (update instanceof GramJs.UpdatePrivacy) { } else if (update instanceof GramJs.UpdatePrivacy) {
const key = buildPrivacyKey(update.key); const key = buildPrivacyKey(update.key);
if (key) { if (key) {
onUpdate({ sendApiUpdate({
'@type': 'updatePrivacy', '@type': 'updatePrivacy',
key, key,
rules: buildPrivacyRules(update.rules), rules: buildPrivacyRules(update.rules),
@ -922,35 +859,35 @@ export function updater(update: Update) {
// Misc // Misc
} else if (update instanceof GramJs.UpdateDraftMessage) { } else if (update instanceof GramJs.UpdateDraftMessage) {
onUpdate({ sendApiUpdate({
'@type': 'draftMessage', '@type': 'draftMessage',
chatId: getApiChatIdFromMtpPeer(update.peer), chatId: getApiChatIdFromMtpPeer(update.peer),
threadId: update.topMsgId, threadId: update.topMsgId,
draft: buildMessageDraft(update.draft), draft: buildMessageDraft(update.draft),
}); });
} else if (update instanceof GramJs.UpdateContactsReset) { } else if (update instanceof GramJs.UpdateContactsReset) {
onUpdate({ '@type': 'updateResetContactList' }); sendApiUpdate({ '@type': 'updateResetContactList' });
} else if (update instanceof GramJs.UpdateFavedStickers) { } else if (update instanceof GramJs.UpdateFavedStickers) {
onUpdate({ '@type': 'updateFavoriteStickers' }); sendApiUpdate({ '@type': 'updateFavoriteStickers' });
} else if (update instanceof GramJs.UpdateRecentStickers) { } else if (update instanceof GramJs.UpdateRecentStickers) {
onUpdate({ '@type': 'updateRecentStickers' }); sendApiUpdate({ '@type': 'updateRecentStickers' });
} else if (update instanceof GramJs.UpdateRecentReactions) { } else if (update instanceof GramJs.UpdateRecentReactions) {
onUpdate({ '@type': 'updateRecentReactions' }); sendApiUpdate({ '@type': 'updateRecentReactions' });
} else if (update instanceof GramJs.UpdateSavedReactionTags) { } else if (update instanceof GramJs.UpdateSavedReactionTags) {
onUpdate({ '@type': 'updateSavedReactionTags' }); sendApiUpdate({ '@type': 'updateSavedReactionTags' });
} else if (update instanceof GramJs.UpdateMoveStickerSetToTop) { } else if (update instanceof GramJs.UpdateMoveStickerSetToTop) {
if (!update.masks) { if (!update.masks) {
onUpdate({ sendApiUpdate({
'@type': 'updateMoveStickerSetToTop', '@type': 'updateMoveStickerSetToTop',
isCustomEmoji: update.emojis, isCustomEmoji: update.emojis,
id: update.stickerset.toString(), id: update.stickerset.toString(),
}); });
} }
} else if (update instanceof GramJs.UpdateStickerSets) { } else if (update instanceof GramJs.UpdateStickerSets) {
onUpdate({ '@type': 'updateStickerSets' }); sendApiUpdate({ '@type': 'updateStickerSets' });
} else if (update instanceof GramJs.UpdateStickerSetsOrder) { } else if (update instanceof GramJs.UpdateStickerSetsOrder) {
if (!update.masks) { if (!update.masks) {
onUpdate({ sendApiUpdate({
'@type': 'updateStickerSetsOrder', '@type': 'updateStickerSetsOrder',
order: update.order.map((n) => n.toString()), order: update.order.map((n) => n.toString()),
isCustomEmoji: update.emojis, isCustomEmoji: update.emojis,
@ -959,73 +896,45 @@ export function updater(update: Update) {
} else if (update instanceof GramJs.UpdateNewStickerSet) { } else if (update instanceof GramJs.UpdateNewStickerSet) {
if (update.stickerset instanceof GramJs.messages.StickerSet) { if (update.stickerset instanceof GramJs.messages.StickerSet) {
const stickerSet = buildStickerSet(update.stickerset.set); const stickerSet = buildStickerSet(update.stickerset.set);
onUpdate({ sendApiUpdate({
'@type': 'updateStickerSet', '@type': 'updateStickerSet',
id: stickerSet.id, id: stickerSet.id,
stickerSet, stickerSet,
}); });
} }
} else if (update instanceof GramJs.UpdateSavedGifs) { } else if (update instanceof GramJs.UpdateSavedGifs) {
onUpdate({ '@type': 'updateSavedGifs' }); sendApiUpdate({ '@type': 'updateSavedGifs' });
} else if (update instanceof GramJs.UpdateGroupCall) { } else if (update instanceof GramJs.UpdateGroupCall) {
// eslint-disable-next-line no-underscore-dangle sendApiUpdate({
const entities = update._entities;
if (entities) {
addEntitiesToLocalDb(entities);
dispatchUserAndChatUpdates(entities);
}
onUpdate({
'@type': 'updateGroupCall', '@type': 'updateGroupCall',
call: buildApiGroupCall(update.call), call: buildApiGroupCall(update.call),
}); });
} else if (update instanceof GramJs.UpdateGroupCallConnection) { } else if (update instanceof GramJs.UpdateGroupCallConnection) {
onUpdate({ sendApiUpdate({
'@type': 'updateGroupCallConnection', '@type': 'updateGroupCallConnection',
data: JSON.parse(update.params.data) as GroupCallConnectionData, data: JSON.parse(update.params.data) as GroupCallConnectionData,
presentation: Boolean(update.presentation), presentation: Boolean(update.presentation),
}); });
} else if (update instanceof GramJs.UpdateGroupCallParticipants) { } else if (update instanceof GramJs.UpdateGroupCallParticipants) {
// eslint-disable-next-line no-underscore-dangle sendApiUpdate({
const entities = update._entities;
if (entities) {
addEntitiesToLocalDb(entities);
dispatchUserAndChatUpdates(entities);
}
onUpdate({
'@type': 'updateGroupCallParticipants', '@type': 'updateGroupCallParticipants',
groupCallId: getGroupCallId(update.call), groupCallId: getGroupCallId(update.call),
participants: update.participants.map(buildApiGroupCallParticipant), participants: update.participants.map(buildApiGroupCallParticipant),
}); });
} else if (update instanceof GramJs.UpdatePendingJoinRequests) { } else if (update instanceof GramJs.UpdatePendingJoinRequests) {
// eslint-disable-next-line no-underscore-dangle sendApiUpdate({
const entities = update._entities;
if (entities) {
addEntitiesToLocalDb(entities);
dispatchUserAndChatUpdates(entities);
}
onUpdate({
'@type': 'updatePendingJoinRequests', '@type': 'updatePendingJoinRequests',
chatId: getApiChatIdFromMtpPeer(update.peer), chatId: getApiChatIdFromMtpPeer(update.peer),
recentRequesterIds: update.recentRequesters.map((id) => buildApiPeerId(id, 'user')), recentRequesterIds: update.recentRequesters.map((id) => buildApiPeerId(id, 'user')),
requestsPending: update.requestsPending, requestsPending: update.requestsPending,
}); });
} else if (update instanceof GramJs.UpdatePhoneCall) { } else if (update instanceof GramJs.UpdatePhoneCall) {
// eslint-disable-next-line no-underscore-dangle sendApiUpdate({
const entities = update._entities;
if (entities) {
addEntitiesToLocalDb(entities);
dispatchUserAndChatUpdates(entities);
}
onUpdate({
'@type': 'updatePhoneCall', '@type': 'updatePhoneCall',
call: buildPhoneCall(update.phoneCall), call: buildPhoneCall(update.phoneCall),
}); });
} else if (update instanceof GramJs.UpdatePhoneCallSignalingData) { } else if (update instanceof GramJs.UpdatePhoneCallSignalingData) {
onUpdate({ sendApiUpdate({
'@type': 'updatePhoneCallSignalingData', '@type': 'updatePhoneCallSignalingData',
callId: update.phoneCallId.toString(), callId: update.phoneCallId.toString(),
data: Array.from(update.data), data: Array.from(update.data),
@ -1033,7 +942,7 @@ export function updater(update: Update) {
} else if (update instanceof GramJs.UpdateWebViewResultSent) { } else if (update instanceof GramJs.UpdateWebViewResultSent) {
const { queryId } = update; const { queryId } = update;
onUpdate({ sendApiUpdate({
'@type': 'updateWebViewResultSent', '@type': 'updateWebViewResultSent',
queryId: queryId.toString(), queryId: queryId.toString(),
}); });
@ -1045,98 +954,78 @@ export function updater(update: Update) {
const id = buildApiPeerId(botId, 'user'); const id = buildApiPeerId(botId, 'user');
onUpdate({ sendApiUpdate({
'@type': 'updateBotMenuButton', '@type': 'updateBotMenuButton',
botId: id, botId: id,
button: buildApiBotMenuButton(button), button: buildApiBotMenuButton(button),
}); });
} else if (update instanceof GramJs.UpdateTranscribedAudio) { } else if (update instanceof GramJs.UpdateTranscribedAudio) {
// eslint-disable-next-line no-underscore-dangle sendApiUpdate({
const entities = update._entities;
if (entities) {
addEntitiesToLocalDb(entities);
dispatchUserAndChatUpdates(entities);
}
onUpdate({
'@type': 'updateTranscribedAudio', '@type': 'updateTranscribedAudio',
transcriptionId: update.transcriptionId.toString(), transcriptionId: update.transcriptionId.toString(),
text: update.text, text: update.text,
isPending: update.pending, isPending: update.pending,
}); });
} else if (update instanceof GramJs.UpdateConfig) { } else if (update instanceof GramJs.UpdateConfig) {
// eslint-disable-next-line no-underscore-dangle sendApiUpdate({ '@type': 'updateConfig' });
const entities = update._entities;
if (entities) {
addEntitiesToLocalDb(entities);
dispatchUserAndChatUpdates(entities);
}
onUpdate({ '@type': 'updateConfig' });
} else if (update instanceof GramJs.UpdateChannelPinnedTopic) { } else if (update instanceof GramJs.UpdateChannelPinnedTopic) {
onUpdate({ sendApiUpdate({
'@type': 'updatePinnedTopic', '@type': 'updatePinnedTopic',
chatId: buildApiPeerId(update.channelId, 'channel'), chatId: buildApiPeerId(update.channelId, 'channel'),
topicId: update.topicId, topicId: update.topicId,
isPinned: Boolean(update.pinned), isPinned: Boolean(update.pinned),
}); });
} else if (update instanceof GramJs.UpdateChannelPinnedTopics) { } else if (update instanceof GramJs.UpdateChannelPinnedTopics) {
onUpdate({ sendApiUpdate({
'@type': 'updatePinnedTopicsOrder', '@type': 'updatePinnedTopicsOrder',
chatId: buildApiPeerId(update.channelId, 'channel'), chatId: buildApiPeerId(update.channelId, 'channel'),
order: update.order || [], order: update.order || [],
}); });
} else if (update instanceof GramJs.UpdateRecentEmojiStatuses) { } else if (update instanceof GramJs.UpdateRecentEmojiStatuses) {
onUpdate({ '@type': 'updateRecentEmojiStatuses' }); sendApiUpdate({ '@type': 'updateRecentEmojiStatuses' });
} else if (update instanceof GramJs.UpdateStory) { } 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 { story } = update;
const peerId = getApiChatIdFromMtpPeer(update.peer); const peerId = getApiChatIdFromMtpPeer(update.peer);
const apiStory = buildApiStory(peerId, story) as ApiStory | ApiStorySkipped; const apiStory = buildApiStory(peerId, story) as ApiStory | ApiStorySkipped;
addStoryToLocalDb(story, peerId); // Add after building to prevent repair info overwrite addStoryToLocalDb(story, peerId); // Add after building to prevent repair info overwrite
if (story instanceof GramJs.StoryItemDeleted) { if (story instanceof GramJs.StoryItemDeleted) {
onUpdate({ sendApiUpdate({
'@type': 'deleteStory', '@type': 'deleteStory',
peerId, peerId,
storyId: story.id, storyId: story.id,
}); });
} else { } else {
onUpdate({ sendApiUpdate({
'@type': 'updateStory', '@type': 'updateStory',
peerId, peerId,
story: apiStory, story: apiStory,
}); });
} }
} else if (update instanceof GramJs.UpdateReadStories) { } else if (update instanceof GramJs.UpdateReadStories) {
onUpdate({ sendApiUpdate({
'@type': 'updateReadStories', '@type': 'updateReadStories',
peerId: getApiChatIdFromMtpPeer(update.peer), peerId: getApiChatIdFromMtpPeer(update.peer),
lastReadId: update.maxId, lastReadId: update.maxId,
}); });
} else if (update instanceof GramJs.UpdateSentStoryReaction) { } else if (update instanceof GramJs.UpdateSentStoryReaction) {
onUpdate({ sendApiUpdate({
'@type': 'updateSentStoryReaction', '@type': 'updateSentStoryReaction',
peerId: getApiChatIdFromMtpPeer(update.peer), peerId: getApiChatIdFromMtpPeer(update.peer),
storyId: update.storyId, storyId: update.storyId,
reaction: buildApiReaction(update.reaction), reaction: buildApiReaction(update.reaction),
}); });
} else if (update instanceof GramJs.UpdateStoriesStealthMode) { } else if (update instanceof GramJs.UpdateStoriesStealthMode) {
onUpdate({ sendApiUpdate({
'@type': 'updateStealthMode', '@type': 'updateStealthMode',
stealthMode: buildApiStealthMode(update.stealthMode), stealthMode: buildApiStealthMode(update.stealthMode),
}); });
} else if (update instanceof GramJs.UpdateAttachMenuBots) { } else if (update instanceof GramJs.UpdateAttachMenuBots) {
onUpdate({ sendApiUpdate({
'@type': 'updateAttachMenuBots', '@type': 'updateAttachMenuBots',
}); });
} else if (update instanceof GramJs.UpdateNewAuthorization) { } else if (update instanceof GramJs.UpdateNewAuthorization) {
onUpdate({ sendApiUpdate({
'@type': 'updateNewAuthorization', '@type': 'updateNewAuthorization',
hash: update.hash.toString(), hash: update.hash.toString(),
date: update.date, date: update.date,
@ -1145,18 +1034,18 @@ export function updater(update: Update) {
isUnconfirmed: update.unconfirmed, isUnconfirmed: update.unconfirmed,
}); });
} else if (update instanceof GramJs.UpdateChannelViewForumAsMessages) { } else if (update instanceof GramJs.UpdateChannelViewForumAsMessages) {
onUpdate({ sendApiUpdate({
'@type': 'updateViewForumAsMessages', '@type': 'updateViewForumAsMessages',
chatId: buildApiPeerId(update.channelId, 'channel'), chatId: buildApiPeerId(update.channelId, 'channel'),
isEnabled: update.enabled ? true : undefined, isEnabled: update.enabled ? true : undefined,
}); });
} else if (update instanceof GramJs.UpdateStarsBalance) { } else if (update instanceof GramJs.UpdateStarsBalance) {
onUpdate({ sendApiUpdate({
'@type': 'updateStarsBalance', '@type': 'updateStarsBalance',
balance: update.balance.toJSNumber(), balance: update.balance.toJSNumber(),
}); });
} else if (update instanceof LocalUpdatePremiumFloodWait) { } else if (update instanceof LocalUpdatePremiumFloodWait) {
onUpdate({ sendApiUpdate({
'@type': 'updatePremiumFloodWait', '@type': 'updatePremiumFloodWait',
isUpload: update.isUpload, isUpload: update.isUpload,
}); });

View File

@ -3,15 +3,15 @@ import { UpdateConnectionState, UpdateServerTimeOffset } from '../../../lib/gram
import type { ApiChat } from '../../types'; import type { ApiChat } from '../../types';
import type { invokeRequest } from '../methods/client'; import type { invokeRequest } from '../methods/client';
import type { Update } from './updater';
import { DEBUG } from '../../../config'; import { DEBUG } from '../../../config';
import SortedQueue from '../../../util/SortedQueue'; import SortedQueue from '../../../util/SortedQueue';
import { buildApiPeerId } from '../apiBuilders/peers'; import { buildApiPeerId } from '../apiBuilders/peers';
import { buildInputEntity, buildMtpPeerId } from '../gramjsBuilders'; import { buildInputEntity, buildMtpPeerId } from '../gramjsBuilders';
import { addEntitiesToLocalDb } from '../helpers';
import localDb from '../localDb'; 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'; import { buildLocalUpdatePts, type UpdatePts } from './UpdatePts';
@ -139,6 +139,7 @@ function applyUpdate(updateObject: SeqUpdate | PtsUpdate) {
} }
if (updateObject instanceof GramJs.UpdatesCombined || updateObject instanceof GramJs.Updates) { if (updateObject instanceof GramJs.UpdatesCombined || updateObject instanceof GramJs.Updates) {
processAndUpdateEntities(updateObject);
const entities = updateObject.users.concat(updateObject.chats); const entities = updateObject.users.concat(updateObject.chats);
updateObject.updates.forEach((update) => { updateObject.updates.forEach((update) => {
@ -277,7 +278,7 @@ export async function getDifference() {
return; return;
} }
sendUpdate({ sendApiUpdate({
'@type': 'updateFetchingDifference', '@type': 'updateFetchingDifference',
isFetching: true, isFetching: true,
}); });
@ -296,7 +297,7 @@ export async function getDifference() {
if (response instanceof GramJs.updates.DifferenceEmpty) { if (response instanceof GramJs.updates.DifferenceEmpty) {
localDb.commonBoxState.seq = response.seq; localDb.commonBoxState.seq = response.seq;
localDb.commonBoxState.date = response.date; localDb.commonBoxState.date = response.date;
sendUpdate({ sendApiUpdate({
'@type': 'updateFetchingDifference', '@type': 'updateFetchingDifference',
isFetching: false, isFetching: false,
}); });
@ -313,7 +314,7 @@ export async function getDifference() {
return; return;
} }
sendUpdate({ sendApiUpdate({
'@type': 'updateFetchingDifference', '@type': 'updateFetchingDifference',
isFetching: false, isFetching: false,
}); });
@ -366,7 +367,7 @@ async function getChannelDifference(channelId: string) {
function forceSync() { function forceSync() {
reset(); reset();
sendUpdate({ sendApiUpdate({
'@type': 'requestSync', '@type': 'requestSync',
}); });
@ -425,11 +426,7 @@ function processDifference(
})); }));
}); });
addEntitiesToLocalDb(difference.users); processAndUpdateEntities(difference);
addEntitiesToLocalDb(difference.chats);
dispatchUserAndChatUpdates(difference.users);
dispatchUserAndChatUpdates(difference.chats);
// Ignore `pts`/`seq` holes when applying updates from 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` // BUT, if we got an `UpdateChannelTooLong`, make sure to process other updates after receiving `ChannelDifference`

View File

@ -1,5 +1,7 @@
import type { DebugLevel } from '../../../util/debugConsole'; 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 { LocalDb } from '../localDb';
import type { MethodArgs, MethodResponse, Methods } from '../methods/types'; import type { MethodArgs, MethodResponse, Methods } from '../methods/types';

View File

@ -41,6 +41,7 @@ handleErrors();
let pendingPayloads: WorkerPayload[] = []; let pendingPayloads: WorkerPayload[] = [];
let pendingTransferables: Transferable[] = []; let pendingTransferables: Transferable[] = [];
let pendingUpdates: ApiUpdate[] = [];
const callbackState = new Map<string, ApiOnProgress>(); const callbackState = new Map<string, ApiOnProgress>();
@ -158,31 +159,18 @@ function handleErrors() {
}); });
} }
let pendingUpdates: ApiUpdate[] = []; const sendToOriginOnTickEnd = throttleWithTickEnd(() => {
if (pendingUpdates.length) {
const sendUpdatesOnTickEnd = throttleWithTickEnd(() => { pendingPayloads.unshift({
const currentUpdates = pendingUpdates; type: 'updates',
pendingUpdates = []; updates: pendingUpdates,
});
sendToOrigin({
type: 'updates',
updates: currentUpdates,
});
});
function onUpdate(update: ApiUpdate) {
if (DEBUG && update['@type'] !== 'updateUserStatus' && update['@type'] !== 'updateServerTimeOffset') {
log('UPDATE', update['@type'], update);
} }
pendingUpdates.push(update);
sendUpdatesOnTickEnd();
}
const sendToOriginOnTickEnd = throttleWithTickEnd(() => {
const data = { payloads: pendingPayloads }; const data = { payloads: pendingPayloads };
const transferables = pendingTransferables; const transferables = pendingTransferables;
pendingUpdates = [];
pendingPayloads = []; pendingPayloads = [];
pendingTransferables = []; pendingTransferables = [];
@ -202,3 +190,12 @@ function sendToOrigin(payload: WorkerPayload, transferable?: Transferable) {
sendToOriginOnTickEnd(); sendToOriginOnTickEnd();
} }
function onUpdate(update: ApiUpdate) {
if (DEBUG && update['@type'] !== 'updateUserStatus' && update['@type'] !== 'updateServerTimeOffset') {
log('UPDATE', update['@type'], update);
}
pendingUpdates.push(update);
sendToOriginOnTickEnd();
}

View File

@ -745,7 +745,7 @@ interface ApiBaseThreadInfo {
export interface ApiCommentsInfo extends ApiBaseThreadInfo { export interface ApiCommentsInfo extends ApiBaseThreadInfo {
isCommentsInfo: true; isCommentsInfo: true;
threadId?: ThreadId; threadId?: never;
originChannelId: string; originChannelId: string;
originMessageId: number; originMessageId: number;
} }

View File

@ -264,9 +264,9 @@ export type ApiUpdatePinnedMessageIds = {
messageIds: number[]; messageIds: number[];
}; };
export type ApiUpdateThreadInfos = { export type ApiUpdateThreadInfo = {
'@type': 'updateThreadInfos'; '@type': 'updateThreadInfo';
threadInfoUpdates: Partial<ApiThreadInfo>[]; threadInfo: Partial<ApiThreadInfo>;
}; };
export type ApiUpdateScheduledMessageSendSucceeded = { export type ApiUpdateScheduledMessageSendSucceeded = {
@ -753,13 +753,20 @@ export type ApiUpdateNewProfilePhoto = {
photo: ApiPhoto; photo: ApiPhoto;
}; };
export type ApiUpdateEntities = {
'@type': 'updateEntities';
users?: Record<string, ApiUser>;
chats?: Record<string, ApiChat>;
threadInfos?: ApiThreadInfo[];
};
export type ApiUpdate = ( export type ApiUpdate = (
ApiUpdateReady | ApiUpdateSession | ApiUpdateWebAuthTokenFailed | ApiUpdateRequestUserUpdate | ApiUpdateReady | ApiUpdateSession | ApiUpdateWebAuthTokenFailed | ApiUpdateRequestUserUpdate |
ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser | ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser |
ApiUpdateChat | ApiUpdateChatInbox | ApiUpdateChatTypingStatus | ApiUpdateChatFullInfo | ApiUpdatePinnedChatIds | ApiUpdateChat | ApiUpdateChatInbox | ApiUpdateChatTypingStatus | ApiUpdateChatFullInfo | ApiUpdatePinnedChatIds |
ApiUpdateChatMembers | ApiUpdateChatJoin | ApiUpdateChatLeave | ApiUpdateChatPinned | ApiUpdatePinnedMessageIds | ApiUpdateChatMembers | ApiUpdateChatJoin | ApiUpdateChatLeave | ApiUpdateChatPinned | ApiUpdatePinnedMessageIds |
ApiUpdateChatListType | ApiUpdateChatFolder | ApiUpdateChatFoldersOrder | ApiUpdateRecommendedChatFolders | ApiUpdateChatListType | ApiUpdateChatFolder | ApiUpdateChatFoldersOrder | ApiUpdateRecommendedChatFolders |
ApiUpdateNewMessage | ApiUpdateMessage | ApiUpdateThreadInfos | ApiUpdateCommonBoxMessages | ApiUpdateNewMessage | ApiUpdateMessage | ApiUpdateThreadInfo | ApiUpdateCommonBoxMessages |
ApiUpdateDeleteMessages | ApiUpdateMessagePoll | ApiUpdateMessagePollVote | ApiUpdateDeleteHistory | ApiUpdateDeleteMessages | ApiUpdateMessagePoll | ApiUpdateMessagePollVote | ApiUpdateDeleteHistory |
ApiUpdateMessageSendSucceeded | ApiUpdateMessageSendFailed | ApiUpdateServiceNotification | ApiUpdateMessageSendSucceeded | ApiUpdateMessageSendFailed | ApiUpdateServiceNotification |
ApiDeleteContact | ApiUpdateUser | ApiUpdateUserStatus | ApiUpdateUserFullInfo | ApiDeleteContact | ApiUpdateUser | ApiUpdateUserStatus | ApiUpdateUserFullInfo |
@ -785,7 +792,7 @@ export type ApiUpdate = (
ApiUpdateViewForumAsMessages | ApiUpdateSavedDialogPinned | ApiUpdatePinnedSavedDialogIds | ApiUpdateChatLastMessage | ApiUpdateViewForumAsMessages | ApiUpdateSavedDialogPinned | ApiUpdatePinnedSavedDialogIds | ApiUpdateChatLastMessage |
ApiUpdateDeleteSavedHistory | ApiUpdatePremiumFloodWait | ApiUpdateStarsBalance | ApiUpdateDeleteSavedHistory | ApiUpdatePremiumFloodWait | ApiUpdateStarsBalance |
ApiUpdateQuickReplyMessage | ApiUpdateQuickReplies | ApiDeleteQuickReply | ApiUpdateDeleteQuickReplyMessages | ApiUpdateQuickReplyMessage | ApiUpdateQuickReplies | ApiDeleteQuickReply | ApiUpdateDeleteQuickReplyMessages |
ApiUpdateDeleteProfilePhoto | ApiUpdateNewProfilePhoto ApiUpdateDeleteProfilePhoto | ApiUpdateNewProfilePhoto | ApiUpdateEntities
); );
export type OnApiUpdate = (update: ApiUpdate) => void; export type OnApiUpdate = (update: ApiUpdate) => void;

View File

@ -450,7 +450,7 @@ export default memo(withGlobal<OwnProps>(
const isForum = chat?.isForum; const isForum = chat?.isForum;
const isMuted = chat && selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global)); const isMuted = chat && selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global));
const { threadId } = selectCurrentMessageList(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 chatFullInfo = chat && selectChatFullInfo(global, chat.id);
const userFullInfo = user && selectUserFullInfo(global, user.id); const userFullInfo = user && selectUserFullInfo(global, user.id);

View File

@ -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_UPDATE_THROTTLE = Math.round((FAST_SMOOTH_MIN_DURATION + FAST_SMOOTH_MAX_DURATION) / 2);
export const API_THROTTLE_RESET_UPDATES = new Set([ export const API_THROTTLE_RESET_UPDATES = new Set([
'newMessage', 'newScheduledMessage', 'deleteMessages', 'deleteScheduledMessages', 'deleteHistory', 'newMessage', 'newScheduledMessage', 'deleteMessages', 'deleteScheduledMessages', 'deleteHistory',
'updateThreadInfos',
]); ]);
export const LOCK_SCREEN_ANIMATION_DURATION_MS = 200; 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 BOT_FATHER_USERNAME = 'botfather';
export const USERNAME_PURCHASE_ERROR = 'USERNAME_PURCHASE_AVAILABLE'; export const USERNAME_PURCHASE_ERROR = 'USERNAME_PURCHASE_AVAILABLE';
export const PURCHASE_USERNAME = 'auction'; 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 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'; export const WEB_APP_PLATFORM = 'weba';

View File

@ -1,9 +1,7 @@
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey } from '../../../util/iteratees';
import { oldTranslate } from '../../../util/oldLangProvider'; import { oldTranslate } from '../../../util/oldLangProvider';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { addUsers } from '../../reducers';
import { selectChat } from '../../selectors'; import { selectChat } from '../../selectors';
addActionHandler('reportPeer', async (global, actions, payload): Promise<void> => { addActionHandler('reportPeer', async (global, actions, payload): Promise<void> => {
@ -193,11 +191,9 @@ addActionHandler('loadWebAuthorizations', async (global): Promise<void> => {
if (!result) { if (!result) {
return; return;
} }
const { users, webAuthorizations } = result; const { webAuthorizations } = result;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = { global = {
...global, ...global,
activeWebSessions: { activeWebSessions: {

View File

@ -9,7 +9,6 @@ import { ManagementProgress } from '../../../types';
import { BOT_FATHER_USERNAME, GENERAL_REFETCH_INTERVAL } from '../../../config'; import { BOT_FATHER_USERNAME, GENERAL_REFETCH_INTERVAL } from '../../../config';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey } from '../../../util/iteratees';
import { oldTranslate } from '../../../util/oldLangProvider'; import { oldTranslate } from '../../../util/oldLangProvider';
import PopupManager from '../../../util/PopupManager'; import PopupManager from '../../../util/PopupManager';
import requestActionTimeout from '../../../util/requestActionTimeout'; import requestActionTimeout from '../../../util/requestActionTimeout';
@ -21,7 +20,7 @@ import {
addActionHandler, getGlobal, setGlobal, addActionHandler, getGlobal, setGlobal,
} from '../../index'; } from '../../index';
import { import {
addChats, addUsers, removeBlockedUser, updateManagementProgress, updateUser, updateUserFullInfo, removeBlockedUser, updateManagementProgress, updateUser, updateUserFullInfo,
} from '../../reducers'; } from '../../reducers';
import { replaceInlineBotSettings, replaceInlineBotsIsLoading } from '../../reducers/bots'; import { replaceInlineBotSettings, replaceInlineBotsIsLoading } from '../../reducers/bots';
import { updateTabState } from '../../reducers/tabs'; import { updateTabState } from '../../reducers/tabs';
@ -224,10 +223,9 @@ addActionHandler('loadTopInlineBots', async (global): Promise<void> => {
return; return;
} }
const { ids, users } = result; const { ids } = result;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = { global = {
...global, ...global,
topInlineBots: { topInlineBots: {
@ -250,10 +248,9 @@ addActionHandler('loadTopBotApps', async (global): Promise<void> => {
return; return;
} }
const { ids, users } = result; const { ids } = result;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = { global = {
...global, ...global,
topBotApps: { topBotApps: {
@ -285,8 +282,6 @@ addActionHandler('queryInlineBot', async (global, actions, payload): Promise<voi
return; return;
} }
global = addUsers(global, { [inlineBot.id]: inlineBot });
global = addChats(global, { [chat.id]: chat });
inlineBotData = { inlineBotData = {
id: inlineBot.id, id: inlineBot.id,
query: '', query: '',
@ -689,11 +684,9 @@ addActionHandler('requestAppWebView', async (global, actions, payload): Promise<
bot, bot,
}); });
if (result) { if (result) {
const attachBot = result.bot;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
setGlobal(global);
const attachBot = result.bot;
const shouldAskForTos = attachBot.isDisclaimerNeeded || attachBot.isForAttachMenu || attachBot.isForSideMenu; const shouldAskForTos = attachBot.isDisclaimerNeeded || attachBot.isForAttachMenu || attachBot.isForSideMenu;
if (shouldAskForTos) { if (shouldAskForTos) {
@ -886,7 +879,6 @@ async function loadAttachBots<T extends GlobalState>(global: T, hash?: string) {
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = { global = {
...global, ...global,
attachMenu: { attachMenu: {

View File

@ -10,10 +10,8 @@ import {
toggleStream, toggleStream,
} from '../../../lib/secret-sauce'; } from '../../../lib/secret-sauce';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey } from '../../../util/iteratees';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { addUsers } from '../../reducers';
import { import {
removeGroupCall, removeGroupCall,
updateActiveGroupCall, updateActiveGroupCall,
@ -263,11 +261,7 @@ addActionHandler('connectToActivePhoneCall', async (global, actions): Promise<vo
if (!result) { if (!result) {
if ('hangUp' in actions) actions.hangUp({ tabId: getCurrentTabId() }); if ('hangUp' in actions) actions.hangUp({ tabId: getCurrentTabId() });
return;
} }
global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
setGlobal(global);
}); });
addActionHandler('acceptCall', async (global): Promise<void> => { addActionHandler('acceptCall', async (global): Promise<void> => {
@ -281,13 +275,7 @@ addActionHandler('acceptCall', async (global): Promise<void> => {
await callApi('createPhoneCallState', [false]); await callApi('createPhoneCallState', [false]);
const gB = await callApi('acceptPhoneCall', [dhConfig])!; const gB = await callApi('acceptPhoneCall', [dhConfig])!;
const result = await callApi('acceptCall', { call: phoneCall, gB }); await callApi('acceptCall', { call: phoneCall, gB });
if (!result) {
return;
}
global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
setGlobal(global);
}); });
addActionHandler('sendSignalingData', (global, actions, payload): ActionReturnType => { addActionHandler('sendSignalingData', (global, actions, payload): ActionReturnType => {

View File

@ -1,6 +1,6 @@
import type { import type {
ApiChat, ApiChatFolder, ApiChatlistExportedInvite, ApiChat, ApiChatFolder, ApiChatlistExportedInvite,
ApiChatMember, ApiError, ApiMissingInvitedUser, ApiUser, ApiChatMember, ApiError, ApiMissingInvitedUser,
} from '../../../api/types'; } from '../../../api/types';
import type { RequiredGlobalActions } from '../../index'; import type { RequiredGlobalActions } from '../../index';
import type { import type {
@ -21,7 +21,6 @@ import {
ARCHIVED_FOLDER_ID, ARCHIVED_FOLDER_ID,
CHAT_LIST_LOAD_SLICE, CHAT_LIST_LOAD_SLICE,
DEBUG, DEBUG,
GLOBAL_STATE_CACHE_ARCHIVED_CHAT_LIST_LIMIT,
GLOBAL_SUGGESTED_CHANNELS_ID, GLOBAL_SUGGESTED_CHANNELS_ID,
RE_TG_LINK, RE_TG_LINK,
SAVED_FOLDER_ID, SAVED_FOLDER_ID,
@ -57,10 +56,8 @@ import {
} from '../../index'; } from '../../index';
import { import {
addChatMembers, addChatMembers,
addChats,
addMessages, addMessages,
addSimilarChannels, addSimilarChannels,
addUsers,
addUserStatuses, addUserStatuses,
deleteChatMessages, deleteChatMessages,
deletePeerPhoto, deletePeerPhoto,
@ -70,9 +67,7 @@ import {
replaceChatFullInfo, replaceChatFullInfo,
replaceChatListIds, replaceChatListIds,
replaceChatListLoadingParameters, replaceChatListLoadingParameters,
replaceChats,
replaceThreadParam, replaceThreadParam,
replaceUsers,
replaceUserStatuses, replaceUserStatuses,
toggleSimilarChannels, toggleSimilarChannels,
updateChat, updateChat,
@ -91,6 +86,7 @@ import {
updateTopic, updateTopic,
updateTopics, updateTopics,
updateUser, updateUser,
updateUsers,
} from '../../reducers'; } from '../../reducers';
import { updateGroupCall } from '../../reducers/calls'; import { updateGroupCall } from '../../reducers/calls';
import { updateTabState } from '../../reducers/tabs'; import { updateTabState } from '../../reducers/tabs';
@ -118,7 +114,6 @@ import {
selectThreadInfo, selectThreadInfo,
selectUser, selectUser,
selectUserByPhoneNumber, selectUserByPhoneNumber,
selectVisibleUsers,
} from '../../selectors'; } from '../../selectors';
import { selectGroupCall } from '../../selectors/calls'; import { selectGroupCall } from '../../selectors/calls';
import { selectCurrentLimit } from '../../selectors/limits'; import { selectCurrentLimit } from '../../selectors/limits';
@ -126,13 +121,6 @@ import { selectCurrentLimit } from '../../selectors/limits';
const TOP_CHAT_MESSAGES_PRELOAD_INTERVAL = 100; const TOP_CHAT_MESSAGES_PRELOAD_INTERVAL = 100;
const INFINITE_LOOP_MARKER = 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([ const CHATLIST_LIMIT_ERROR_LIST = new Set([
'FILTERS_TOO_MUCH', 'FILTERS_TOO_MUCH',
'CHATLISTS_TOO_MUCH', 'CHATLISTS_TOO_MUCH',
@ -404,8 +392,6 @@ addActionHandler('openThread', async (global, actions, payload): Promise<void> =
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = addMessages(global, result.messages); global = addMessages(global, result.messages);
if (isComments) { if (isComments) {
global = updateThreadInfo(global, loadingChatId, loadingThreadId, { global = updateThreadInfo(global, loadingChatId, loadingThreadId, {
@ -509,12 +495,12 @@ addActionHandler('openSupportChat', async (global, actions, payload): Promise<vo
}); });
addActionHandler('loadAllChats', async (global, actions, payload): Promise<void> => { addActionHandler('loadAllChats', async (global, actions, payload): Promise<void> => {
const { onFirstBatchDone } = payload;
const listType = payload.listType; const listType = payload.listType;
const { onReplace } = payload; let isCallbackFired = false;
let { shouldReplace } = payload;
let i = 0; let i = 0;
while (shouldReplace || !global.chats.isFullyLoaded[listType]) { while (!global.chats.isFullyLoaded[listType]) {
if (i++ >= INFINITE_LOOP_MARKER) { if (i++ >= INFINITE_LOOP_MARKER) {
if (DEBUG) { if (DEBUG) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -532,13 +518,12 @@ addActionHandler('loadAllChats', async (global, actions, payload): Promise<void>
await loadChats( await loadChats(
listType, listType,
shouldReplace,
true, true,
); );
if (shouldReplace) { if (!isCallbackFired) {
onReplace?.(); onFirstBatchDone?.();
shouldReplace = false; isCallbackFired = true;
} }
global = getGlobal(); global = getGlobal();
@ -608,8 +593,6 @@ addActionHandler('requestSavedDialogUpdate', async (global, actions, payload): P
global = getGlobal(); global = getGlobal();
global = addMessages(global, result.messages); global = addMessages(global, result.messages);
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
if (result.messages.length) { if (result.messages.length) {
global = updateChatLastMessageId(global, chatId, result.messages[0].id, 'saved'); global = updateChatLastMessageId(global, chatId, result.messages[0].id, 'saved');
@ -1792,7 +1775,6 @@ addActionHandler('loadGroupsForDiscussion', async (global): Promise<void> => {
}, {} as Record<string, ApiChat>); }, {} as Record<string, ApiChat>);
global = getGlobal(); global = getGlobal();
global = addChats(global, addedById);
global = { global = {
...global, ...global,
chats: { chats: {
@ -1900,13 +1882,12 @@ addActionHandler('loadMoreMembers', async (global, actions, payload): Promise<vo
return; return;
} }
const { members, users, userStatusesById } = result; const { members, userStatusesById } = result;
if (!members || !members.length) { if (!members || !members.length) {
return; return;
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = addUserStatuses(global, userStatusesById); global = addUserStatuses(global, userStatusesById);
global = addChatMembers(global, chat, members); global = addChatMembers(global, chat, members);
setGlobal(global); setGlobal(global);
@ -2000,11 +1981,10 @@ addActionHandler('loadChatSettings', async (global, actions, payload): Promise<v
const result = await callApi('fetchChatSettings', chat); const result = await callApi('fetchChatSettings', chat);
if (!result) return; if (!result) return;
const { settings, users } = result;
const { settings } = result;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = updateChat(global, chat.id, { settings }); global = updateChat(global, chat.id, { settings });
setGlobal(global); setGlobal(global);
}); });
@ -2117,8 +2097,6 @@ addActionHandler('loadTopics', async (global, actions, payload): Promise<void> =
if (!result) return; if (!result) return;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = addMessages(global, result.messages); global = addMessages(global, result.messages);
global = updateTopics(global, chatId, result.count, result.topics); global = updateTopics(global, chatId, result.count, result.topics);
global = updateListedTopicIds(global, chatId, result.topics.map((topic) => topic.id)); global = updateListedTopicIds(global, chatId, result.topics.map((topic) => topic.id));
@ -2149,8 +2127,6 @@ addActionHandler('loadTopicById', async (global, actions, payload): Promise<void
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = addMessages(global, result.messages); global = addMessages(global, result.messages);
global = updateTopic(global, chatId, topicId, result.topic); global = updateTopic(global, chatId, topicId, result.topic);
@ -2313,9 +2289,6 @@ addActionHandler('checkChatlistInvite', async (global, actions, payload): Promis
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = updateTabState(global, { global = updateTabState(global, {
chatlistModal: { chatlistModal: {
invite: result.invite, invite: result.invite,
@ -2379,8 +2352,6 @@ addActionHandler('loadChatlistInvites', async (global, actions, payload): Promis
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = { global = {
...global, ...global,
chatFolders: { chatFolders: {
@ -2639,7 +2610,6 @@ addActionHandler('loadChannelRecommendations', async (global, actions, payload):
const chatsById = buildCollectionByKey(similarChannels, 'id'); const chatsById = buildCollectionByKey(similarChannels, 'id');
global = getGlobal(); global = getGlobal();
global = addChats(global, chatsById);
global = addSimilarChannels(global, chatId || GLOBAL_SUGGESTED_CHANNELS_ID, Object.keys(chatsById), count); global = addSimilarChannels(global, chatId || GLOBAL_SUGGESTED_CHANNELS_ID, Object.keys(chatsById), count);
setGlobal(global); setGlobal(global);
}); });
@ -2667,12 +2637,7 @@ addActionHandler('resolveBusinessChatLink', async (global, actions, payload): Pr
return; return;
} }
const { users, chats, chatLink } = result; const { chatLink } = result;
global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = addChats(global, buildCollectionByKey(chats, 'id'));
setGlobal(global);
actions.openChatWithDraft({ actions.openChatWithDraft({
chatId: chatLink.chatId, chatId: chatLink.chatId,
@ -2715,7 +2680,6 @@ addActionHandler('requestCollectibleInfo', async (global, actions, payload): Pro
async function loadChats( async function loadChats(
listType: ChatListType, listType: ChatListType,
shouldReplace = false,
isFullDraftSync?: boolean, isFullDraftSync?: boolean,
) { ) {
// eslint-disable-next-line eslint-multitab-tt/no-immediate-global // eslint-disable-next-line eslint-multitab-tt/no-immediate-global
@ -2723,24 +2687,25 @@ async function loadChats(
let lastLocalServiceMessageId = selectLastServiceNotification(global)?.id; let lastLocalServiceMessageId = selectLastServiceNotification(global)?.id;
const params = selectChatListLoadingParameters(global, listType); const params = selectChatListLoadingParameters(global, listType);
const offsetPeer = !shouldReplace && params.nextOffsetPeerId const offsetPeer = params.nextOffsetPeerId ? selectPeer(global, params.nextOffsetPeerId) : undefined;
? selectPeer(global, params.nextOffsetPeerId) : undefined; const offsetDate = params.nextOffsetDate;
const offsetDate = !shouldReplace ? params.nextOffsetDate : undefined; const offsetId = params.nextOffsetId;
const offsetId = !shouldReplace ? params.nextOffsetId : undefined;
const isFirstBatch = !offsetPeer && !offsetDate && !offsetId;
const result = listType === 'saved' ? await callApi('fetchSavedChats', { const result = listType === 'saved' ? await callApi('fetchSavedChats', {
limit: CHAT_LIST_LOAD_SLICE, limit: CHAT_LIST_LOAD_SLICE,
offsetDate, offsetDate,
offsetId, offsetId,
offsetPeer, offsetPeer,
withPinned: shouldReplace, withPinned: isFirstBatch,
}) : await callApi('fetchChats', { }) : await callApi('fetchChats', {
limit: CHAT_LIST_LOAD_SLICE, limit: CHAT_LIST_LOAD_SLICE,
offsetDate, offsetDate,
offsetId, offsetId,
offsetPeer, offsetPeer,
archived: listType === 'archived', archived: listType === 'archived',
withPinned: shouldReplace, withPinned: isFirstBatch,
lastLocalServiceMessageId, lastLocalServiceMessageId,
}); });
@ -2753,64 +2718,16 @@ async function loadChats(
global = getGlobal(); global = getGlobal();
lastLocalServiceMessageId = selectLastServiceNotification(global)?.id; lastLocalServiceMessageId = selectLastServiceNotification(global)?.id;
if (shouldReplace) { const newChats = buildCollectionByKey(result.chats, 'id');
if (listType === 'active') {
// Always include service notifications chat
if (!chatIds.includes(SERVICE_NOTIFICATIONS_USER_ID)) {
const result2 = await callApi('fetchChat', {
type: 'user',
user: SERVICE_NOTIFICATIONS_USER_MOCK,
});
global = getGlobal(); global = updateUsers(global, buildCollectionByKey(result.users, 'id'));
global = updateChats(global, newChats);
const notificationsChat = result2 && selectChat(global, result2.chatId); if (isFirstBatch) {
if (notificationsChat) { global = replaceChatListIds(global, listType, chatIds);
chatIds.unshift(notificationsChat.id); global = replaceUserStatuses(global, result.userStatusesById);
result.chats.unshift(notificationsChat);
if (lastLocalServiceMessageId) {
result.lastMessageByChatId[notificationsChat.id] = lastLocalServiceMessageId;
}
}
}
const tabStates = Object.values(global.byTabId);
const topArchivedChats = getOrderedIds(ARCHIVED_FOLDER_ID)
?.slice(0, GLOBAL_STATE_CACHE_ARCHIVED_CHAT_LIST_LIMIT)
.map((chatId) => 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);
}
} else { } 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 = updateChatListIds(global, listType, chatIds);
global = addUserStatuses(global, result.userStatusesById);
} }
global = updateChatListSecondaryInfo(global, listType, result); global = updateChatListSecondaryInfo(global, listType, result);
@ -2860,11 +2777,10 @@ export async function loadFullChat<T extends GlobalState>(
} }
const { const {
chats, users, userStatusesById, fullInfo, groupCall, membersCount, isForumAsMessages, chats, userStatusesById, fullInfo, groupCall, membersCount, isForumAsMessages,
} = result; } = result;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = updateChats(global, buildCollectionByKey(chats, 'id')); global = updateChats(global, buildCollectionByKey(chats, 'id'));
if (userStatusesById) { if (userStatusesById) {
@ -3024,8 +2940,6 @@ async function getAttachBotOrNotify<T extends GlobalState>(
return undefined; return undefined;
} }
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
setGlobal(global); setGlobal(global);
return result.bot; return result.bot;

View File

@ -1,5 +1,5 @@
import type { import type {
ApiChat, ApiGlobalMessageSearchType, ApiMessage, ApiTopic, ApiUser, ApiChat, ApiGlobalMessageSearchType, ApiMessage, ApiTopic,
ApiUserStatus, ApiUserStatus,
} from '../../../api/types'; } from '../../../api/types';
import type { ActionReturnType, GlobalState, TabArgs } from '../../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 { timestampPlusDay } from '../../../util/dates/dateFormat';
import { isDeepLink, tryParseDeepLink } from '../../../util/deepLinkParser'; import { isDeepLink, tryParseDeepLink } from '../../../util/deepLinkParser';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey } from '../../../util/iteratees';
import { throttle } from '../../../util/schedulers'; import { throttle } from '../../../util/schedulers';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { isChatChannel, isChatGroup, toChannelId } from '../../helpers/chats'; import { isChatChannel, isChatGroup, toChannelId } from '../../helpers/chats';
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { import {
addChats,
addMessages, addMessages,
addUsers,
addUserStatuses, addUserStatuses,
updateGlobalSearch, updateGlobalSearch,
updateGlobalSearchFetchingStatus, updateGlobalSearchFetchingStatus,
@ -46,13 +43,9 @@ addActionHandler('setGlobalSearchQuery', (global, actions, payload): ActionRetur
} }
const { const {
accountResultIds, globalResultIds, users, chats, accountResultIds, globalResultIds,
} = result; } = result;
global = addChats(global, buildCollectionByKey(chats, 'id'));
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = updateGlobalSearchFetchingStatus(global, { chats: false }, tabId); global = updateGlobalSearchFetchingStatus(global, { chats: false }, tabId);
global = updateGlobalSearch(global, { global = updateGlobalSearch(global, {
localResults: { localResults: {
@ -137,12 +130,9 @@ addActionHandler('searchPopularBotApps', async (global, actions, payload): Promi
return; 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, { global = updateGlobalSearch(global, {
popularBotApps: { popularBotApps: {
peerIds: [...(popularBotApps?.peerIds || []), ...peerIds], peerIds: [...(popularBotApps?.peerIds || []), ...result.peerIds],
nextOffset: result.nextOffset, nextOffset: result.nextOffset,
}, },
}, tabId); }, tabId);
@ -167,9 +157,7 @@ async function searchMessagesGlobal<T extends GlobalState>(global: T, params: {
} = params; } = params;
let result: { let result: {
messages: ApiMessage[]; messages: ApiMessage[];
users: ApiUser[];
userStatusesById?: Record<number, ApiUserStatus>; userStatusesById?: Record<number, ApiUserStatus>;
chats: ApiChat[];
topics?: ApiTopic[]; topics?: ApiTopic[];
totalTopicsCount?: number; totalTopicsCount?: number;
totalCount: number; totalCount: number;
@ -200,7 +188,7 @@ async function searchMessagesGlobal<T extends GlobalState>(global: T, params: {
if (inChatResult) { if (inChatResult) {
const { const {
messages, users, totalCount, nextOffsetId, messages, totalCount, nextOffsetId,
} = inChatResult; } = inChatResult;
const { topics: localTopics, count } = topics || {}; const { topics: localTopics, count } = topics || {};
@ -209,8 +197,6 @@ async function searchMessagesGlobal<T extends GlobalState>(global: T, params: {
topics: localTopics, topics: localTopics,
totalTopicsCount: count, totalTopicsCount: count,
messages, messages,
users,
chats: [],
totalCount, totalCount,
nextOffsetId, nextOffsetId,
}; };
@ -249,17 +235,9 @@ async function searchMessagesGlobal<T extends GlobalState>(global: T, params: {
} }
const { const {
messages, users, chats, userStatusesById, totalCount, nextOffsetRate, nextOffsetId, nextOffsetPeerId, messages, userStatusesById, totalCount, nextOffsetRate, nextOffsetId, nextOffsetPeerId,
} = result; } = result;
if (chats.length) {
global = addChats(global, buildCollectionByKey(chats, 'id'));
}
if (users.length) {
global = addUsers(global, buildCollectionByKey(users, 'id'));
}
if (userStatusesById) { if (userStatusesById) {
global = addUserStatuses(global, userStatusesById); global = addUserStatuses(global, userStatusesById);
} }

View File

@ -13,7 +13,6 @@ import { updateAppBadge } from '../../../util/appBadge';
import { MAIN_IDB_STORE, PASSCODE_IDB_STORE } from '../../../util/browser/idb'; import { MAIN_IDB_STORE, PASSCODE_IDB_STORE } from '../../../util/browser/idb';
import * as cacheApi from '../../../util/cacheApi'; import * as cacheApi from '../../../util/cacheApi';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey } from '../../../util/iteratees';
import { unsubscribe } from '../../../util/notifications'; import { unsubscribe } from '../../../util/notifications';
import { clearEncryptedSession, encryptSession, forgetPasscode } from '../../../util/passcode'; import { clearEncryptedSession, encryptSession, forgetPasscode } from '../../../util/passcode';
import { parseInitialLocationHash, resetInitialLocationHash, resetLocationHash } from '../../../util/routing'; import { parseInitialLocationHash, resetInitialLocationHash, resetLocationHash } from '../../../util/routing';
@ -35,7 +34,7 @@ import {
addActionHandler, getGlobal, setGlobal, addActionHandler, getGlobal, setGlobal,
} from '../../index'; } from '../../index';
import { import {
addUsers, clearGlobalForLockScreen, updateManagementProgress, updatePasscodeSettings, clearGlobalForLockScreen, updateManagementProgress, updatePasscodeSettings,
} from '../../reducers'; } from '../../reducers';
addActionHandler('initApi', (global, actions): ActionReturnType => { addActionHandler('initApi', (global, actions): ActionReturnType => {
@ -109,7 +108,6 @@ addActionHandler('uploadProfilePhoto', async (global, actions, payload): Promise
if (!result) return; if (!result) return;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = updateManagementProgress(global, ManagementProgress.Complete, tabId); global = updateManagementProgress(global, ManagementProgress.Complete, tabId);
setGlobal(global); setGlobal(global);

View File

@ -2,13 +2,12 @@ import type { ActionReturnType } from '../../types';
import { ManagementProgress } from '../../../types'; import { ManagementProgress } from '../../../types';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey } from '../../../util/iteratees';
import * as langProvider from '../../../util/oldLangProvider'; import * as langProvider from '../../../util/oldLangProvider';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { getUserFirstOrLastName } from '../../helpers'; import { getUserFirstOrLastName } from '../../helpers';
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { import {
addUsers, updateChat, updateChatFullInfo, updateManagement, updateManagementProgress, updateChat, updateChatFullInfo, updateManagement, updateManagementProgress,
} from '../../reducers'; } from '../../reducers';
import { import {
selectChat, selectCurrentMessageList, selectTabState, selectUser, selectChat, selectCurrentMessageList, selectTabState, selectUser,
@ -124,9 +123,7 @@ addActionHandler('loadExportedChatInvites', async (global, actions, payload): Pr
return; return;
} }
global = getGlobal(); global = getGlobal();
const { invites, users } = result; const { invites } = result;
global = addUsers(global, buildCollectionByKey(users, 'id'));
const update = isRevoked ? { revokedInvites: invites } : { invites }; const update = isRevoked ? { revokedInvites: invites } : { invites };
global = updateManagement(global, chatId, update, tabId); global = updateManagement(global, chatId, update, tabId);
@ -153,7 +150,7 @@ addActionHandler('editExportedChatInvite', async (global, actions, payload): Pro
return; return;
} }
const { oldInvite, newInvite, users } = result; const { oldInvite, newInvite } = result;
global = getGlobal(); global = getGlobal();
const { management } = selectTabState(global, tabId); const { management } = selectTabState(global, tabId);
@ -167,8 +164,6 @@ addActionHandler('editExportedChatInvite', async (global, actions, payload): Pro
invites.push(newInvite); invites.push(newInvite);
} }
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = updateManagement(global, chatId, { global = updateManagement(global, chatId, {
invites, invites,
revokedInvites, revokedInvites,
@ -269,7 +264,7 @@ addActionHandler('loadChatInviteImporters', async (
if (!result) { if (!result) {
return; return;
} }
const { importers, users } = result; const { importers } = result;
global = getGlobal(); global = getGlobal();
const currentInviteInfo = selectTabState(global, tabId).management.byChatId[chatId]?.inviteInfo; const currentInviteInfo = selectTabState(global, tabId).management.byChatId[chatId]?.inviteInfo;
@ -283,7 +278,6 @@ addActionHandler('loadChatInviteImporters', async (
importers, importers,
}, },
}, tabId); }, tabId);
global = addUsers(global, users);
setGlobal(global); setGlobal(global);
}); });
@ -308,7 +302,7 @@ addActionHandler('loadChatInviteRequesters', async (
if (!result) { if (!result) {
return; return;
} }
const { importers, users } = result; const { importers } = result;
global = getGlobal(); global = getGlobal();
const currentInviteInfo = selectTabState(global, tabId).management.byChatId[chatId]?.inviteInfo; const currentInviteInfo = selectTabState(global, tabId).management.byChatId[chatId]?.inviteInfo;
@ -321,7 +315,6 @@ addActionHandler('loadChatInviteRequesters', async (
requesters: importers, requesters: importers,
}, },
}, tabId); }, tabId);
global = addUsers(global, users);
setGlobal(global); setGlobal(global);
}); });
@ -343,11 +336,10 @@ addActionHandler('loadChatJoinRequests', async (global, actions, payload): Promi
if (!result) { if (!result) {
return; return;
} }
const { importers, users } = result; const { importers } = result;
global = getGlobal(); global = getGlobal();
global = updateChat(global, chatId, { joinRequests: importers }); global = updateChat(global, chatId, { joinRequests: importers });
global = addUsers(global, users);
setGlobal(global); setGlobal(global);
}); });
@ -443,7 +435,6 @@ addActionHandler('uploadContactProfilePhoto', async (global, actions, payload):
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
setGlobal(global); setGlobal(global);
const { id, accessHash } = user; const { id, accessHash } = user;

View File

@ -66,9 +66,7 @@ import {
} from '../../index'; } from '../../index';
import { import {
addChatMessagesById, addChatMessagesById,
addChats,
addUnreadMentions, addUnreadMentions,
addUsers,
deleteSponsoredMessage, deleteSponsoredMessage,
removeOutlyingList, removeOutlyingList,
removeRequestedMessageTranslation, removeRequestedMessageTranslation,
@ -81,7 +79,6 @@ import {
updateChat, updateChat,
updateChatFullInfo, updateChatFullInfo,
updateChatMessage, updateChatMessage,
updateChats,
updateListedIds, updateListedIds,
updateMessageTranslation, updateMessageTranslation,
updateOutlyingLists, updateOutlyingLists,
@ -95,7 +92,6 @@ import {
updateTopic, updateTopic,
updateUploadByMessageKey, updateUploadByMessageKey,
updateUserFullInfo, updateUserFullInfo,
updateUsers,
} from '../../reducers'; } from '../../reducers';
import { updateTabState } from '../../reducers/tabs'; import { updateTabState } from '../../reducers/tabs';
import { import {
@ -1014,9 +1010,6 @@ addActionHandler('loadPollOptionResults', async (global, actions, payload): Prom
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
const tabState = selectTabState(global, tabId); const tabState = selectTabState(global, tabId);
const { pollResults } = tabState; const { pollResults } = tabState;
const { voters } = tabState.pollResults; const { voters } = tabState.pollResults;
@ -1301,7 +1294,7 @@ async function loadViewportMessages<T extends GlobalState>(
} }
const { const {
messages, users, chats, count, messages, count,
} = result; } = result;
global = getGlobal(); global = getGlobal();
@ -1325,9 +1318,6 @@ async function loadViewportMessages<T extends GlobalState>(
? updateOutlyingLists(global, chatId, threadId, ids) ? updateOutlyingLists(global, chatId, threadId, ids)
: updateListedIds(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); let listedIds = selectListedIds(global, chatId, threadId);
const outlyingList = offsetId ? selectOutlyingListByMessageId(global, chatId, threadId, offsetId) : undefined; const outlyingList = offsetId ? selectOutlyingListByMessageId(global, chatId, threadId, offsetId) : undefined;
@ -1382,7 +1372,6 @@ async function loadMessage<T extends GlobalState>(
global = getGlobal(); global = getGlobal();
global = updateChatMessage(global, chat.id, messageId, result.message); global = updateChatMessage(global, chat.id, messageId, result.message);
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
setGlobal(global); setGlobal(global);
return result.message; return result.message;
@ -1498,7 +1487,7 @@ addActionHandler('loadPinnedMessages', async (global, actions, payload): Promise
return; return;
} }
const { messages, chats, users } = result; const { messages } = result;
const byId = buildCollectionByKey(messages, 'id'); const byId = buildCollectionByKey(messages, 'id');
const ids = Object.keys(byId).map(Number).sort((a, b) => b - a); 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 = getGlobal();
global = addChatMessagesById(global, chat.id, byId); global = addChatMessagesById(global, chat.id, byId);
global = safeReplacePinnedIds(global, chat.id, threadId, ids); global = safeReplacePinnedIds(global, chat.id, threadId, ids);
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = addChats(global, buildCollectionByKey(chats, 'id'));
setGlobal(global); setGlobal(global);
}); });
@ -1562,8 +1549,6 @@ addActionHandler('loadSendAs', async (global, actions, payload): Promise<void> =
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = updateChat(global, chatId, { sendAsPeerIds: result.sendAs }); global = updateChat(global, chatId, { sendAsPeerIds: result.sendAs });
setGlobal(global); setGlobal(global);
}); });
@ -1582,8 +1567,6 @@ addActionHandler('loadSponsoredMessages', async (global, actions, payload): Prom
global = getGlobal(); global = getGlobal();
global = updateSponsoredMessage(global, chatId, result.messages[0]); global = updateSponsoredMessage(global, chatId, result.messages[0]);
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
setGlobal(global); setGlobal(global);
}); });
@ -1695,15 +1678,13 @@ async function fetchUnreadMentions<T extends GlobalState>(global: T, chatId: str
if (!result) return; if (!result) return;
const { messages, chats, users } = result; const { messages } = result;
const byId = buildCollectionByKey(messages, 'id'); const byId = buildCollectionByKey(messages, 'id');
const ids = Object.keys(byId).map(Number); const ids = Object.keys(byId).map(Number);
global = getGlobal(); global = getGlobal();
global = addChatMessagesById(global, chat.id, byId); global = addChatMessagesById(global, chat.id, byId);
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = addChats(global, buildCollectionByKey(chats, 'id'));
global = addUnreadMentions(global, chatId, chat, ids); global = addUnreadMentions(global, chatId, chat, ids);
setGlobal(global); setGlobal(global);
@ -2073,8 +2054,6 @@ addActionHandler('loadMessageViews', async (global, actions, payload): Promise<v
if (!result) return; if (!result) return;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
result.viewsInfo.forEach((update) => { result.viewsInfo.forEach((update) => {
global = updateChatMessage(global, chatId, update.id, { global = updateChatMessage(global, chatId, update.id, {
viewsCount: update.views, viewsCount: update.views,
@ -2155,8 +2134,6 @@ addActionHandler('loadQuickReplies', async (global): Promise<void> => {
if (!result) return; if (!result) return;
global = getGlobal(); global = getGlobal();
global = updateUsers(global, buildCollectionByKey(result.users, 'id'));
global = updateChats(global, buildCollectionByKey(result.chats, 'id'));
global = updateQuickReplyMessages(global, buildCollectionByKey(result.messages, 'id')); global = updateQuickReplyMessages(global, buildCollectionByKey(result.messages, 'id'));
global = updateQuickReplies(global, result.quickReplies); global = updateQuickReplies(global, result.quickReplies);

View File

@ -18,9 +18,7 @@ import {
} from '../../index'; } from '../../index';
import { import {
addChatMessagesById, addChatMessagesById,
addChats,
addMessages, addMessages,
addUsers,
addUserStatuses, addUserStatuses,
initializeChatMediaSearchResults, initializeChatMediaSearchResults,
mergeWithChatMediaSearchSegment, mergeWithChatMediaSearchSegment,
@ -126,7 +124,7 @@ addActionHandler('performMiddleSearch', async (global, actions, payload): Promis
} }
const { const {
chats, users, userStatusesById, messages, totalCount, nextOffsetId, nextOffsetRate, nextOffsetPeerId, userStatusesById, messages, totalCount, nextOffsetId, nextOffsetRate, nextOffsetPeerId,
} = result; } = result;
const newFoundIds = messages.map(getSearchResultKey); const newFoundIds = messages.map(getSearchResultKey);
@ -142,8 +140,6 @@ addActionHandler('performMiddleSearch', async (global, actions, payload): Promis
const resultChatId = isSavedDialog ? currentUserId : chat.id; const resultChatId = isSavedDialog ? currentUserId : chat.id;
global = addChats(global, buildCollectionByKey(chats, 'id'));
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = addUserStatuses(global, userStatusesById); global = addUserStatuses(global, userStatusesById);
global = addMessages(global, messages); global = addMessages(global, messages);
global = updateMiddleSearch(global, resultChatId, threadId, { global = updateMiddleSearch(global, resultChatId, threadId, {
@ -300,7 +296,7 @@ async function searchSharedMedia<T extends GlobalState>(
} }
const { const {
chats, users, messages, totalCount, nextOffsetId, userStatusesById, messages, totalCount, nextOffsetId,
} = result; } = result;
const byId = buildCollectionByKey(messages, 'id'); const byId = buildCollectionByKey(messages, 'id');
@ -313,8 +309,7 @@ async function searchSharedMedia<T extends GlobalState>(
return; return;
} }
global = addChats(global, buildCollectionByKey(chats, 'id')); global = addUserStatuses(global, userStatusesById);
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = addChatMessagesById(global, resultChatId, byId); global = addChatMessagesById(global, resultChatId, byId);
global = updateSharedMediaSearchResults( global = updateSharedMediaSearchResults(
global, resultChatId, threadId, type, newFoundIds, totalCount, nextOffsetId, tabId, global, resultChatId, threadId, type, newFoundIds, totalCount, nextOffsetId, tabId,
@ -469,14 +464,13 @@ async function searchChatMedia<T extends GlobalState>(
} }
const { const {
chats, users, messages, messages, userStatusesById,
} = result; } = result;
const byId = buildCollectionByKey(messages, 'id'); const byId = buildCollectionByKey(messages, 'id');
const newFoundIds = Object.keys(byId).map(Number); const newFoundIds = Object.keys(byId).map(Number);
global = addChats(global, buildCollectionByKey(chats, 'id')); global = addUserStatuses(global, userStatusesById);
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = addChatMessagesById(global, resultChatId, byId); global = addChatMessagesById(global, resultChatId, byId);
const loadingState = calcLoadingState(direction, limit, newFoundIds.length, currentSegment); const loadingState = calcLoadingState(direction, limit, newFoundIds.length, currentSegment);

View File

@ -5,7 +5,6 @@ import { PaymentStep } from '../../../types';
import { DEBUG_PAYMENT_SMART_GLOCAL } from '../../../config'; import { DEBUG_PAYMENT_SMART_GLOCAL } from '../../../config';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey } from '../../../util/iteratees';
import * as langProvider from '../../../util/oldLangProvider'; import * as langProvider from '../../../util/oldLangProvider';
import { getStripeError } from '../../../util/payments/stripe'; import { getStripeError } from '../../../util/payments/stripe';
import { buildQueryString } from '../../../util/requestQuery'; import { buildQueryString } from '../../../util/requestQuery';
@ -15,8 +14,7 @@ import { isChatChannel, isChatSuperGroup } from '../../helpers';
import { getRequestInputInvoice, getStarsTransactionFromGift } from '../../helpers/payments'; import { getRequestInputInvoice, getStarsTransactionFromGift } from '../../helpers/payments';
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { import {
addChats, appendStarsTransactions, closeInvoice,
addUsers, appendStarsTransactions, closeInvoice,
openStarsTransactionFromReceipt, openStarsTransactionFromReceipt,
openStarsTransactionModal, openStarsTransactionModal,
setInvoiceInfo, setPaymentForm, setInvoiceInfo, setPaymentForm,
@ -105,12 +103,11 @@ async function getPaymentForm<T extends GlobalState>(
} }
const { const {
form, invoice, users, form, invoice,
} = result; } = result;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = setPaymentForm(global, form, tabId); global = setPaymentForm(global, form, tabId);
global = setPaymentStep(global, PaymentStep.Checkout, tabId); global = setPaymentStep(global, PaymentStep.Checkout, tabId);
setGlobal(global); setGlobal(global);
@ -133,7 +130,6 @@ addActionHandler('getReceipt', async (global, actions, payload): Promise<void> =
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
if (result.receipt.type === 'stars') { if (result.receipt.type === 'stars') {
global = openStarsTransactionFromReceipt(global, result.receipt, tabId); global = openStarsTransactionFromReceipt(global, result.receipt, tabId);
} else { } else {
@ -430,7 +426,6 @@ addActionHandler('openPremiumModal', async (global, actions, payload): Promise<v
if (!result) return; if (!result) return;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = updateTabState(global, { global = updateTabState(global, {
premiumModal: { premiumModal: {
@ -559,7 +554,6 @@ addActionHandler('openPremiumGiftModal', async (global, actions, payload): Promi
if (!result) return; if (!result) return;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
const gifts = await callApi('getPremiumGiftCodeOptions', {}); const gifts = await callApi('getPremiumGiftCodeOptions', {});
@ -686,8 +680,6 @@ addActionHandler('openBoostModal', async (global, actions, payload): Promise<voi
const tabState = selectTabState(global, tabId); const tabState = selectTabState(global, tabId);
if (!tabState.boostModal) return; if (!tabState.boostModal) return;
global = addChats(global, buildCollectionByKey(myBoosts.chats, 'id'));
global = addUsers(global, buildCollectionByKey(myBoosts.users, 'id'));
global = updateTabState(global, { global = updateTabState(global, {
boostModal: { boostModal: {
...tabState.boostModal, ...tabState.boostModal,
@ -726,8 +718,6 @@ addActionHandler('openBoostStatistics', async (global, actions, payload): Promis
return; return;
} }
const totalBoostUserList = [...boostListResult.users, ...boostListGiftResult.users];
global = addUsers(global, buildCollectionByKey(totalBoostUserList, 'id'));
global = updateTabState(global, { global = updateTabState(global, {
boostStatistics: { boostStatistics: {
chatId, chatId,
@ -784,7 +774,6 @@ addActionHandler('loadMoreBoosters', async (global, actions, payload): Promise<v
if (!result) return; if (!result) return;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
tabState = selectTabState(global, tabId); tabState = selectTabState(global, tabId);
if (!tabState.boostStatistics) return; if (!tabState.boostStatistics) return;
@ -895,8 +884,6 @@ addActionHandler('applyBoost', async (global, actions, payload): Promise<void> =
} }
tabState = selectTabState(global, tabId); tabState = selectTabState(global, tabId);
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
if (oldChatFullInfo) { if (oldChatFullInfo) {
global = updateChatFullInfo(global, chatId, { global = updateChatFullInfo(global, chatId, {
boostsApplied: oldBoostsApplied + slots.length, boostsApplied: oldBoostsApplied + slots.length,
@ -930,8 +917,6 @@ addActionHandler('checkGiftCode', async (global, actions, payload): Promise<void
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = updateTabState(global, { global = updateTabState(global, {
giftCodeModal: { giftCodeModal: {
slug, slug,
@ -1003,8 +988,6 @@ addActionHandler('loadStarStatus', async (global): Promise<void> => {
} }
global = getGlobal(); global = getGlobal();
global = addChats(global, buildCollectionByKey(status.chats, 'id'));
global = addUsers(global, buildCollectionByKey(status.users, 'id'));
global = { global = {
...global, ...global,
@ -1043,8 +1026,6 @@ addActionHandler('loadStarsTransactions', async (global, actions, payload): Prom
} }
global = getGlobal(); global = getGlobal();
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = updateStarsBalance(global, result.balance); global = updateStarsBalance(global, result.balance);
if (result.history) { if (result.history) {

View File

@ -20,7 +20,7 @@ import {
} from '../../helpers'; } from '../../helpers';
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { import {
addChatMessagesById, addChats, addUsers, updateChat, updateChatMessage, addChatMessagesById, updateChat, updateChatMessage,
} from '../../reducers'; } from '../../reducers';
import { addMessageReaction, subtractXForEmojiInteraction, updateUnreadReactions } from '../../reducers/reactions'; import { addMessageReaction, subtractXForEmojiInteraction, updateUnreadReactions } from '../../reducers/reactions';
import { updateTabState } from '../../reducers/tabs'; import { updateTabState } from '../../reducers/tabs';
@ -336,10 +336,6 @@ addActionHandler('loadReactors', async (global, actions, payload): Promise<void>
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = updateChatMessage(global, chatId, messageId, { global = updateChatMessage(global, chatId, messageId, {
reactors: result, reactors: result,
}); });
@ -409,15 +405,13 @@ addActionHandler('fetchUnreadReactions', async (global, actions, payload): Promi
return; return;
} }
const { messages, chats, users } = result; const { messages } = result;
const byId = buildCollectionByKey(messages, 'id'); const byId = buildCollectionByKey(messages, 'id');
const ids = Object.keys(byId).map(Number); const ids = Object.keys(byId).map(Number);
global = getGlobal(); global = getGlobal();
global = addChatMessagesById(global, chat.id, byId); global = addChatMessagesById(global, chat.id, byId);
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = addChats(global, buildCollectionByKey(chats, 'id'));
global = updateUnreadReactions(global, chatId, { global = updateUnreadReactions(global, chatId, {
unreadReactions: unique([...(chat.unreadReactions || []), ...ids]).sort((a, b) => b - a), unreadReactions: unique([...(chat.unreadReactions || []), ...ids]).sort((a, b) => b - a),
}); });

View File

@ -1,4 +1,4 @@
import type { ApiUser, ApiUsername } from '../../../api/types'; import type { ApiUsername } from '../../../api/types';
import type { import type {
ApiPrivacySettings, ApiPrivacySettings,
} from '../../../types'; } from '../../../types';
@ -19,8 +19,8 @@ import { callApi } from '../../../api/gramjs';
import { buildApiInputPrivacyRules } from '../../helpers'; import { buildApiInputPrivacyRules } from '../../helpers';
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { import {
addBlockedUser, addNotifyExceptions, addUsers, deletePeerPhoto, addBlockedUser, addNotifyExceptions, deletePeerPhoto,
removeBlockedUser, replaceSettings, updateChat, updateChats, removeBlockedUser, replaceSettings, updateChat,
updateNotifySettings, updateUser, updateUserFullInfo, updateNotifySettings, updateUser, updateUserFullInfo,
} from '../../reducers'; } from '../../reducers';
import { updateTabState } from '../../reducers/tabs'; import { updateTabState } from '../../reducers/tabs';
@ -47,12 +47,7 @@ addActionHandler('updateProfile', async (global, actions, payload): Promise<void
setGlobal(global); setGlobal(global);
if (photo) { if (photo) {
const result = await callApi('uploadProfilePhoto', photo); await callApi('uploadProfilePhoto', photo);
if (result) {
global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
setGlobal(global);
}
} }
if (firstName || lastName || about) { if (firstName || lastName || about) {
@ -119,10 +114,6 @@ addActionHandler('updateProfilePhoto', async (global, actions, payload): Promise
const result = await callApi('updateProfilePhoto', photo, isFallback); const result = await callApi('updateProfilePhoto', photo, isFallback);
if (!result) return; if (!result) return;
const { users } = result;
global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id'));
setGlobal(global);
actions.loadFullUser({ userId: currentUserId, withPhotos: true }); actions.loadFullUser({ userId: currentUserId, withPhotos: true });
}); });
@ -261,13 +252,6 @@ addActionHandler('loadBlockedUsers', async (global): Promise<void> => {
global = getGlobal(); 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 = {
...global, ...global,
blocked: { blocked: {
@ -425,14 +409,10 @@ addActionHandler('loadPrivacySettings', async (global): Promise<void> => {
bioSettings, bioSettings,
birthdaySettings, birthdaySettings,
] = result as { ] = result as {
users: ApiUser[];
rules: ApiPrivacySettings; rules: ApiPrivacySettings;
}[]; }[];
const allUsers = result.flatMap((e) => e!.users);
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(allUsers, 'id'));
global = { global = {
...global, ...global,
settings: { settings: {
@ -466,7 +446,6 @@ addActionHandler('setPrivacyVisibility', async (global, actions, payload): Promi
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = { global = {
...global, ...global,
settings: { settings: {
@ -502,7 +481,6 @@ addActionHandler('setPrivacyVisibility', async (global, actions, payload): Promi
onSuccess?.(); onSuccess?.();
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = { global = {
...global, ...global,
settings: { settings: {
@ -542,7 +520,6 @@ addActionHandler('setPrivacySettings', async (global, actions, payload): Promise
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = { global = {
...global, ...global,
settings: { settings: {

View File

@ -1,11 +1,8 @@
import { areDeepEqual } from '../../../util/areDeepEqual'; import { areDeepEqual } from '../../../util/areDeepEqual';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey } from '../../../util/iteratees';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { import {
addChats,
addUsers,
updateChannelMonetizationStatistics, updateChannelMonetizationStatistics,
updateMessageStatistics, updateMessageStatistics,
updateStatistics, updateStatistics,
@ -36,10 +33,8 @@ addActionHandler('loadStatistics', async (global, actions, payload): Promise<voi
return; return;
} }
const { stats } = result;
global = getGlobal(); global = getGlobal();
const { stats, users } = result;
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = updateStatistics(global, chatId, stats, tabId); global = updateStatistics(global, chatId, stats, tabId);
setGlobal(global); setGlobal(global);
}); });
@ -212,8 +207,6 @@ addActionHandler('loadStoryPublicForwards', async (global, actions, payload): Pr
const { const {
publicForwards, publicForwards,
users,
chats,
count, count,
nextOffset, nextOffset,
} = await callApi('fetchStoryPublicForwards', { } = await callApi('fetchStoryPublicForwards', {
@ -221,13 +214,6 @@ addActionHandler('loadStoryPublicForwards', async (global, actions, payload): Pr
}) || {}; }) || {};
global = getGlobal(); global = getGlobal();
if (chats) {
global = addChats(global, buildCollectionByKey(chats, 'id'));
}
if (users) {
global = addUsers(global, buildCollectionByKey(users, 'id'));
}
global = updateStoryStatistics(global, { global = updateStoryStatistics(global, {
...stats, ...stats,
publicForwards: count || publicForwards?.length, publicForwards: count || publicForwards?.length,

View File

@ -2,17 +2,14 @@ import type { ActionReturnType } from '../../types';
import { DEBUG } from '../../../config'; import { DEBUG } from '../../../config';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey } from '../../../util/iteratees';
import { oldTranslate } from '../../../util/oldLangProvider'; import { oldTranslate } from '../../../util/oldLangProvider';
import { getServerTime } from '../../../util/serverTime'; import { getServerTime } from '../../../util/serverTime';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { buildApiInputPrivacyRules } from '../../helpers'; import { buildApiInputPrivacyRules } from '../../helpers';
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { import {
addChats,
addStories, addStories,
addStoriesForPeer, addStoriesForPeer,
addUsers,
removePeerStory, removePeerStory,
updateLastReadStoryForPeer, updateLastReadStoryForPeer,
updateLastViewedStoryForPeer, updateLastViewedStoryForPeer,
@ -67,8 +64,6 @@ addActionHandler('loadAllStories', async (global): Promise<void> => {
global.stories.stateHash = result.state; global.stories.stateHash = result.state;
if ('peerStories' in result) { if ('peerStories' in result) {
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = addStories(global, result.peerStories); global = addStories(global, result.peerStories);
global = updatePeersWithStories(global, result.peerStories); global = updatePeersWithStories(global, result.peerStories);
global = updateStealthMode(global, result.stealthMode); global = updateStealthMode(global, result.stealthMode);
@ -112,8 +107,6 @@ addActionHandler('loadAllHiddenStories', async (global): Promise<void> => {
global.stories.archiveStateHash = result.state; global.stories.archiveStateHash = result.state;
if ('peerStories' in result) { if ('peerStories' in result) {
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = addStories(global, result.peerStories); global = addStories(global, result.peerStories);
global = updatePeersWithStories(global, result.peerStories); global = updatePeersWithStories(global, result.peerStories);
global = updateStealthMode(global, result.stealthMode); global = updateStealthMode(global, result.stealthMode);
@ -153,8 +146,6 @@ addActionHandler('loadPeerSkippedStories', async (global, actions, payload): Pro
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = addStoriesForPeer(global, peerId, result.stories, result.pinnedIds); global = addStoriesForPeer(global, peerId, result.stories, result.pinnedIds);
setGlobal(global); setGlobal(global);
}); });
@ -295,8 +286,6 @@ addActionHandler('loadPeerStories', async (global, actions, payload): Promise<vo
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = addStoriesForPeer(global, peerId, result.stories); global = addStoriesForPeer(global, peerId, result.stories);
if (result.lastReadStoryId) { if (result.lastReadStoryId) {
global = updateLastReadStoryForPeer(global, peerId, result.lastReadStoryId); global = updateLastReadStoryForPeer(global, peerId, result.lastReadStoryId);
@ -322,8 +311,6 @@ addActionHandler('loadPeerProfileStories', async (global, actions, payload): Pro
global = updatePeerStoriesFullyLoaded(global, peerId, true); global = updatePeerStoriesFullyLoaded(global, peerId, true);
} }
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = addStoriesForPeer(global, peerId, result.stories, result.pinnedIds); global = addStoriesForPeer(global, peerId, result.stories, result.pinnedIds);
setGlobal(global); setGlobal(global);
}); });
@ -343,8 +330,6 @@ addActionHandler('loadStoriesArchive', async (global, actions, payload): Promise
if (Object.values(result.stories).length === 0) { if (Object.values(result.stories).length === 0) {
global = updatePeerStoriesFullyLoaded(global, peerId, true, true); global = updatePeerStoriesFullyLoaded(global, peerId, true, true);
} }
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = addStoriesForPeer(global, peerId, result.stories, undefined, true); global = addStoriesForPeer(global, peerId, result.stories, undefined, true);
setGlobal(global); setGlobal(global);
}); });
@ -362,8 +347,6 @@ addActionHandler('loadPeerStoriesByIds', async (global, actions, payload): Promi
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = addStoriesForPeer(global, peerId, result.stories); global = addStoriesForPeer(global, peerId, result.stories);
setGlobal(global); setGlobal(global);
}); });
@ -382,7 +365,6 @@ addActionHandler('loadStoryViews', async (global, actions, payload): Promise<voi
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = updatePeerStoryViews(global, peerId, storyId, result.views); global = updatePeerStoryViews(global, peerId, storyId, result.views);
setGlobal(global); setGlobal(global);
}); });
@ -424,8 +406,6 @@ addActionHandler('loadStoryViewList', async (global, actions, payload): Promise<
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = updateStoryViews(global, storyId, result.views, result.nextOffset, tabId); global = updateStoryViews(global, storyId, result.views, result.nextOffset, tabId);
setGlobal(global); setGlobal(global);
}); });

View File

@ -72,8 +72,7 @@ addActionHandler('sync', (global, actions): ActionReturnType => {
loadAllChats({ loadAllChats({
listType: 'active', listType: 'active',
shouldReplace: true, onFirstBatchDone: async () => {
onReplace: async () => {
await loadAndReplaceMessages(global, actions); await loadAndReplaceMessages(global, actions);
global = getGlobal(); global = getGlobal();
@ -90,8 +89,8 @@ addActionHandler('sync', (global, actions): ActionReturnType => {
console.log('>>> FINISH SYNC'); console.log('>>> FINISH SYNC');
} }
loadAllChats({ listType: 'archived', shouldReplace: true }); loadAllChats({ listType: 'archived' });
loadAllChats({ listType: 'saved', shouldReplace: true }); loadAllChats({ listType: 'saved' });
preloadTopChatMessages(); preloadTopChatMessages();
loadAllStories(); loadAllStories();
loadAllHiddenStories(); loadAllHiddenStories();

View File

@ -15,8 +15,6 @@ import {
setGlobal, setGlobal,
} from '../../index'; } from '../../index';
import { import {
addChats,
addUsers,
addUserStatuses, addUserStatuses,
closeNewContactDialog, closeNewContactDialog,
replaceUserStatuses, replaceUserStatuses,
@ -113,10 +111,9 @@ addActionHandler('loadTopUsers', async (global): Promise<void> => {
return; return;
} }
const { ids, users } = result; const { ids } = result;
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = { global = {
...global, ...global,
topPeers: { topPeers: {
@ -135,8 +132,6 @@ addActionHandler('loadContactList', async (global): Promise<void> => {
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(contactList.users, 'id'));
global = addChats(global, buildCollectionByKey(contactList.chats, 'id'));
global = addUserStatuses(global, contactList.userStatusesById); global = addUserStatuses(global, contactList.userStatusesById);
// Sort contact list by Last Name (or First Name), with latin names being placed first // 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<vo
return; return;
} }
const { chats, chatIds, isFullyLoaded } = result; const { chatIds, isFullyLoaded } = result;
global = getGlobal(); global = getGlobal();
if (chats.length) {
global = addChats(global, buildCollectionByKey(chats, 'id'));
}
global = updateUser(global, user.id, { global = updateUser(global, user.id, {
commonChats: { commonChats: {
maxId: chatIds.length ? chatIds[chatIds.length - 1] : '0', maxId: chatIds.length ? chatIds[chatIds.length - 1] : '0',
@ -315,11 +307,9 @@ addActionHandler('loadMoreProfilePhotos', async (global, actions, payload): Prom
global = getGlobal(); global = getGlobal();
const { const {
photos, users, count, nextOffsetId, photos, count, nextOffsetId,
} = result; } = result;
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = updatePeerPhotos(global, peerId, { global = updatePeerPhotos(global, peerId, {
newPhotos: photos, newPhotos: photos,
count, count,
@ -349,12 +339,9 @@ addActionHandler('setUserSearchQuery', (global, actions, payload): ActionReturnT
} }
const { const {
users, chats, accountResultIds, globalResultIds, accountResultIds, globalResultIds,
} = result; } = result;
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = addChats(global, buildCollectionByKey(chats, 'id'));
const localUserIds = accountResultIds.filter(isUserId); const localUserIds = accountResultIds.filter(isUserId);
const globalUserIds = globalResultIds.filter(isUserId); const globalUserIds = globalResultIds.filter(isUserId);

View File

@ -8,13 +8,12 @@ import {
joinPhoneCall, processSignalingMessage, joinPhoneCall, processSignalingMessage,
} from '../../../lib/secret-sauce'; } from '../../../lib/secret-sauce';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey, omit } from '../../../util/iteratees'; import { omit } from '../../../util/iteratees';
import * as langProvider from '../../../util/oldLangProvider'; import * as langProvider from '../../../util/oldLangProvider';
import { EMOJI_DATA, EMOJI_OFFSETS } from '../../../util/phoneCallEmojiConstants'; import { EMOJI_DATA, EMOJI_OFFSETS } from '../../../util/phoneCallEmojiConstants';
import { ARE_CALLS_SUPPORTED } from '../../../util/windowEnvironment'; import { ARE_CALLS_SUPPORTED } from '../../../util/windowEnvironment';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { addUsers } from '../../reducers';
import { updateGroupCall, updateGroupCallParticipant } from '../../reducers/calls'; import { updateGroupCall, updateGroupCallParticipant } from '../../reducers/calls';
import { updateTabState } from '../../reducers/tabs'; import { updateTabState } from '../../reducers/tabs';
import { selectActiveGroupCall, selectGroupCallParticipant, selectPhoneCallUser } from '../../selectors/calls'; import { selectActiveGroupCall, selectGroupCallParticipant, selectPhoneCallUser } from '../../selectors/calls';
@ -143,14 +142,9 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
}; };
setGlobal(global); setGlobal(global);
const result = await callApi('confirmCall', { callApi('confirmCall', {
call, gA, keyFingerprint, call, gA, keyFingerprint,
}); });
if (result) {
global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
setGlobal(global);
}
})(); })();
} else if (state === 'active' && connections && phoneCall?.state !== 'active') { } else if (state === 'active' && connections && phoneCall?.state !== 'active') {
if (!isOutgoing) { if (!isOutgoing) {

View File

@ -45,7 +45,8 @@ const TYPING_STATUS_CLEAR_DELAY = 6000; // 6 seconds
addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
switch (update['@type']) { switch (update['@type']) {
case 'updateChat': { 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 if (update.chat.lastReadOutboxMessageId && lastReadOutboxMessageId
&& 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); global = updateChat(global, update.id, update.chat);
if (localChat?.areStoriesHidden !== update.chat.areStoriesHidden) { if (localChat?.areStoriesHidden !== update.chat.areStoriesHidden) {
@ -65,8 +64,10 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
setGlobal(global); setGlobal(global);
if (!update.noTopChatsRequest && !selectIsChatListed(global, update.id)) { const updatedChat = selectChat(global, update.id);
// Chat can appear in dialogs list. if (!update.noTopChatsRequest && updatedChat && !selectIsChatListed(global, update.id)
&& !updatedChat.isNotJoined) {
// Reload top chats to update chat listing
actions.loadTopChats(); actions.loadTopChats();
} }

View File

@ -423,33 +423,31 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
break; break;
} }
case 'updateThreadInfos': { case 'updateThreadInfo': {
const { const {
threadInfoUpdates, threadInfo,
} = update; } = update;
global = updateThreadInfos(global, threadInfoUpdates); global = updateThreadInfos(global, [threadInfo]);
threadInfoUpdates.forEach((threadInfo) => { const { chatId, threadId } = threadInfo;
const { chatId, threadId } = threadInfo; if (!chatId || !threadId) return;
if (!chatId || !threadId) return;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
const currentThreadInfo = selectThreadInfo(global, chatId, threadId); const currentThreadInfo = selectThreadInfo(global, chatId, threadId);
if (chat?.isForum && threadInfo.lastReadInboxMessageId !== currentThreadInfo?.lastReadInboxMessageId) { if (chat?.isForum && threadInfo.lastReadInboxMessageId !== currentThreadInfo?.lastReadInboxMessageId) {
actions.loadTopicById({ chatId, topicId: Number(threadId) }); actions.loadTopicById({ chatId, topicId: Number(threadId) });
} }
// Update reply thread last read message id if already read in main thread // Update reply thread last read message id if already read in main thread
if (!chat?.isForum) { if (!chat?.isForum) {
const lastReadInboxMessageId = chat?.lastReadInboxMessageId; const lastReadInboxMessageId = chat?.lastReadInboxMessageId;
const lastReadInboxMessageIdInThread = threadInfo.lastReadInboxMessageId || lastReadInboxMessageId; const lastReadInboxMessageIdInThread = threadInfo.lastReadInboxMessageId || lastReadInboxMessageId;
if (lastReadInboxMessageId && lastReadInboxMessageIdInThread) { if (lastReadInboxMessageId && lastReadInboxMessageIdInThread) {
global = updateThreadInfo(global, chatId, threadId, { global = updateThreadInfo(global, chatId, threadId, {
lastReadInboxMessageId: Math.max(lastReadInboxMessageIdInThread, lastReadInboxMessageId), lastReadInboxMessageId: Math.max(lastReadInboxMessageIdInThread, lastReadInboxMessageId),
}); });
}
} }
}); }
setGlobal(global); setGlobal(global);
break; break;

View File

@ -4,7 +4,9 @@ import { PaymentStep } from '../../../types';
import { addActionHandler, setGlobal } from '../../index'; import { addActionHandler, setGlobal } from '../../index';
import { import {
addBlockedUser, addBlockedUser,
addChats,
addStoriesForPeer, addStoriesForPeer,
addUsers,
removeBlockedUser, removeBlockedUser,
removePeerStory, removePeerStory,
setConfirmPaymentUrl, setConfirmPaymentUrl,
@ -13,11 +15,21 @@ import {
updatePeerStory, updatePeerStory,
updatePeersWithStories, updatePeersWithStories,
updateStealthMode, updateStealthMode,
updateThreadInfos,
} from '../../reducers'; } from '../../reducers';
import { selectPeerStories, selectPeerStory } from '../../selectors'; import { selectPeerStories, selectPeerStory } from '../../selectors';
addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
switch (update['@type']) { 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': case 'updatePeerBlocked':
if (update.isBlocked) { if (update.isBlocked) {
return addBlockedUser(global, update.id); return addBlockedUser(global, update.id);

View File

@ -7,7 +7,7 @@ import type {
import { requestNextMutation } from '../../../lib/fasterdom/fasterdom'; import { requestNextMutation } from '../../../lib/fasterdom/fasterdom';
import { copyTextToClipboard } from '../../../util/clipboard'; import { copyTextToClipboard } from '../../../util/clipboard';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey, omit } from '../../../util/iteratees'; import { omit } from '../../../util/iteratees';
import * as langProvider from '../../../util/oldLangProvider'; import * as langProvider from '../../../util/oldLangProvider';
import safePlay from '../../../util/safePlay'; import safePlay from '../../../util/safePlay';
import { ARE_CALLS_SUPPORTED } from '../../../util/windowEnvironment'; import { ARE_CALLS_SUPPORTED } from '../../../util/windowEnvironment';
@ -17,7 +17,6 @@ import {
addActionHandler, getGlobal, addActionHandler, getGlobal,
setGlobal, setGlobal,
} from '../../index'; } from '../../index';
import { addChats, addUsers } from '../../reducers';
import { updateGroupCall } from '../../reducers/calls'; import { updateGroupCall } from '../../reducers/calls';
import { updateTabState } from '../../reducers/tabs'; import { updateTabState } from '../../reducers/tabs';
import { import {
@ -107,31 +106,19 @@ async function fetchGroupCall<T extends GlobalState>(global: T, groupCall: Parti
undefined, undefined,
existingGroupCall?.isLoaded ? undefined : result.groupCall.participantsCount, existingGroupCall?.isLoaded ? undefined : result.groupCall.participantsCount,
); );
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
setGlobal(global); setGlobal(global);
return result.groupCall; return result.groupCall;
} }
async function fetchGroupCallParticipants<T extends GlobalState>( function requestGroupCallParticipants(
global: T,
groupCall: Partial<ApiGroupCall>, nextOffset?: string, groupCall: Partial<ApiGroupCall>, nextOffset?: string,
) { ) {
const result = await callApi('fetchGroupCallParticipants', { return callApi('fetchGroupCallParticipants', {
call: groupCall as ApiGroupCall, call: groupCall as ApiGroupCall,
offset: nextOffset, 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 => { addActionHandler('toggleGroupCallPanel', (global, actions, payload): ActionReturnType => {
@ -150,7 +137,7 @@ addActionHandler('subscribeToGroupCallUpdates', async (global, actions, payload)
if (subscribed) { if (subscribed) {
await fetchGroupCall(global, groupCall); await fetchGroupCall(global, groupCall);
global = getGlobal(); global = getGlobal();
await fetchGroupCallParticipants(global, groupCall); await requestGroupCallParticipants(groupCall);
} }
await callApi('toggleGroupCallStartSubscription', { await callApi('toggleGroupCallStartSubscription', {
@ -373,7 +360,7 @@ addActionHandler('loadMoreGroupCallParticipants', (global): ActionReturnType =>
return; return;
} }
void fetchGroupCallParticipants(global, groupCall, groupCall.nextOffset); void requestGroupCallParticipants(groupCall, groupCall.nextOffset);
}); });
addActionHandler('requestMasterAndRequestCall', (global, actions, payload): ActionReturnType => { addActionHandler('requestMasterAndRequestCall', (global, actions, payload): ActionReturnType => {

View File

@ -2,11 +2,11 @@ import type { ActionReturnType } from '../../types';
import { copyTextToClipboard } from '../../../util/clipboard'; import { copyTextToClipboard } from '../../../util/clipboard';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { buildCollectionByKey, omit } from '../../../util/iteratees'; import { omit } from '../../../util/iteratees';
import * as langProvider from '../../../util/oldLangProvider'; import * as langProvider from '../../../util/oldLangProvider';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { addChats, addStoriesForPeer, addUsers } from '../../reducers'; import { addStoriesForPeer } from '../../reducers';
import { updateTabState } from '../../reducers/tabs'; import { updateTabState } from '../../reducers/tabs';
import { import {
selectCurrentViewedStory, selectCurrentViewedStory,
@ -39,8 +39,6 @@ addActionHandler('openStoryViewer', async (global, actions, payload): Promise<vo
return; return;
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = addStoriesForPeer(global, peerId, result.stories); global = addStoriesForPeer(global, peerId, result.stories);
} }

View File

@ -576,15 +576,9 @@ export function updateThreadInfo<T extends GlobalState>(
...update, ...update,
} as ApiThreadInfo; } as ApiThreadInfo;
if (!doNotUpdateLinked) { if (!doNotUpdateLinked && !newThreadInfo.isCommentsInfo) {
const linkedUpdate = pick(newThreadInfo, ['messagesCount', 'lastMessageId', 'lastReadInboxMessageId']); const linkedUpdate = pick(newThreadInfo, ['messagesCount', 'lastMessageId', 'lastReadInboxMessageId']);
if (newThreadInfo.isCommentsInfo) { if (newThreadInfo.fromChannelId && newThreadInfo.fromMessageId) {
if (newThreadInfo.threadId) {
global = updateThreadInfo(
global, newThreadInfo.chatId, newThreadInfo.threadId, linkedUpdate, true,
);
}
} else if (newThreadInfo.fromChannelId && newThreadInfo.fromMessageId) {
global = updateThreadInfo( global = updateThreadInfo(
global, newThreadInfo.fromChannelId, newThreadInfo.fromMessageId, linkedUpdate, true, global, newThreadInfo.fromChannelId, newThreadInfo.fromMessageId, linkedUpdate, true,
); );

View File

@ -5,7 +5,7 @@ import type { GlobalState, TabArgs, TabState } from '../types';
import { areDeepEqual } from '../../util/areDeepEqual'; import { areDeepEqual } from '../../util/areDeepEqual';
import { getCurrentTabId } from '../../util/establishMultitabRole'; 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 { MEMO_EMPTY_ARRAY } from '../../util/memo';
import { selectTabState } from '../selectors'; import { selectTabState } from '../selectors';
import { updateChat } from './chats'; import { updateChat } from './chats';
@ -26,19 +26,19 @@ function updateContactList<T extends GlobalState>(global: T, updatedUsers: ApiUs
if (!contactUserIds) return global; if (!contactUserIds) return global;
const newContactUserIds = updatedUsers const contactUserIdsFromUpdate = updatedUsers
.filter((user) => user?.isContact && !contactUserIds.includes(user.id)) .filter((user) => user?.isContact)
.map((user) => user.id); .map((user) => user.id);
if (newContactUserIds.length === 0) return global; if (contactUserIdsFromUpdate.length === 0) return global;
return { return {
...global, ...global,
contactList: { contactList: {
userIds: [ userIds: unique([
...newContactUserIds, ...contactUserIdsFromUpdate,
...contactUserIds, ...contactUserIds,
], ]),
}, },
}; };
} }
@ -244,14 +244,9 @@ export function updateUserFullInfo<T extends GlobalState>(
export function addUserStatuses<T extends GlobalState>(global: T, newById: Record<string, ApiUserStatus>): T { export function addUserStatuses<T extends GlobalState>(global: T, newById: Record<string, ApiUserStatus>): T {
const { statusesById } = global.users; const { statusesById } = global.users;
const newKeys = Object.keys(newById).filter((id) => !statusesById[id]);
if (!newKeys.length) {
return global;
}
global = replaceUserStatuses(global, { global = replaceUserStatuses(global, {
...statusesById, ...statusesById,
...pick(newById, newKeys), ...newById,
}); });
return global; return global;

View File

@ -1437,8 +1437,7 @@ export interface ActionPayloads {
preloadTopChatMessages: undefined; preloadTopChatMessages: undefined;
loadAllChats: { loadAllChats: {
listType: ChatListType; listType: ChatListType;
onReplace?: VoidFunction; onFirstBatchDone?: VoidFunction;
shouldReplace?: boolean;
}; };
openChatWithInfo: ActionPayloads['openChat'] & { openChatWithInfo: ActionPayloads['openChat'] & {
profileTab?: ProfileTabType; profileTab?: ProfileTabType;