[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 usersById: Record<string, ApiUser> = {};
mtpUsers.forEach((mtpUser) => {
const user = buildApiUser(mtpUser);
if (!user) {
return;
}
const duplicateUser = usersById[user.id];
if (!duplicateUser || duplicateUser.isMin) {
usersById[user.id] = user;
}
if ('status' in mtpUser) {
userStatusesById[user.id] = buildApiUserStatus(mtpUser.status);
const userId = buildApiPeerId(mtpUser.id, 'user');
userStatusesById[userId] = buildApiUserStatus(mtpUser.status);
}
});
return { users: Object.values(usersById), userStatusesById };
return userStatusesById;
}
export function buildApiPremiumGiftOption(option: GramJs.TypePremiumGiftOption): ApiPremiumGiftOption {

View File

@ -199,16 +199,6 @@ export function addUserToLocalDb(user: GramJs.User) {
localDb.users[id] = user;
}
export function addEntitiesToLocalDb(entities: (GramJs.TypeUser | GramJs.TypeChat)[]) {
entities.forEach((entity) => {
if (entity instanceof GramJs.User) {
addUserToLocalDb(entity);
} else if ((entity instanceof GramJs.Chat || entity instanceof GramJs.Channel)) {
addChatToLocalDb(entity);
}
});
}
export function addWebDocumentToLocalDb(webDocument: GramJs.TypeWebDocument) {
localDb.webDocuments[webDocument.url] = webDocument;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,106 +3,42 @@ export {
setForceHttpTransport, setShouldDebugExportedSenders, setAllowHttpTransport, requestChannelDifference,
} from './client';
export * from './account';
export {
provideAuthPhoneNumber, provideAuthCode, provideAuthPassword, provideAuthRegistration, restartAuth, restartAuthWithQr,
} from './auth';
export {
fetchChats, fetchFullChat, searchChats, requestChatUpdate, fetchChatSettings,
saveDraft, fetchChat, updateChatMutedState, updateTopicMutedState, fetchMember,
createChannel, joinChannel, deleteChatUser, deleteChat, leaveChannel, deleteChannel, createGroupChat, editChatPhoto,
toggleChatPinned, toggleChatArchived, toggleDialogUnread, setChatEnabledReactions,
fetchChatFolders, editChatFolder, deleteChatFolder, sortChatFolders, fetchRecommendedChatFolders,
getChatByUsername, togglePreHistoryHidden, updateChatDefaultBannedRights, updateChatMemberBannedRights,
updateChatTitle, updateChatAbout, toggleSignatures, updateChatAdmin, fetchGroupsForDiscussion, setDiscussionGroup,
migrateChat, openChatByInvite, fetchMembers, importChatInvite, addChatMembers, deleteChatMember, toggleIsProtected,
getChatByPhoneNumber, toggleJoinToSend, toggleJoinRequest, fetchTopics, deleteTopic, togglePinnedTopic,
editTopic, toggleForum, fetchTopicById, createTopic, toggleParticipantsHidden, checkChatlistInvite,
joinChatlistInvite, createChalistInvite, editChatlistInvite, deleteChatlistInvite, fetchChatlistInvites,
fetchLeaveChatlistSuggestions, leaveChatlist, togglePeerTranslations, setViewForumAsMessages,
fetchChannelRecommendations, fetchSavedChats, toggleSavedDialogPinned, reportSponsoredMessage,
} from './chats';
export {
fetchMessages, fetchMessage, sendMessage, pinMessage, unpinAllMessages, deleteMessages, deleteHistory,
markMessageListRead, markMessagesRead, searchMessagesInChat, searchMessagesGlobal, searchHashtagPosts,
fetchWebPagePreview, editMessage, forwardMessages, loadPollOptionResults, sendPollVote, findFirstMessageIdAfterDate,
fetchPinnedMessages, fetchScheduledHistory, sendScheduledMessages, rescheduleMessage, deleteScheduledMessages,
reportMessages, sendMessageAction, fetchSeenBy, fetchSponsoredMessages, viewSponsoredMessage, fetchSendAs,
saveDefaultSendAs, fetchUnreadReactions, readAllReactions, fetchUnreadMentions, readAllMentions, transcribeAudio,
closePoll, fetchExtendedMedia, translateText, fetchMessageViews, fetchDiscussionMessage, clickSponsoredMessage,
fetchOutboxReadDate, exportMessageLink, fetchQuickReplies, sendQuickReply, fetchFactChecks,
deleteSavedHistory,
} from './messages';
export {
fetchFullUser, fetchNearestCountry, fetchTopUsers, fetchContactList, fetchUsers,
updateContact, importContact, deleteContact, fetchProfilePhotos, fetchCommonChats, reportSpam, updateEmojiStatus,
saveCloseFriends,
} from './users';
export {
fetchStickerSets, fetchRecentStickers, fetchFavoriteStickers, fetchFeaturedStickers, fetchRecentEmojiStatuses,
faveSticker, fetchStickers, fetchSavedGifs, saveGif, searchStickers, installStickerSet, uninstallStickerSet,
searchGifs, fetchAnimatedEmojis, fetchStickersForEmoji, fetchEmojiKeywords, fetchAnimatedEmojiEffects,
removeRecentSticker, clearRecentStickers, fetchCustomEmoji, fetchPremiumGifts, fetchCustomEmojiSets,
fetchFeaturedEmojiStickers, fetchGenericEmojiEffects, fetchDefaultTopicIcons, fetchDefaultStatusEmojis,
} from './symbols';
export {
checkChatUsername, setChatUsername, updatePrivateLink, deactivateAllUsernames,
fetchExportedChatInvites, editExportedChatInvite, exportChatInvite, deleteExportedChatInvite,
deleteRevokedExportedChatInvites, fetchChatInviteImporters, hideChatJoinRequest, hideAllChatJoinRequests,
hideChatReportPanel,
} from './management';
export * from './settings';
export {
getPasswordInfo, checkPassword, clearPassword, updatePassword, updateRecoveryEmail, provideRecoveryEmailCode,
} from './twoFaSettings';
export {
answerCallbackButton, setBotInfo, fetchTopInlineBots, fetchPreviewMedias, fetchInlineBot, fetchInlineBotResults,
sendInlineBotResult, startBot, requestMainWebView, fetchPopularAppBots, fetchTopBotApps,
requestWebView, requestSimpleWebView, sendWebViewData, prolongWebView, loadAttachBots, toggleAttachBot, fetchBotApp,
requestBotUrlAuth, requestLinkUrlAuth, acceptBotUrlAuth, acceptLinkUrlAuth, loadAttachBot, requestAppWebView,
allowBotSendMessages, fetchBotCanSendMessage, invokeWebViewCustomMethod,
} from './bots';
export {
getGroupCall, joinGroupCall, discardGroupCall, createGroupCall,
editGroupCallTitle, editGroupCallParticipant, exportGroupCallInvite, fetchGroupCallParticipants,
joinGroupCallPresentation, leaveGroupCall, leaveGroupCallPresentation, toggleGroupCallStartSubscription,
requestCall, getDhConfig, confirmCall, sendSignalingData, acceptCall, discardCall, setCallRating, receivedCall,
} from './calls';
export * from './reactions';
export {
fetchChannelStatistics, fetchGroupStatistics, fetchMessageStatistics,
fetchMessagePublicForwards, fetchStatisticsAsyncGraph, fetchStoryStatistics, fetchStoryPublicForwards,
fetchChannelMonetizationStatistics,
} from './statistics';
export {
acceptPhoneCall, confirmPhoneCall, requestPhoneCall, decodePhoneCallData, createPhoneCallState,
destroyPhoneCallState, encodePhoneCallData,
} from './phoneCallState';
export {
broadcastLocalDbUpdateFull,
} from '../localDb';
export * from './account';
export * from './chats';
export * from './messages';
export * from './users';
export * from './symbols';
export * from './management';
export * from './settings';
export * from './twoFaSettings';
export * from './bots';
export * from './calls';
export * from './reactions';
export * from './statistics';
export * from './phoneCallState';
export * from './stories';
export {
validateRequestedInfo, sendPaymentForm, getPaymentForm, getReceipt, fetchPremiumPromo, fetchTemporaryPaymentPassword,
applyBoost, fetchBoostList, fetchBoostStatus, fetchGiveawayInfo, fetchMyBoosts, applyGiftCode, checkGiftCode,
getPremiumGiftCodeOptions, launchPrepaidGiveaway, fetchStarsStatus, fetchStarsTopupOptions, fetchStarsTransactions,
sendStarPaymentForm, getStarsGiftOptions, fetchStarsTransactionById,
} from './payments';
export * from './payments';
export * from './fragment';

View File

@ -1,49 +1,22 @@
import type {
ApiInitialArgs,
ApiOnProgress,
ApiUpdate,
OnApiUpdate,
} from '../../types';
import type { LocalDb } from '../localDb';
import type { MethodArgs, MethodResponse, Methods } from './types';
import { API_THROTTLE_RESET_UPDATES, API_UPDATE_THROTTLE } from '../../../config';
import { throttle, throttleWithTickEnd } from '../../../util/schedulers';
import { updateFullLocalDb } from '../localDb';
import { init as initUpdater } from '../updates/updater';
import { init as initAuth } from './auth';
import { init as initBots } from './bots';
import { init as initCalls } from './calls';
import { init as initChats } from './chats';
import { init as initUpdateEmitter } from '../updates/apiUpdateEmitter';
import { init as initClient } from './client';
import * as methods from './index';
import { init as initManagement } from './management';
import { init as initMessages } from './messages';
import { init as initPayments } from './payments';
import { init as initStickers } from './symbols';
import { init as initTwoFaSettings } from './twoFaSettings';
import { init as initUsers } from './users';
let onUpdate: OnApiUpdate;
export function initApi(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs, initialLocalDb?: LocalDb) {
onUpdate = _onUpdate;
initUpdater(handleUpdate);
initAuth(handleUpdate);
initChats(handleUpdate);
initMessages(handleUpdate);
initUsers(handleUpdate);
initStickers(handleUpdate);
initManagement(handleUpdate);
initTwoFaSettings(handleUpdate);
initBots(handleUpdate);
initCalls(handleUpdate);
initPayments(handleUpdate);
initUpdateEmitter(_onUpdate);
if (initialLocalDb) updateFullLocalDb(initialLocalDb);
initClient(handleUpdate, initialArgs);
initClient(initialArgs);
}
export function callApi<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) {
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 type {
ApiChat, ApiError, ApiUser, ApiUsername, OnApiUpdate,
ApiChat, ApiError, ApiUser, ApiUsername,
} from '../../types';
import { USERNAME_PURCHASE_ERROR } from '../../../config';
import { buildCollectionByKey } from '../../../util/iteratees';
import { ACCEPTABLE_USERNAME_ERRORS } from '../../../config';
import { buildApiExportedInvite, buildChatInviteImporter } from '../apiBuilders/chats';
import { buildApiUser } from '../apiBuilders/users';
import { buildInputEntity, buildInputPeer } from '../gramjsBuilders';
import { addEntitiesToLocalDb } from '../helpers';
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { invokeRequest } from './client';
let onUpdate: OnApiUpdate;
export const ACCEPTABLE_USERNAME_ERRORS = new Set([USERNAME_PURCHASE_ERROR, 'USERNAME_INVALID']);
export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
export async function checkChatUsername({ username }: { username: string }) {
try {
const result = await invokeRequest(new GramJs.channels.CheckUsername({
@ -59,7 +49,7 @@ export async function setChatUsername(
}
if (result) {
onUpdate({
sendApiUpdate({
'@type': 'updateChat',
id: chat.id,
chat: { usernames: usernames.length ? usernames : undefined },
@ -82,7 +72,7 @@ export async function deactivateAllUsernames({ chat }: { chat: ApiChat }) {
.filter((u) => u.username)
: undefined;
onUpdate({
sendApiUpdate({
'@type': 'updateChat',
id: chat.id,
chat: { usernames },
@ -105,7 +95,7 @@ export async function updatePrivateLink({
if (!(result instanceof GramJs.ChatInviteExported)) return undefined;
onUpdate({
sendApiUpdate({
'@type': 'updateChatFullInfo',
id: chat.id,
fullInfo: {
@ -129,7 +119,6 @@ export async function fetchExportedChatInvites({
});
if (!exportedInvites) return undefined;
addEntitiesToLocalDb(exportedInvites.users);
const invites = (exportedInvites.invites
.filter((invite): invite is GramJs.ChatInviteExported => invite instanceof GramJs.ChatInviteExported))
@ -137,7 +126,6 @@ export async function fetchExportedChatInvites({
return {
invites,
users: exportedInvites.users.map(buildApiUser).filter(Boolean),
};
}
@ -164,13 +152,11 @@ export async function editExportedChatInvite({
if (!invite) return undefined;
addEntitiesToLocalDb(invite.users);
if (invite instanceof GramJs.messages.ExportedChatInvite && invite.invite instanceof GramJs.ChatInviteExported) {
const replaceInvite = buildApiExportedInvite(invite.invite);
return {
oldInvite: replaceInvite,
newInvite: replaceInvite,
users: invite.users.map(buildApiUser).filter(Boolean),
};
}
@ -182,7 +168,6 @@ export async function editExportedChatInvite({
return {
oldInvite,
newInvite,
users: invite.users.map(buildApiUser).filter(Boolean),
};
}
return undefined;
@ -253,11 +238,9 @@ export async function fetchChatInviteImporters({
});
if (!result) return undefined;
const users = result.users.map((user) => buildApiUser(user)).filter(Boolean);
addEntitiesToLocalDb(result.users);
return {
importers: result.importers.map((importer) => buildChatInviteImporter(importer)),
users: buildCollectionByKey(users, 'id'),
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@ -1,5 +1,7 @@
import type { DebugLevel } from '../../../util/debugConsole';
import type { ApiInitialArgs, ApiUpdate } from '../../types';
import type {
ApiInitialArgs, ApiUpdate,
} from '../../types';
import type { LocalDb } from '../localDb';
import type { MethodArgs, MethodResponse, Methods } from '../methods/types';

View File

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

View File

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

View File

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

View File

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

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_THROTTLE_RESET_UPDATES = new Set([
'newMessage', 'newScheduledMessage', 'deleteMessages', 'deleteScheduledMessages', 'deleteHistory',
'updateThreadInfos',
]);
export const LOCK_SCREEN_ANIMATION_DURATION_MS = 200;
@ -287,6 +286,7 @@ export const TME_LINK_PREFIX = 'https://t.me/';
export const BOT_FATHER_USERNAME = 'botfather';
export const USERNAME_PURCHASE_ERROR = 'USERNAME_PURCHASE_AVAILABLE';
export const PURCHASE_USERNAME = 'auction';
export const ACCEPTABLE_USERNAME_ERRORS = new Set([USERNAME_PURCHASE_ERROR, 'USERNAME_INVALID']);
export const TME_WEB_DOMAINS = new Set(['t.me', 'web.t.me', 'a.t.me', 'k.t.me', 'z.t.me']);
export const WEB_APP_PLATFORM = 'weba';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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