GramJS: Validate updates order (#2957)
This commit is contained in:
parent
4244c014cb
commit
d160b2b4cb
23
src/api/gramjs/ChatAbortController.ts
Normal file
23
src/api/gramjs/ChatAbortController.ts
Normal file
@ -0,0 +1,23 @@
|
||||
export class ChatAbortController extends AbortController {
|
||||
private threads = new Map<number, AbortController>();
|
||||
|
||||
public getThreadSignal(threadId: number): AbortSignal {
|
||||
let controller = this.threads.get(threadId);
|
||||
if (!controller) {
|
||||
controller = new AbortController();
|
||||
this.threads.set(threadId, controller);
|
||||
}
|
||||
return controller.signal;
|
||||
}
|
||||
|
||||
public abortThread(threadId: number, reason?: string): void {
|
||||
this.threads.get(threadId)?.abort(reason);
|
||||
this.threads.delete(threadId);
|
||||
}
|
||||
|
||||
public abort(reason?: string): void {
|
||||
super.abort(reason);
|
||||
this.threads.forEach((controller) => controller.abort(reason));
|
||||
this.threads.clear();
|
||||
}
|
||||
}
|
||||
@ -83,25 +83,30 @@ export function addPhotoToLocalDb(photo: GramJs.TypePhoto) {
|
||||
}
|
||||
}
|
||||
|
||||
function addChatToLocalDb(chat: GramJs.Chat | GramJs.Channel, noOverwrite = false) {
|
||||
export function addChatToLocalDb(chat: GramJs.Chat | GramJs.Channel) {
|
||||
const id = buildApiPeerId(chat.id, chat instanceof GramJs.Chat ? 'chat' : 'channel');
|
||||
if (!noOverwrite || !localDb.chats[id]) {
|
||||
localDb.chats[id] = chat;
|
||||
}
|
||||
const storedChat = localDb.chats[id];
|
||||
|
||||
const isStoredMin = storedChat && 'min' in storedChat && storedChat.min;
|
||||
const isChatMin = 'min' in chat && chat.min;
|
||||
if (storedChat && !isStoredMin && isChatMin) return;
|
||||
|
||||
localDb.chats[id] = chat;
|
||||
}
|
||||
|
||||
export function addUserToLocalDb(user: GramJs.User, shouldOverwrite = false) {
|
||||
export function addUserToLocalDb(user: GramJs.User) {
|
||||
const id = buildApiPeerId(user.id, 'user');
|
||||
if (shouldOverwrite || !localDb.users[id]) {
|
||||
localDb.users[id] = user;
|
||||
}
|
||||
const storedUser = localDb.users[id];
|
||||
if (storedUser && !storedUser.min && user.min) return;
|
||||
|
||||
localDb.users[id] = user;
|
||||
}
|
||||
|
||||
export function addEntitiesWithPhotosToLocalDb(entities: (GramJs.TypeUser | GramJs.TypeChat)[]) {
|
||||
export function addEntitiesToLocalDb(entities: (GramJs.TypeUser | GramJs.TypeChat)[]) {
|
||||
entities.forEach((entity) => {
|
||||
if (entity instanceof GramJs.User && entity.photo) {
|
||||
if (entity instanceof GramJs.User) {
|
||||
addUserToLocalDb(entity);
|
||||
} else if ((entity instanceof GramJs.Chat || entity instanceof GramJs.Channel) && entity.photo) {
|
||||
} else if ((entity instanceof GramJs.Chat || entity instanceof GramJs.Channel)) {
|
||||
addChatToLocalDb(entity);
|
||||
}
|
||||
});
|
||||
@ -147,3 +152,10 @@ export function log(suffix: keyof typeof LOG_SUFFIX, ...data: any) {
|
||||
);
|
||||
/* eslint-enable max-len */
|
||||
}
|
||||
|
||||
export function isResponseUpdate<T extends GramJs.AnyRequest>(result: T['__response']): result is GramJs.TypeUpdate {
|
||||
return result instanceof GramJs.UpdatesTooLong || result instanceof GramJs.UpdateShortMessage
|
||||
|| result instanceof GramJs.UpdateShortChatMessage || result instanceof GramJs.UpdateShort
|
||||
|| result instanceof GramJs.UpdatesCombined || result instanceof GramJs.Updates
|
||||
|| result instanceof GramJs.UpdateShortSentMessage;
|
||||
}
|
||||
|
||||
@ -17,6 +17,9 @@ export interface LocalDb {
|
||||
stickerSets: Record<string, GramJs.StickerSet>;
|
||||
photos: Record<string, GramJs.Photo>;
|
||||
webDocuments: Record<string, GramJs.TypeWebDocument>;
|
||||
|
||||
commonBoxState: Record<string, number>;
|
||||
channelPtsById: Record<string, number>;
|
||||
}
|
||||
|
||||
const channel = IS_MULTITAB_SUPPORTED ? new BroadcastChannel(DATA_BROADCAST_CHANNEL_NAME) : undefined;
|
||||
@ -77,17 +80,24 @@ function convertToVirtualClass(value: any): any {
|
||||
function createLocalDbInitial(initial?: LocalDb): LocalDb {
|
||||
return [
|
||||
'localMessages', 'chats', 'users', 'messages', 'documents', 'stickerSets', 'photos', 'webDocuments',
|
||||
'commonBoxState', 'channelPtsById',
|
||||
]
|
||||
.reduce((acc: Record<string, any>, key) => {
|
||||
const value = initial?.[key as keyof LocalDb] ?? {};
|
||||
const valueVirtualClass = Object.keys(value).reduce((acc2, key2) => {
|
||||
const convertedValue = Object.keys(value).reduce((acc2, key2) => {
|
||||
if (key === 'commonBoxState' || key === 'channelPtsById') {
|
||||
const typedValue = value as Record<string, number>;
|
||||
acc2[key2] = typedValue[key2];
|
||||
return acc2;
|
||||
}
|
||||
|
||||
acc2[key2] = convertToVirtualClass(value[key2]);
|
||||
return acc2;
|
||||
}, {} as Record<string, any>);
|
||||
|
||||
acc[key] = IS_MULTITAB_SUPPORTED
|
||||
? createProxy(key, valueVirtualClass)
|
||||
: valueVirtualClass;
|
||||
? createProxy(key, convertedValue)
|
||||
: convertedValue;
|
||||
return acc;
|
||||
}, {} as LocalDb) as LocalDb;
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ import {
|
||||
buildBotSwitchWebview,
|
||||
} from '../apiBuilders/bots';
|
||||
import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
||||
import { addEntitiesWithPhotosToLocalDb, addUserToLocalDb, deserializeBytes } from '../helpers';
|
||||
import { addEntitiesToLocalDb, addUserToLocalDb, deserializeBytes } from '../helpers';
|
||||
import { omitVirtualClassFields } from '../apiBuilders/helpers';
|
||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||
import { buildApiUrlAuthResult } from '../apiBuilders/misc';
|
||||
@ -104,7 +104,7 @@ export async function fetchInlineBotResults({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
return {
|
||||
isGallery: Boolean(result.gallery),
|
||||
@ -143,7 +143,7 @@ export async function sendInlineBotResult({
|
||||
...(isSilent && { silent: true }),
|
||||
...(replyingTo && { replyToMsgId: replyingTo }),
|
||||
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
||||
}), true);
|
||||
}));
|
||||
}
|
||||
|
||||
export async function startBot({
|
||||
@ -159,7 +159,7 @@ export async function startBot({
|
||||
peer: buildInputPeer(bot.id, bot.accessHash),
|
||||
randomId,
|
||||
startParam,
|
||||
}), true);
|
||||
}));
|
||||
}
|
||||
|
||||
export async function requestWebView({
|
||||
@ -313,7 +313,7 @@ export async function sendWebViewData({
|
||||
buttonText,
|
||||
data,
|
||||
randomId,
|
||||
}), true);
|
||||
}));
|
||||
}
|
||||
|
||||
export async function loadAttachBots({
|
||||
@ -326,7 +326,7 @@ export async function loadAttachBots({
|
||||
}));
|
||||
|
||||
if (result instanceof GramJs.AttachMenuBots) {
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
return {
|
||||
hash: result.hash.toString(),
|
||||
bots: buildCollectionByKey(result.bots.map(buildApiAttachBot), 'id'),
|
||||
@ -346,7 +346,7 @@ export async function loadAttachBot({
|
||||
}));
|
||||
|
||||
if (result instanceof GramJs.AttachMenuBotsBot) {
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
return {
|
||||
bot: buildApiAttachBot(result.bot),
|
||||
users: result.users.map(buildApiUser).filter(Boolean),
|
||||
|
||||
@ -17,7 +17,7 @@ import {
|
||||
} from '../apiBuilders/calls';
|
||||
import { buildApiUser } from '../apiBuilders/users';
|
||||
import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
||||
import { addEntitiesWithPhotosToLocalDb } from '../helpers';
|
||||
import { addEntitiesToLocalDb } from '../helpers';
|
||||
import { GROUP_CALL_PARTICIPANTS_LIMIT } from '../../../config';
|
||||
|
||||
let onUpdate: OnApiUpdate;
|
||||
@ -39,8 +39,8 @@ export async function getGroupCall({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesWithPhotosToLocalDb(result.chats);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.chats);
|
||||
|
||||
const users = result.users.map(buildApiUser).filter(Boolean);
|
||||
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
|
||||
@ -59,7 +59,9 @@ export function discardGroupCall({
|
||||
}) {
|
||||
return invokeRequest(new GramJs.phone.DiscardGroupCall({
|
||||
call: buildInputGroupCall(call),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function editGroupCallParticipant({
|
||||
@ -78,7 +80,9 @@ export function editGroupCallParticipant({
|
||||
...(presentationPaused !== undefined && { presentationPaused }),
|
||||
...(raiseHand !== undefined && { raiseHand }),
|
||||
...(volume !== undefined && { volume }),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function editGroupCallTitle({
|
||||
@ -89,7 +93,9 @@ export function editGroupCallTitle({
|
||||
return invokeRequest(new GramJs.phone.EditGroupCallTitle({
|
||||
title,
|
||||
call: buildInputGroupCall(groupCall),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function exportGroupCallInvite({
|
||||
@ -126,8 +132,8 @@ export async function fetchGroupCallParticipants({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesWithPhotosToLocalDb(result.chats);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.chats);
|
||||
|
||||
const users = result.users.map(buildApiUser).filter(Boolean);
|
||||
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
|
||||
@ -151,7 +157,9 @@ export function leaveGroupCall({
|
||||
}) {
|
||||
return invokeRequest(new GramJs.phone.LeaveGroupCall({
|
||||
call: buildInputGroupCall(call),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function joinGroupCall({
|
||||
@ -215,7 +223,9 @@ export function joinGroupCallPresentation({
|
||||
params: new GramJs.DataJSON({
|
||||
data: JSON.stringify(params),
|
||||
}),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleGroupCallStartSubscription({
|
||||
@ -226,7 +236,10 @@ export function toggleGroupCallStartSubscription({
|
||||
return invokeRequest(new GramJs.phone.ToggleGroupCallStartSubscription({
|
||||
call: buildInputGroupCall(call),
|
||||
subscribed,
|
||||
}), true, undefined, undefined, undefined, true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
shouldIgnoreErrors: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function leaveGroupCallPresentation({
|
||||
@ -236,7 +249,9 @@ export function leaveGroupCallPresentation({
|
||||
}) {
|
||||
return invokeRequest(new GramJs.phone.LeaveGroupCallPresentation({
|
||||
call: buildInputGroupCall(call),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getDhConfig() {
|
||||
@ -259,7 +274,9 @@ export function discardCall({
|
||||
return invokeRequest(new GramJs.phone.DiscardCall({
|
||||
peer: buildInputPhoneCall(call),
|
||||
reason: isBusy ? new GramJs.PhoneCallDiscardReasonBusy() : new GramJs.PhoneCallDiscardReasonHangup(),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function requestCall({
|
||||
@ -286,7 +303,7 @@ export async function requestCall({
|
||||
call,
|
||||
});
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
return {
|
||||
users: result.users.map(buildApiUser).filter(Boolean),
|
||||
@ -302,7 +319,9 @@ export function setCallRating({
|
||||
rating,
|
||||
peer: buildInputPhoneCall(call),
|
||||
comment,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function receivedCall({
|
||||
@ -337,7 +356,7 @@ export async function acceptCall({
|
||||
call,
|
||||
});
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
return {
|
||||
users: result.users.map(buildApiUser).filter(Boolean),
|
||||
@ -367,7 +386,7 @@ export async function confirmCall({
|
||||
call,
|
||||
});
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
return {
|
||||
users: result.users.map(buildApiUser).filter(Boolean),
|
||||
|
||||
@ -59,7 +59,7 @@ import {
|
||||
generateRandomBigInt,
|
||||
} from '../gramjsBuilders';
|
||||
import {
|
||||
addEntitiesWithPhotosToLocalDb,
|
||||
addEntitiesToLocalDb,
|
||||
addMessageToLocalDb,
|
||||
addPhotoToLocalDb,
|
||||
isChatFolder,
|
||||
@ -69,6 +69,7 @@ import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
||||
import { buildApiPhoto } from '../apiBuilders/common';
|
||||
import { buildStickerSet } from '../apiBuilders/symbols';
|
||||
import localDb from '../localDb';
|
||||
import { updateChannelState } from '../updateManager';
|
||||
import { scheduleMutedChatUpdate } from '../scheduleUnmute';
|
||||
|
||||
type FullChatData = {
|
||||
@ -153,6 +154,10 @@ export async function fetchChats({
|
||||
const peerEntity = peersByKey[getPeerKey(dialog.peer)];
|
||||
const chat = buildApiChatFromDialog(dialog, peerEntity);
|
||||
|
||||
if (dialog.pts) {
|
||||
updateChannelState(chat.id, dialog.pts);
|
||||
}
|
||||
|
||||
if (
|
||||
chat.id === SERVICE_NOTIFICATIONS_USER_ID
|
||||
&& lastLocalServiceMessage
|
||||
@ -222,13 +227,15 @@ export async function fetchChatSettings(chat: ApiChat) {
|
||||
|
||||
const result = await invokeRequest(new GramJs.messages.GetPeerSettings({
|
||||
peer: buildInputPeer(id, accessHash),
|
||||
}));
|
||||
}), {
|
||||
abortControllerChatId: id,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
return {
|
||||
users: result.users.map(buildApiUser).filter(Boolean),
|
||||
@ -516,6 +523,10 @@ async function getFullChannelInfo(
|
||||
}
|
||||
}
|
||||
|
||||
if (result.fullChat.pts) {
|
||||
updateChannelState(id, result.fullChat.pts);
|
||||
}
|
||||
|
||||
const statusesById = {
|
||||
...userStatusesById,
|
||||
...bannedStatusesById,
|
||||
@ -631,7 +642,9 @@ export async function createChannel({
|
||||
broadcast: true,
|
||||
title,
|
||||
about,
|
||||
}), undefined, true);
|
||||
}), {
|
||||
shouldThrow: true,
|
||||
});
|
||||
|
||||
// `createChannel` can return a lot of different update types according to docs,
|
||||
// but currently channel creation returns only `Updates` type.
|
||||
@ -660,7 +673,9 @@ export async function createChannel({
|
||||
await invokeRequest(new GramJs.channels.InviteToChannel({
|
||||
channel: buildInputEntity(channel.id, channel.accessHash) as GramJs.InputChannel,
|
||||
users: users.map(({ id, accessHash }) => buildInputEntity(id, accessHash)) as GramJs.InputUser[],
|
||||
}), undefined, noErrorUpdate);
|
||||
}), {
|
||||
shouldThrow: noErrorUpdate,
|
||||
});
|
||||
} catch (err) {
|
||||
// `noErrorUpdate` will cause an exception which we don't want either
|
||||
}
|
||||
@ -676,7 +691,10 @@ export function joinChannel({
|
||||
}) {
|
||||
return invokeRequest(new GramJs.channels.JoinChannel({
|
||||
channel: buildInputEntity(channelId, accessHash) as GramJs.InputChannel,
|
||||
}), true, true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
shouldThrow: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteChatUser({
|
||||
@ -688,7 +706,9 @@ export function deleteChatUser({
|
||||
return invokeRequest(new GramJs.messages.DeleteChatUser({
|
||||
chatId: buildInputEntity(chat.id, chat.accessHash) as BigInt.BigInteger,
|
||||
userId: buildInputEntity(user.id, user.accessHash) as GramJs.InputUser,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteChat({
|
||||
@ -698,7 +718,9 @@ export function deleteChat({
|
||||
}) {
|
||||
return invokeRequest(new GramJs.messages.DeleteChat({
|
||||
chatId: buildInputEntity(chatId) as BigInt.BigInteger,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function leaveChannel({
|
||||
@ -708,7 +730,9 @@ export function leaveChannel({
|
||||
}) {
|
||||
return invokeRequest(new GramJs.channels.LeaveChannel({
|
||||
channel: buildInputEntity(channelId, accessHash) as GramJs.InputChannel,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteChannel({
|
||||
@ -718,7 +742,9 @@ export function deleteChannel({
|
||||
}) {
|
||||
return invokeRequest(new GramJs.channels.DeleteChannel({
|
||||
channel: buildInputEntity(channelId, accessHash) as GramJs.InputChannel,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function createGroupChat({
|
||||
@ -729,7 +755,9 @@ export async function createGroupChat({
|
||||
const result = await invokeRequest(new GramJs.messages.CreateChat({
|
||||
title,
|
||||
users: users.map(({ id, accessHash }) => buildInputEntity(id, accessHash)) as GramJs.InputUser[],
|
||||
}), undefined, true);
|
||||
}), {
|
||||
shouldThrow: true,
|
||||
});
|
||||
|
||||
// `createChat` can return a lot of different update types according to docs,
|
||||
// but currently chat creation returns only `Updates` type.
|
||||
@ -785,7 +813,9 @@ export async function editChatPhoto({
|
||||
chatId: inputEntity as BigInt.BigInteger,
|
||||
photo: inputPhoto,
|
||||
}),
|
||||
true,
|
||||
{
|
||||
shouldReturnTrue: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -826,7 +856,9 @@ export function toggleChatArchived({
|
||||
peer: buildInputPeer(id, accessHash),
|
||||
folderId,
|
||||
})],
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchChatFolders() {
|
||||
@ -988,7 +1020,9 @@ export function togglePreHistoryHidden({
|
||||
return invokeRequest(new GramJs.channels.TogglePreHistoryHidden({
|
||||
channel: channel as GramJs.InputChannel,
|
||||
enabled: isEnabled,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateChatDefaultBannedRights({
|
||||
@ -1000,7 +1034,9 @@ export function updateChatDefaultBannedRights({
|
||||
return invokeRequest(new GramJs.messages.EditChatDefaultBannedRights({
|
||||
peer,
|
||||
bannedRights: buildChatBannedRights(bannedRights),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateChatMemberBannedRights({
|
||||
@ -1013,7 +1049,9 @@ export function updateChatMemberBannedRights({
|
||||
channel,
|
||||
participant,
|
||||
bannedRights: buildChatBannedRights(bannedRights, untilDate),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateChatAdmin({
|
||||
@ -1027,7 +1065,9 @@ export function updateChatAdmin({
|
||||
userId,
|
||||
adminRights: buildChatAdminRights(adminRights),
|
||||
rank: customTitle,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateChatTitle(chat: ApiChat, title: string) {
|
||||
@ -1041,7 +1081,9 @@ export async function updateChatTitle(chat: ApiChat, title: string) {
|
||||
chatId: inputEntity as BigInt.BigInteger,
|
||||
title,
|
||||
}),
|
||||
true,
|
||||
{
|
||||
shouldReturnTrue: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -1073,7 +1115,9 @@ export function toggleSignatures({
|
||||
return invokeRequest(new GramJs.channels.ToggleSignatures({
|
||||
channel: channel as GramJs.InputChannel,
|
||||
enabled: isEnabled,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
type ChannelMembersFilter =
|
||||
@ -1106,7 +1150,9 @@ export async function fetchMembers(
|
||||
filter,
|
||||
offset,
|
||||
limit: MEMBERS_LOAD_SLICE,
|
||||
}));
|
||||
}), {
|
||||
abortControllerChatId: chatId,
|
||||
});
|
||||
|
||||
if (!result || result instanceof GramJs.channels.ChannelParticipantsNotModified) {
|
||||
return undefined;
|
||||
@ -1144,14 +1190,17 @@ export function setDiscussionGroup({
|
||||
return invokeRequest(new GramJs.channels.SetDiscussionGroup({
|
||||
broadcast: buildInputPeer(channel.id, channel.accessHash),
|
||||
group: chat ? buildInputPeer(chat.id, chat.accessHash) : new GramJs.InputChannelEmpty(),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function migrateChat(chat: ApiChat) {
|
||||
const result = await invokeRequest(
|
||||
new GramJs.messages.MigrateChat({ chatId: buildInputEntity(chat.id) as BigInt.BigInteger }),
|
||||
undefined,
|
||||
true,
|
||||
{
|
||||
shouldThrow: true,
|
||||
},
|
||||
);
|
||||
|
||||
// `migrateChat` can return a lot of different update types according to docs,
|
||||
@ -1226,14 +1275,20 @@ export async function addChatMembers(chat: ApiChat, users: ApiUser[], noErrorUpd
|
||||
return await invokeRequest(new GramJs.channels.InviteToChannel({
|
||||
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
|
||||
users: users.map((user) => buildInputEntity(user.id, user.accessHash)) as GramJs.InputUser[],
|
||||
}), true, noErrorUpdate);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
shouldThrow: noErrorUpdate,
|
||||
});
|
||||
}
|
||||
|
||||
return await Promise.all(users.map((user) => {
|
||||
return invokeRequest(new GramJs.messages.AddChatUser({
|
||||
chatId: buildInputEntity(chat.id) as BigInt.BigInteger,
|
||||
userId: buildInputEntity(user.id, user.accessHash) as GramJs.InputUser,
|
||||
}), true, noErrorUpdate);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
shouldThrow: noErrorUpdate,
|
||||
});
|
||||
}));
|
||||
} catch (err) {
|
||||
// `noErrorUpdate` will cause an exception which we don't want either
|
||||
@ -1274,7 +1329,9 @@ export function deleteChatMember(chat: ApiChat, user: ApiUser) {
|
||||
return invokeRequest(new GramJs.messages.DeleteChatUser({
|
||||
chatId: buildInputEntity(chat.id) as BigInt.BigInteger,
|
||||
userId: buildInputEntity(user.id, user.accessHash) as GramJs.InputUser,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1282,14 +1339,18 @@ export function toggleJoinToSend(chat: ApiChat, isEnabled: boolean) {
|
||||
return invokeRequest(new GramJs.channels.ToggleJoinToSend({
|
||||
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
|
||||
enabled: isEnabled,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleJoinRequest(chat: ApiChat, isEnabled: boolean) {
|
||||
return invokeRequest(new GramJs.channels.ToggleJoinRequest({
|
||||
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
|
||||
enabled: isEnabled,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
function preparePeers(
|
||||
@ -1328,11 +1389,11 @@ function updateLocalDb(result: (
|
||||
GramJs.messages.Chats | GramJs.messages.ChatsSlice | GramJs.TypeUpdates | GramJs.messages.ForumTopics
|
||||
)) {
|
||||
if ('users' in result) {
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
}
|
||||
|
||||
if ('chats' in result) {
|
||||
addEntitiesWithPhotosToLocalDb(result.chats);
|
||||
addEntitiesToLocalDb(result.chats);
|
||||
}
|
||||
|
||||
if ('messages' in result) {
|
||||
@ -1361,7 +1422,9 @@ export function setChatEnabledReactions({
|
||||
return invokeRequest(new GramJs.messages.SetChatAvailableReactions({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
availableReactions: buildInputChatReactions(enabledReactions),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleIsProtected({
|
||||
@ -1372,7 +1435,9 @@ export function toggleIsProtected({
|
||||
return invokeRequest(new GramJs.messages.ToggleNoForwards({
|
||||
peer: buildInputPeer(id, accessHash),
|
||||
enabled: isProtected,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleParticipantsHidden({
|
||||
@ -1383,7 +1448,9 @@ export function toggleParticipantsHidden({
|
||||
return invokeRequest(new GramJs.channels.ToggleParticipantsHidden({
|
||||
channel: buildInputPeer(id, accessHash),
|
||||
enabled: isEnabled,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleForum({
|
||||
@ -1394,7 +1461,10 @@ export function toggleForum({
|
||||
return invokeRequest(new GramJs.channels.ToggleForum({
|
||||
channel: buildInputPeer(id, accessHash),
|
||||
enabled: isEnabled,
|
||||
}), true, true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
shouldThrow: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function createTopic({
|
||||
@ -1540,7 +1610,9 @@ export function deleteTopic({
|
||||
return invokeRequest(new GramJs.channels.DeleteTopicHistory({
|
||||
channel: buildInputPeer(id, accessHash),
|
||||
topMsgId: topicId,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function togglePinnedTopic({
|
||||
@ -1556,7 +1628,9 @@ export function togglePinnedTopic({
|
||||
channel: buildInputPeer(id, accessHash),
|
||||
topicId,
|
||||
pinned: isPinned,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function editTopic({
|
||||
@ -1578,7 +1652,9 @@ export function editTopic({
|
||||
iconEmojiId: topicId !== GENERAL_TOPIC_ID && iconEmojiId ? BigInt(iconEmojiId) : undefined,
|
||||
closed: isClosed,
|
||||
hidden: isHidden,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function checkChatlistInvite({
|
||||
@ -1613,7 +1689,10 @@ export function joinChatlistInvite({
|
||||
return invokeRequest(new GramJs.chatlists.JoinChatlistInvite({
|
||||
slug,
|
||||
peers: peers.map((peer) => buildInputPeer(peer.id, peer.accessHash)),
|
||||
}), true, true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
shouldThrow: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchLeaveChatlistSuggestions({
|
||||
@ -1644,7 +1723,9 @@ export function leaveChatlist({
|
||||
filterId: folderId,
|
||||
}),
|
||||
peers: peers.map((peer) => buildInputPeer(peer.id, peer.accessHash)),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function createChalistInvite({
|
||||
@ -1660,7 +1741,9 @@ export async function createChalistInvite({
|
||||
}),
|
||||
title: title || '',
|
||||
peers: peers.map((peer) => buildInputPeer(peer.id, peer.accessHash)),
|
||||
}), undefined, true);
|
||||
}), {
|
||||
shouldThrow: true,
|
||||
});
|
||||
|
||||
if (!result || result.filter instanceof GramJs.DialogFilterDefault) return undefined;
|
||||
|
||||
@ -1699,7 +1782,9 @@ export async function editChatlistInvite({
|
||||
slug,
|
||||
title,
|
||||
peers: peers.map((peer) => buildInputPeer(peer.id, peer.accessHash)),
|
||||
}), undefined, true);
|
||||
}), {
|
||||
shouldThrow: true,
|
||||
});
|
||||
if (!result) return undefined;
|
||||
|
||||
return buildApiChatlistExportedInvite(result);
|
||||
|
||||
@ -5,7 +5,6 @@ import TelegramClient from '../../../lib/gramjs/client/TelegramClient';
|
||||
|
||||
import { Logger as GramJsLogger } from '../../../lib/gramjs/extensions/index';
|
||||
import type { TwoFaParams } from '../../../lib/gramjs/client/2fa';
|
||||
|
||||
import type {
|
||||
ApiInitialArgs,
|
||||
ApiMediaFormat,
|
||||
@ -21,13 +20,18 @@ import {
|
||||
onRequestPhoneNumber, onRequestCode, onRequestPassword, onRequestRegistration,
|
||||
onAuthError, onAuthReady, onCurrentUserUpdate, onRequestQrCode, onWebAuthTokenFailed,
|
||||
} from './auth';
|
||||
import { updater } from '../updater';
|
||||
import { setMessageBuilderCurrentUserId } from '../apiBuilders/messages';
|
||||
import downloadMediaWithClient, { parseMediaUrl } from './media';
|
||||
import { buildApiUser, buildApiUserFullInfo } from '../apiBuilders/users';
|
||||
import localDb, { clearLocalDb } from '../localDb';
|
||||
import { buildApiPeerId } from '../apiBuilders/peers';
|
||||
import { addMessageToLocalDb, log } from '../helpers';
|
||||
import {
|
||||
addMessageToLocalDb, addUserToLocalDb, isResponseUpdate, log,
|
||||
} from '../helpers';
|
||||
import { ChatAbortController } from '../ChatAbortController';
|
||||
import {
|
||||
updateChannelState, getDifference, init as initUpdatesManager, processUpdate, reset as resetUpdatesManager,
|
||||
} from '../updateManager';
|
||||
|
||||
const DEFAULT_USER_AGENT = 'Unknown UserAgent';
|
||||
const DEFAULT_PLATFORM = 'Unknown platform';
|
||||
@ -37,6 +41,8 @@ GramJsLogger.setLevel(DEBUG_GRAMJS ? 'debug' : 'warn');
|
||||
|
||||
const gramJsUpdateEventBuilder = { build: (update: object) => update };
|
||||
|
||||
const CHAT_ABORT_CONTROLLERS = new Map<string, ChatAbortController>();
|
||||
|
||||
let onUpdate: OnApiUpdate;
|
||||
let client: TelegramClient;
|
||||
let isConnected = false;
|
||||
@ -81,7 +87,6 @@ export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs)
|
||||
);
|
||||
|
||||
client.addEventHandler(handleGramJsUpdate, gramJsUpdateEventBuilder);
|
||||
client.addEventHandler(updater, gramJsUpdateEventBuilder);
|
||||
|
||||
try {
|
||||
if (DEBUG) {
|
||||
@ -94,6 +99,7 @@ export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs)
|
||||
}
|
||||
|
||||
try {
|
||||
client.setPingCallback(getDifference);
|
||||
await client.start({
|
||||
phoneNumber: onRequestPhoneNumber,
|
||||
phoneCode: onRequestCode,
|
||||
@ -131,6 +137,8 @@ export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs)
|
||||
onSessionUpdate(session.getSessionData());
|
||||
onUpdate({ '@type': 'updateApiReady' });
|
||||
|
||||
initUpdatesManager(invokeRequest);
|
||||
|
||||
void fetchCurrentUser();
|
||||
} catch (err) {
|
||||
if (DEBUG) {
|
||||
@ -150,7 +158,10 @@ export async function destroy(noLogOut = false, noClearLocalDb = false) {
|
||||
await invokeRequest(new GramJs.auth.LogOut());
|
||||
}
|
||||
|
||||
if (!noClearLocalDb) clearLocalDb();
|
||||
if (!noClearLocalDb) {
|
||||
clearLocalDb();
|
||||
resetUpdatesManager();
|
||||
}
|
||||
|
||||
await client.destroy();
|
||||
}
|
||||
@ -170,54 +181,66 @@ function onSessionUpdate(sessionData: ApiSessionData) {
|
||||
});
|
||||
}
|
||||
|
||||
function handleGramJsUpdate(update: any) {
|
||||
type UpdateConfig = GramJs.UpdateConfig & { _entities?: (GramJs.TypeUser | GramJs.TypeChat)[] };
|
||||
|
||||
export function handleGramJsUpdate(update: any) {
|
||||
processUpdate(update);
|
||||
|
||||
if (update instanceof connection.UpdateConnectionState) {
|
||||
isConnected = update.state === connection.UpdateConnectionState.connected;
|
||||
} else if (update instanceof GramJs.UpdatesTooLong) {
|
||||
void handleTerminatedSession();
|
||||
} else if (update instanceof GramJs.UpdateConfig) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const currentUser = (update as GramJs.UpdateConfig & { _entities?: (GramJs.TypeUser | GramJs.TypeChat)[] })
|
||||
._entities
|
||||
?.find((entity) => entity instanceof GramJs.User && buildApiPeerId(entity.id, 'user') === currentUserId);
|
||||
if (!(currentUser instanceof GramJs.User)) return;
|
||||
} else {
|
||||
const updates = 'updates' in update ? update.updates : [update];
|
||||
updates.forEach((nestedUpdate: any) => {
|
||||
if (!(nestedUpdate instanceof GramJs.UpdateConfig)) return;
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const currentUser = (nestedUpdate as UpdateConfig)._entities
|
||||
?.find((entity) => entity instanceof GramJs.User && buildApiPeerId(entity.id, 'user') === currentUserId);
|
||||
if (!(currentUser instanceof GramJs.User)) return;
|
||||
|
||||
setIsPremium({ isPremium: Boolean(currentUser.premium) });
|
||||
setIsPremium({ isPremium: Boolean(currentUser.premium) });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function invokeRequest<T extends GramJs.AnyRequest>(
|
||||
request: T,
|
||||
shouldReturnTrue: true,
|
||||
shouldThrow?: boolean,
|
||||
shouldIgnoreUpdates?: undefined,
|
||||
dcId?: number,
|
||||
shouldIgnoreErrors?: boolean,
|
||||
): Promise<true | undefined>;
|
||||
type InvokeRequestParams = {
|
||||
shouldThrow?: boolean;
|
||||
shouldIgnoreUpdates?: boolean;
|
||||
dcId?: number;
|
||||
shouldIgnoreErrors?: boolean;
|
||||
abortControllerChatId?: string;
|
||||
abortControllerThreadId?: number;
|
||||
};
|
||||
|
||||
export async function invokeRequest<T extends GramJs.AnyRequest>(
|
||||
request: T,
|
||||
shouldReturnTrue?: boolean,
|
||||
shouldThrow?: boolean,
|
||||
shouldIgnoreUpdates?: boolean,
|
||||
dcId?: number,
|
||||
shouldIgnoreErrors?: boolean,
|
||||
params?: InvokeRequestParams & { shouldReturnTrue?: false },
|
||||
): Promise<T['__response'] | undefined>;
|
||||
|
||||
export async function invokeRequest<T extends GramJs.AnyRequest>(
|
||||
request: T,
|
||||
shouldReturnTrue = false,
|
||||
shouldThrow = false,
|
||||
shouldIgnoreUpdates = false,
|
||||
dcId?: number,
|
||||
shouldIgnoreErrors = false,
|
||||
params?: InvokeRequestParams & { shouldReturnTrue: true },
|
||||
): Promise<true | undefined>;
|
||||
|
||||
export async function invokeRequest<T extends GramJs.AnyRequest>(
|
||||
request: T,
|
||||
params: InvokeRequestParams & { shouldReturnTrue?: boolean } = {},
|
||||
) {
|
||||
if (!isConnected) {
|
||||
if (DEBUG) {
|
||||
log('INVOKE ERROR', request.className, 'Client is not connected');
|
||||
const {
|
||||
shouldThrow, shouldIgnoreUpdates, dcId, shouldIgnoreErrors, abortControllerChatId, abortControllerThreadId,
|
||||
} = params;
|
||||
const shouldReturnTrue = Boolean(params.shouldReturnTrue);
|
||||
|
||||
let abortSignal: AbortSignal | undefined;
|
||||
if (abortControllerChatId) {
|
||||
let controller = CHAT_ABORT_CONTROLLERS.get(abortControllerChatId);
|
||||
if (!controller) {
|
||||
controller = new ChatAbortController();
|
||||
CHAT_ABORT_CONTROLLERS.set(abortControllerChatId, controller);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
abortSignal = abortControllerThreadId ? controller.getThreadSignal(abortControllerThreadId) : controller.signal;
|
||||
}
|
||||
|
||||
try {
|
||||
@ -225,14 +248,14 @@ export async function invokeRequest<T extends GramJs.AnyRequest>(
|
||||
log('INVOKE', request.className);
|
||||
}
|
||||
|
||||
const result = await client.invoke(request, dcId);
|
||||
const result = await client.invoke(request, dcId, abortSignal);
|
||||
|
||||
if (DEBUG) {
|
||||
log('RESPONSE', request.className, result);
|
||||
}
|
||||
|
||||
if (!shouldIgnoreUpdates) {
|
||||
handleUpdates(result);
|
||||
if (!shouldIgnoreUpdates && isResponseUpdate(result)) {
|
||||
processUpdate(result);
|
||||
}
|
||||
|
||||
return shouldReturnTrue ? result && true : result;
|
||||
@ -256,36 +279,6 @@ export async function invokeRequest<T extends GramJs.AnyRequest>(
|
||||
}
|
||||
}
|
||||
|
||||
export function handleUpdates(result: {}) {
|
||||
let manyUpdates;
|
||||
let singleUpdate;
|
||||
|
||||
if (result instanceof GramJs.UpdatesCombined || result instanceof GramJs.Updates) {
|
||||
manyUpdates = result;
|
||||
} else if (typeof result === 'object' && 'updates' in result && (
|
||||
result.updates instanceof GramJs.Updates || result.updates instanceof GramJs.UpdatesCombined
|
||||
)) {
|
||||
manyUpdates = result.updates;
|
||||
} else if (
|
||||
result instanceof GramJs.UpdateShortMessage
|
||||
|| result instanceof GramJs.UpdateShortChatMessage
|
||||
|| result instanceof GramJs.UpdateShort
|
||||
|| result instanceof GramJs.UpdateShortSentMessage
|
||||
) {
|
||||
singleUpdate = result;
|
||||
}
|
||||
|
||||
if (manyUpdates) {
|
||||
injectUpdateEntities(manyUpdates);
|
||||
|
||||
manyUpdates.updates.forEach((update) => {
|
||||
updater(update);
|
||||
});
|
||||
} else if (singleUpdate) {
|
||||
updater(singleUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
export async function downloadMedia(
|
||||
args: { url: string; mediaFormat: ApiMediaFormat; start?: number; end?: number; isHtmlAllowed?: boolean },
|
||||
onProgress?: ApiOnProgress,
|
||||
@ -321,6 +314,18 @@ export function getTmpPassword(currentPassword: string, ttl?: number) {
|
||||
return client.getTmpPassword(currentPassword, ttl);
|
||||
}
|
||||
|
||||
export function abortChatRequests(params: { chatId: string; threadId?: number }) {
|
||||
const { chatId, threadId } = params;
|
||||
const controller = CHAT_ABORT_CONTROLLERS.get(chatId);
|
||||
if (!threadId) {
|
||||
controller?.abort('Chat change');
|
||||
CHAT_ABORT_CONTROLLERS.delete(chatId);
|
||||
return;
|
||||
}
|
||||
|
||||
controller?.abortThread(threadId, 'Thread change');
|
||||
}
|
||||
|
||||
export async function fetchCurrentUser() {
|
||||
const userFull = await invokeRequest(new GramJs.users.GetFullUser({
|
||||
id: new GramJs.InputUserSelf(),
|
||||
@ -335,7 +340,7 @@ export async function fetchCurrentUser() {
|
||||
if (user.photo instanceof GramJs.Photo) {
|
||||
localDb.photos[user.photo.id.toString()] = user.photo;
|
||||
}
|
||||
localDb.users[buildApiPeerId(user.id, 'user')] = user;
|
||||
addUserToLocalDb(user);
|
||||
const currentUserFullInfo = buildApiUserFullInfo(userFull);
|
||||
const currentUser = buildApiUser(user)!;
|
||||
|
||||
@ -365,22 +370,13 @@ export function dispatchErrorUpdate<T extends GramJs.AnyRequest>(err: Error, req
|
||||
});
|
||||
}
|
||||
|
||||
function injectUpdateEntities(result: GramJs.Updates | GramJs.UpdatesCombined) {
|
||||
const entities = [...result.users, ...result.chats];
|
||||
|
||||
result.updates.forEach((update) => {
|
||||
if (entities) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
(update as any)._entities = entities;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function handleTerminatedSession() {
|
||||
try {
|
||||
await invokeRequest(new GramJs.users.GetFullUser({
|
||||
id: new GramJs.InputUserSelf(),
|
||||
}), undefined, true);
|
||||
}), {
|
||||
shouldThrow: true,
|
||||
});
|
||||
} catch (err: any) {
|
||||
if (err.message === 'AUTH_KEY_UNREGISTERED' || err.message === 'SESSION_REVOKED') {
|
||||
onUpdate({
|
||||
@ -429,6 +425,10 @@ export async function repairFileReference({
|
||||
|
||||
if (!result || result instanceof GramJs.messages.MessagesNotModified) return false;
|
||||
|
||||
if (peer && 'pts' in result) {
|
||||
updateChannelState(peer.channelId.toString(), result.pts);
|
||||
}
|
||||
|
||||
const message = result.messages[0];
|
||||
if (message instanceof GramJs.MessageEmpty) return false;
|
||||
addMessageToLocalDb(message);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export {
|
||||
destroy, disconnect, downloadMedia, fetchCurrentUser, repairFileReference,
|
||||
destroy, disconnect, downloadMedia, fetchCurrentUser, repairFileReference, abortChatRequests,
|
||||
} from './client';
|
||||
|
||||
export {
|
||||
|
||||
@ -7,7 +7,7 @@ import type {
|
||||
} from '../../types';
|
||||
|
||||
import { USERNAME_PURCHASE_ERROR } from '../../../config';
|
||||
import { addEntitiesWithPhotosToLocalDb } from '../helpers';
|
||||
import { addEntitiesToLocalDb } from '../helpers';
|
||||
import { buildApiExportedInvite, buildChatInviteImporter } from '../apiBuilders/chats';
|
||||
import { buildApiUser } from '../apiBuilders/users';
|
||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||
@ -25,7 +25,9 @@ export async function checkChatUsername({ username }: { username: string }) {
|
||||
const result = await invokeRequest(new GramJs.channels.CheckUsername({
|
||||
channel: new GramJs.InputChannelEmpty(),
|
||||
username,
|
||||
}), undefined, true);
|
||||
}), {
|
||||
shouldThrow: true,
|
||||
});
|
||||
|
||||
return { result, error: undefined };
|
||||
} catch (error) {
|
||||
@ -100,10 +102,12 @@ export async function fetchExportedChatInvites({
|
||||
adminId: buildInputEntity(admin.id, admin.accessHash) as GramJs.InputUser,
|
||||
limit,
|
||||
revoked: isRevoked || undefined,
|
||||
}));
|
||||
}), {
|
||||
abortControllerChatId: peer.id,
|
||||
});
|
||||
|
||||
if (!exportedInvites) return undefined;
|
||||
addEntitiesWithPhotosToLocalDb(exportedInvites.users);
|
||||
addEntitiesToLocalDb(exportedInvites.users);
|
||||
|
||||
const invites = (exportedInvites.invites
|
||||
.filter((invite): invite is GramJs.ChatInviteExported => invite instanceof GramJs.ChatInviteExported))
|
||||
@ -138,7 +142,7 @@ export async function editExportedChatInvite({
|
||||
|
||||
if (!invite) return undefined;
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(invite.users);
|
||||
addEntitiesToLocalDb(invite.users);
|
||||
if (invite instanceof GramJs.messages.ExportedChatInvite && invite.invite instanceof GramJs.ChatInviteExported) {
|
||||
const replaceInvite = buildApiExportedInvite(invite.invite);
|
||||
return {
|
||||
@ -222,11 +226,13 @@ export async function fetchChatInviteImporters({
|
||||
? buildInputEntity(offsetUser.id, offsetUser.accessHash) as GramJs.InputUser : new GramJs.InputUserEmpty(),
|
||||
limit,
|
||||
requested: isRequested || undefined,
|
||||
}));
|
||||
}), {
|
||||
abortControllerChatId: peer.id,
|
||||
});
|
||||
|
||||
if (!result) return undefined;
|
||||
const users = result.users.map((user) => buildApiUser(user)).filter(Boolean);
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
return {
|
||||
importers: result.importers.map((importer) => buildChatInviteImporter(importer)),
|
||||
users: buildCollectionByKey(users, 'id'),
|
||||
@ -246,7 +252,9 @@ export function hideChatJoinRequest({
|
||||
peer: buildInputPeer(peer.id, peer.accessHash),
|
||||
userId: buildInputEntity(user.id, user.accessHash) as GramJs.InputUser,
|
||||
approved: isApproved || undefined,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function hideAllChatJoinRequests({
|
||||
@ -262,7 +270,9 @@ export function hideAllChatJoinRequests({
|
||||
peer: buildInputPeer(peer.id, peer.accessHash),
|
||||
approved: isApproved || undefined,
|
||||
link,
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function hideChatReportPanel(chat: ApiChat) {
|
||||
|
||||
@ -86,10 +86,6 @@ async function download(
|
||||
entityType, entityId, sizeType, params, mediaMatchType,
|
||||
} = parsed;
|
||||
|
||||
if (!isConnected) {
|
||||
return Promise.reject(new Error('ERROR: Client is not connected'));
|
||||
}
|
||||
|
||||
if (entityType === 'staticMap') {
|
||||
const accessHash = entityId;
|
||||
const parsedParams = new URLSearchParams(params);
|
||||
|
||||
@ -30,7 +30,7 @@ import {
|
||||
SUPPORTED_IMAGE_CONTENT_TYPES,
|
||||
SUPPORTED_VIDEO_CONTENT_TYPES,
|
||||
} from '../../../config';
|
||||
import { handleUpdates, invokeRequest, uploadFile } from './client';
|
||||
import { handleGramJsUpdate, invokeRequest, uploadFile } from './client';
|
||||
import {
|
||||
buildApiMessage,
|
||||
buildLocalForwardedMessage,
|
||||
@ -61,7 +61,7 @@ import {
|
||||
import { buildApiChatFromPreview, buildApiSendAsPeerId } from '../apiBuilders/chats';
|
||||
import { fetchFile } from '../../../util/files';
|
||||
import {
|
||||
addEntitiesWithPhotosToLocalDb,
|
||||
addEntitiesToLocalDb,
|
||||
addMessageToLocalDb,
|
||||
deserializeBytes,
|
||||
resolveMessageApiChatId,
|
||||
@ -71,7 +71,7 @@ import { requestChatUpdate } from './chats';
|
||||
import { getEmojiOnlyCountForMessage } from '../../../global/helpers/getEmojiOnlyCountForMessage';
|
||||
import { getServerTimeOffset } from '../../../util/serverTime';
|
||||
import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
||||
import { updater } from '../updater';
|
||||
import { updateChannelState } from '../updateManager';
|
||||
|
||||
const FAST_SEND_TIMEOUT = 1000;
|
||||
const INPUT_WAVEFORM_LENGTH = 63;
|
||||
@ -117,7 +117,11 @@ export async function fetchMessages({
|
||||
offsetId: Math.min(offsetId, MAX_INT_32),
|
||||
}),
|
||||
...pagination,
|
||||
}), undefined, true);
|
||||
}), {
|
||||
shouldThrow: true,
|
||||
abortControllerChatId: chat.id,
|
||||
abortControllerThreadId: threadId,
|
||||
});
|
||||
} catch (err: any) {
|
||||
if (err.message === 'CHANNEL_PRIVATE') {
|
||||
onUpdate({
|
||||
@ -167,8 +171,10 @@ export async function fetchMessage({ chat, messageId }: { chat: ApiChat; message
|
||||
: new GramJs.messages.GetMessages({
|
||||
id: [new GramJs.InputMessageID({ id: messageId })],
|
||||
}),
|
||||
undefined,
|
||||
true,
|
||||
{
|
||||
shouldThrow: true,
|
||||
abortControllerChatId: chat.id,
|
||||
},
|
||||
);
|
||||
} catch (err: any) {
|
||||
const { message } = err;
|
||||
@ -191,6 +197,10 @@ export async function fetchMessage({ chat, messageId }: { chat: ApiChat; message
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if ('pts' in result) {
|
||||
updateChannelState(chat.id, result.pts);
|
||||
}
|
||||
|
||||
const mtpMessage = result.messages[0];
|
||||
if (!mtpMessage) {
|
||||
return undefined;
|
||||
@ -355,7 +365,10 @@ export function sendMessage(
|
||||
...(noWebPage && { noWebpage: noWebPage }),
|
||||
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
||||
...(shouldUpdateStickerSetOrder && { updateStickersetsOrder: shouldUpdateStickerSetOrder }),
|
||||
}), false, true, true);
|
||||
}), {
|
||||
shouldThrow: true,
|
||||
shouldIgnoreUpdates: true,
|
||||
});
|
||||
if (update) handleLocalMessageUpdate(localMessage, update);
|
||||
} catch (error: any) {
|
||||
onUpdate({
|
||||
@ -476,7 +489,9 @@ function sendGroupedMedia(
|
||||
...(isSilent && { silent: isSilent }),
|
||||
...(scheduledAt && { scheduleDate: scheduledAt }),
|
||||
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
||||
}), false, undefined, true);
|
||||
}), {
|
||||
shouldIgnoreUpdates: true,
|
||||
});
|
||||
|
||||
if (update) handleMultipleLocalMessagesUpdate(localMessages, update);
|
||||
})();
|
||||
@ -573,7 +588,7 @@ export async function editMessage({
|
||||
id: message.id,
|
||||
...(isScheduled && { scheduleDate: message.date }),
|
||||
...(noWebPage && { noWebpage: noWebPage }),
|
||||
}), true);
|
||||
}));
|
||||
}
|
||||
|
||||
export async function rescheduleMessage({
|
||||
@ -589,7 +604,7 @@ export async function rescheduleMessage({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
id: message.id,
|
||||
scheduleDate: scheduledAt,
|
||||
}), true);
|
||||
}));
|
||||
}
|
||||
|
||||
async function uploadMedia(localMessage: ApiMessage, attachment: ApiAttachment, onProgress: ApiOnProgress) {
|
||||
@ -680,7 +695,7 @@ export async function unpinAllMessages({ chat, threadId }: { chat: ApiChat; thre
|
||||
await invokeRequest(new GramJs.messages.UnpinAllMessages({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
...(threadId && { topMsgId: threadId }),
|
||||
}), true);
|
||||
}));
|
||||
}
|
||||
|
||||
export async function deleteMessages({
|
||||
@ -791,7 +806,11 @@ export async function sendMessageAction({
|
||||
peer: buildInputPeer(peer.id, peer.accessHash),
|
||||
topMsgId: threadId,
|
||||
action: gramAction,
|
||||
}), undefined, true);
|
||||
}), {
|
||||
shouldThrow: true,
|
||||
abortControllerChatId: peer.id,
|
||||
abortControllerThreadId: threadId,
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
// Prevent error from being displayed in UI
|
||||
@ -964,8 +983,8 @@ export async function requestThreadInfoUpdate({
|
||||
});
|
||||
}
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(topMessageResult.users);
|
||||
addEntitiesWithPhotosToLocalDb(topMessageResult.chats);
|
||||
addEntitiesToLocalDb(topMessageResult.users);
|
||||
addEntitiesToLocalDb(topMessageResult.chats);
|
||||
|
||||
const users = topMessageResult.users.map(buildApiUser).filter(Boolean);
|
||||
|
||||
@ -1023,7 +1042,10 @@ export async function searchMessagesLocal({
|
||||
minDate,
|
||||
maxDate,
|
||||
...pagination,
|
||||
}));
|
||||
}), {
|
||||
abortControllerChatId: chat.id,
|
||||
abortControllerThreadId: topMessageId,
|
||||
});
|
||||
|
||||
if (
|
||||
!result
|
||||
@ -1170,7 +1192,7 @@ export async function sendPollVote({
|
||||
peer: buildInputPeer(id, accessHash),
|
||||
msgId: messageId,
|
||||
options: options.map(deserializeBytes),
|
||||
}), true);
|
||||
}));
|
||||
}
|
||||
|
||||
export async function closePoll({
|
||||
@ -1309,7 +1331,9 @@ export async function forwardMessages({
|
||||
...(toThreadId && { topMsgId: toThreadId }),
|
||||
...(scheduledAt && { scheduleDate: scheduledAt }),
|
||||
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
||||
}), false, undefined, true);
|
||||
}), {
|
||||
shouldIgnoreUpdates: true,
|
||||
});
|
||||
if (update) handleMultipleLocalMessagesUpdate(localMessages, update);
|
||||
}
|
||||
|
||||
@ -1343,7 +1367,9 @@ export async function fetchScheduledHistory({ chat }: { chat: ApiChat }) {
|
||||
|
||||
const result = await invokeRequest(new GramJs.messages.GetScheduledHistory({
|
||||
peer: buildInputPeer(id, accessHash),
|
||||
}));
|
||||
}), {
|
||||
abortControllerChatId: id,
|
||||
});
|
||||
|
||||
if (
|
||||
!result
|
||||
@ -1368,15 +1394,15 @@ export async function sendScheduledMessages({ chat, ids }: { chat: ApiChat; ids:
|
||||
await invokeRequest(new GramJs.messages.SendScheduledMessages({
|
||||
peer: buildInputPeer(id, accessHash),
|
||||
id: ids,
|
||||
}), true);
|
||||
}));
|
||||
}
|
||||
|
||||
function updateLocalDb(result: (
|
||||
GramJs.messages.MessagesSlice | GramJs.messages.Messages | GramJs.messages.ChannelMessages |
|
||||
GramJs.messages.DiscussionMessage | GramJs.messages.SponsoredMessages
|
||||
)) {
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesWithPhotosToLocalDb(result.chats);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.chats);
|
||||
|
||||
result.messages.forEach((message) => {
|
||||
if ((message instanceof GramJs.Message && isMessageWithMedia(message))
|
||||
@ -1396,7 +1422,10 @@ export async function fetchPinnedMessages({ chat, threadId }: { chat: ApiChat; t
|
||||
limit: PINNED_MESSAGES_LIMIT,
|
||||
topMsgId: threadId,
|
||||
},
|
||||
));
|
||||
), {
|
||||
abortControllerChatId: chat.id,
|
||||
abortControllerThreadId: threadId,
|
||||
});
|
||||
|
||||
if (
|
||||
!result
|
||||
@ -1441,14 +1470,17 @@ export async function fetchSendAs({
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.channels.GetSendAs({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
}), undefined, undefined, undefined, undefined, true);
|
||||
}), {
|
||||
shouldIgnoreErrors: true,
|
||||
abortControllerChatId: chat.id,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesWithPhotosToLocalDb(result.chats);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.chats);
|
||||
|
||||
const users = result.users.map(buildApiUser).filter(Boolean);
|
||||
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
|
||||
@ -1507,7 +1539,9 @@ export function readAllMentions({
|
||||
}) {
|
||||
return invokeRequest(new GramJs.messages.ReadMentions({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function readAllReactions({
|
||||
@ -1517,7 +1551,9 @@ export function readAllReactions({
|
||||
}) {
|
||||
return invokeRequest(new GramJs.messages.ReadReactions({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchUnreadMentions({
|
||||
@ -1652,13 +1688,17 @@ export async function translateText(params: TranslateTextParams) {
|
||||
function handleMultipleLocalMessagesUpdate(
|
||||
localMessages: Record<string, ApiMessage>, update: GramJs.TypeUpdates,
|
||||
) {
|
||||
if (!('updates' in update)) return;
|
||||
if (!('updates' in update)) {
|
||||
handleGramJsUpdate(update);
|
||||
return;
|
||||
}
|
||||
|
||||
update.updates.forEach((u) => {
|
||||
if (u instanceof GramJs.UpdateMessageID) {
|
||||
const localMessage = localMessages[u.randomId.toString()];
|
||||
handleLocalMessageUpdate(localMessage, u);
|
||||
} else {
|
||||
updater(u);
|
||||
handleGramJsUpdate(u);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1672,7 +1712,7 @@ function handleLocalMessageUpdate(localMessage: ApiMessage, update: GramJs.TypeU
|
||||
}
|
||||
|
||||
if (!messageUpdate) {
|
||||
handleUpdates(update);
|
||||
handleGramJsUpdate(update);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1719,5 +1759,5 @@ function handleLocalMessageUpdate(localMessage: ApiMessage, update: GramJs.TypeU
|
||||
},
|
||||
});
|
||||
|
||||
handleUpdates(update);
|
||||
handleGramJsUpdate(update);
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ import type {
|
||||
} from '../../types';
|
||||
import localDb from '../localDb';
|
||||
import {
|
||||
addEntitiesWithPhotosToLocalDb,
|
||||
addEntitiesToLocalDb,
|
||||
deserializeBytes,
|
||||
serializeBytes,
|
||||
} from '../helpers';
|
||||
@ -121,7 +121,7 @@ export async function getPaymentForm(inputInvoice: ApiRequestInputInvoice) {
|
||||
localDb.webDocuments[result.photo.url] = result.photo;
|
||||
}
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
return {
|
||||
form: buildApiPaymentForm(result),
|
||||
@ -140,7 +140,7 @@ export async function getReceipt(chat: ApiChat, msgId: number) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
return {
|
||||
receipt: buildApiReceipt(result),
|
||||
@ -152,7 +152,7 @@ export async function fetchPremiumPromo() {
|
||||
const result = await invokeRequest(new GramJs.help.GetPremiumPromo());
|
||||
if (!result) return undefined;
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
const users = result.users.map(buildApiUser).filter(Boolean);
|
||||
result.videos.forEach((video) => {
|
||||
|
||||
@ -9,7 +9,7 @@ import { buildApiUser } from '../apiBuilders/users';
|
||||
import { buildApiAvailableReaction, buildApiReaction, buildMessagePeerReaction } from '../apiBuilders/messages';
|
||||
import { invokeRequest } from './client';
|
||||
import localDb from '../localDb';
|
||||
import { addEntitiesWithPhotosToLocalDb } from '../helpers';
|
||||
import { addEntitiesToLocalDb } from '../helpers';
|
||||
|
||||
export function sendWatchingEmojiInteraction({
|
||||
chat,
|
||||
@ -22,7 +22,9 @@ export function sendWatchingEmojiInteraction({
|
||||
action: new GramJs.SendMessageEmojiInteractionSeen({
|
||||
emoticon,
|
||||
}),
|
||||
}));
|
||||
}), {
|
||||
abortControllerChatId: chat.id,
|
||||
});
|
||||
}
|
||||
|
||||
export function sendEmojiInteraction({
|
||||
@ -48,7 +50,9 @@ export function sendEmojiInteraction({
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}));
|
||||
}), {
|
||||
abortControllerChatId: chat.id,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getAvailableReactions() {
|
||||
@ -92,7 +96,10 @@ export function sendReaction({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
msgId: messageId,
|
||||
...(shouldAddToRecent && { addToRecent: true }),
|
||||
}), true, true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
shouldThrow: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchMessageReactions({
|
||||
@ -103,7 +110,10 @@ export function fetchMessageReactions({
|
||||
return invokeRequest(new GramJs.messages.GetMessagesReactions({
|
||||
id: ids,
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
abortControllerChatId: chat.id,
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchMessageReactionsList({
|
||||
@ -123,7 +133,7 @@ export async function fetchMessageReactionsList({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
const { nextOffset, reactions, count } = result;
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ import {
|
||||
import { getClient, invokeRequest, uploadFile } from './client';
|
||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
import { addEntitiesWithPhotosToLocalDb, addPhotoToLocalDb } from '../helpers';
|
||||
import { addEntitiesToLocalDb, addPhotoToLocalDb } from '../helpers';
|
||||
import localDb from '../localDb';
|
||||
|
||||
const BETA_LANG_CODES = ['ar', 'fa', 'id', 'ko', 'uz', 'en'];
|
||||
@ -54,14 +54,18 @@ export function updateProfile({
|
||||
firstName: firstName || '',
|
||||
lastName: lastName || '',
|
||||
about: about || '',
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function checkUsername(username: string) {
|
||||
try {
|
||||
const result = await invokeRequest(new GramJs.account.CheckUsername({
|
||||
username,
|
||||
}), undefined, true);
|
||||
}), {
|
||||
shouldThrow: true,
|
||||
});
|
||||
|
||||
return { result, error: undefined };
|
||||
} catch (error) {
|
||||
@ -79,7 +83,9 @@ export async function checkUsername(username: string) {
|
||||
}
|
||||
|
||||
export function updateUsername(username: string) {
|
||||
return invokeRequest(new GramJs.account.UpdateUsername({ username }), true);
|
||||
return invokeRequest(new GramJs.account.UpdateUsername({ username }), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateProfilePhoto(photo?: ApiPhoto, isFallback?: boolean) {
|
||||
@ -90,7 +96,7 @@ export async function updateProfilePhoto(photo?: ApiPhoto, isFallback?: boolean)
|
||||
}));
|
||||
if (!result) return undefined;
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
if (result.photo instanceof GramJs.Photo) {
|
||||
addPhotoToLocalDb(result.photo);
|
||||
return {
|
||||
@ -110,7 +116,7 @@ export async function uploadProfilePhoto(file: File, isFallback?: boolean, isVid
|
||||
|
||||
if (!result) return undefined;
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
if (result.photo instanceof GramJs.Photo) {
|
||||
addPhotoToLocalDb(result.photo);
|
||||
return {
|
||||
@ -137,7 +143,7 @@ export async function uploadContactProfilePhoto({
|
||||
|
||||
if (!result) return undefined;
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
const users = result.users.map(buildApiUser).filter(Boolean);
|
||||
|
||||
@ -157,7 +163,9 @@ export async function uploadContactProfilePhoto({
|
||||
|
||||
export async function deleteProfilePhotos(photos: ApiPhoto[]) {
|
||||
const photoIds = photos.map(buildInputPhoto).filter(Boolean);
|
||||
const isDeleted = await invokeRequest(new GramJs.photos.DeletePhotos({ id: photoIds }), true);
|
||||
const isDeleted = await invokeRequest(new GramJs.photos.DeletePhotos({ id: photoIds }), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
if (isDeleted) {
|
||||
photos.forEach((photo) => {
|
||||
delete localDb.photos[photo.id];
|
||||
@ -271,7 +279,7 @@ export async function fetchWebAuthorizations() {
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
return {
|
||||
users: result.users.map(buildApiUser).filter(Boolean),
|
||||
@ -290,7 +298,9 @@ export function terminateAllWebAuthorizations() {
|
||||
export async function fetchNotificationExceptions() {
|
||||
const result = await invokeRequest(new GramJs.account.GetNotifyExceptions({
|
||||
compareSound: true,
|
||||
}), undefined, undefined, true);
|
||||
}), {
|
||||
shouldIgnoreUpdates: true,
|
||||
});
|
||||
|
||||
if (!(result instanceof GramJs.Updates || result instanceof GramJs.UpdatesCombined)) {
|
||||
return undefined;
|
||||
@ -582,8 +592,8 @@ function updateLocalDb(
|
||||
GramJs.Updates | GramJs.UpdatesCombined
|
||||
),
|
||||
) {
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesWithPhotosToLocalDb(result.chats);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.chats);
|
||||
}
|
||||
|
||||
export async function fetchCountryList({ langCode = 'en' }: { langCode?: LangCode }) {
|
||||
|
||||
@ -6,7 +6,7 @@ import type {
|
||||
} from '../../types';
|
||||
|
||||
import { invokeRequest } from './client';
|
||||
import { addEntitiesWithPhotosToLocalDb } from '../helpers';
|
||||
import { addEntitiesToLocalDb } from '../helpers';
|
||||
import { buildInputEntity } from '../gramjsBuilders';
|
||||
import {
|
||||
buildChannelStatistics, buildGroupStatistics, buildMessageStatistics, buildMessagePublicForwards, buildGraph,
|
||||
@ -18,7 +18,9 @@ export async function fetchChannelStatistics({
|
||||
}: { chat: ApiChat; dcId?: number }) {
|
||||
const result = await invokeRequest(new GramJs.stats.GetBroadcastStats({
|
||||
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
|
||||
}), undefined, undefined, undefined, dcId);
|
||||
}), {
|
||||
dcId,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
@ -35,13 +37,15 @@ export async function fetchGroupStatistics({
|
||||
}: { chat: ApiChat; dcId?: number }) {
|
||||
const result = await invokeRequest(new GramJs.stats.GetMegagroupStats({
|
||||
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
|
||||
}), undefined, undefined, undefined, dcId);
|
||||
}), {
|
||||
dcId,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
return {
|
||||
users: result.users.map(buildApiUser).filter(Boolean),
|
||||
@ -61,7 +65,9 @@ export async function fetchMessageStatistics({
|
||||
const result = await invokeRequest(new GramJs.stats.GetMessageStats({
|
||||
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
|
||||
msgId: messageId,
|
||||
}), undefined, undefined, undefined, dcId);
|
||||
}), {
|
||||
dcId,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
@ -83,14 +89,16 @@ export async function fetchMessagePublicForwards({
|
||||
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
|
||||
msgId: messageId,
|
||||
offsetPeer: new GramJs.InputPeerEmpty(),
|
||||
}), undefined, undefined, undefined, dcId);
|
||||
}), {
|
||||
dcId,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if ('chats' in result) {
|
||||
addEntitiesWithPhotosToLocalDb(result.chats);
|
||||
addEntitiesToLocalDb(result.chats);
|
||||
}
|
||||
|
||||
return buildMessagePublicForwards(result);
|
||||
@ -110,7 +118,9 @@ export async function fetchStatisticsAsyncGraph({
|
||||
const result = await invokeRequest(new GramJs.stats.LoadAsyncGraph({
|
||||
token,
|
||||
...(x && { x: BigInt(x) }),
|
||||
}), undefined, undefined, undefined, dcId);
|
||||
}), {
|
||||
dcId,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
|
||||
@ -164,7 +164,9 @@ export async function fetchStickers(
|
||||
stickerset: 'id' in stickerSetInfo
|
||||
? buildInputStickerSet(stickerSetInfo.id, stickerSetInfo.accessHash)
|
||||
: buildInputStickerSetShortName(stickerSetInfo.shortName),
|
||||
}), undefined, true);
|
||||
}), {
|
||||
shouldThrow: true,
|
||||
});
|
||||
|
||||
if (!(result instanceof GramJs.messages.StickerSet)) {
|
||||
return undefined;
|
||||
@ -314,7 +316,7 @@ export function saveGif({ gif, shouldUnsave }: { gif: ApiVideo; shouldUnsave?: b
|
||||
unsave: shouldUnsave,
|
||||
});
|
||||
|
||||
return invokeRequest(request, true);
|
||||
return invokeRequest(request, { shouldReturnTrue: true });
|
||||
}
|
||||
|
||||
export async function installStickerSet({ stickerSetId, accessHash }: { stickerSetId: string; accessHash: string }) {
|
||||
|
||||
@ -18,7 +18,7 @@ import {
|
||||
import { buildApiUser, buildApiUserFullInfo, buildApiUsersAndStatuses } from '../apiBuilders/users';
|
||||
import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
||||
import { buildApiPhoto } from '../apiBuilders/common';
|
||||
import { addEntitiesWithPhotosToLocalDb, addPhotoToLocalDb, addUserToLocalDb } from '../helpers';
|
||||
import { addEntitiesToLocalDb, addPhotoToLocalDb, addUserToLocalDb } from '../helpers';
|
||||
import { buildApiPeerId } from '../apiBuilders/peers';
|
||||
import localDb from '../localDb';
|
||||
|
||||
@ -47,7 +47,7 @@ export async function fetchFullUser({
|
||||
}
|
||||
|
||||
updateLocalDb(result);
|
||||
addUserToLocalDb(result.users[0], true);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
if (result.fullUser.profilePhoto instanceof GramJs.Photo) {
|
||||
localDb.photos[result.fullUser.profilePhoto.id.toString()] = result.fullUser.profilePhoto;
|
||||
@ -142,11 +142,7 @@ export async function fetchContactList() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
result.users.forEach((user) => {
|
||||
if (user instanceof GramJs.User) {
|
||||
addUserToLocalDb(user, true);
|
||||
}
|
||||
});
|
||||
addEntitiesToLocalDb(result.users);
|
||||
|
||||
const { users, userStatusesById } = buildApiUsersAndStatuses(result.users);
|
||||
|
||||
@ -165,11 +161,7 @@ export async function fetchUsers({ users }: { users: ApiUser[] }) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
result.forEach((user) => {
|
||||
if (user instanceof GramJs.User) {
|
||||
addUserToLocalDb(user, true);
|
||||
}
|
||||
});
|
||||
addEntitiesToLocalDb(result);
|
||||
|
||||
return buildApiUsersAndStatuses(result);
|
||||
}
|
||||
@ -219,7 +211,9 @@ export function updateContact({
|
||||
lastName,
|
||||
phone: phoneNumber,
|
||||
...(shouldSharePhoneNumber && { addPhonePrivacyException: shouldSharePhoneNumber }),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteContact({
|
||||
@ -294,18 +288,22 @@ export function reportSpam(userOrChat: ApiUser | ApiChat) {
|
||||
|
||||
return invokeRequest(new GramJs.messages.ReportSpam({
|
||||
peer: buildInputPeer(id, accessHash),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateEmojiStatus(emojiStatus: ApiSticker, expires?: number) {
|
||||
return invokeRequest(new GramJs.account.UpdateEmojiStatus({
|
||||
emojiStatus: buildInputEmojiStatus(emojiStatus, expires),
|
||||
}), true);
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
function updateLocalDb(result: (GramJs.photos.Photos | GramJs.photos.PhotosSlice | GramJs.messages.Chats)) {
|
||||
if ('chats' in result) {
|
||||
addEntitiesWithPhotosToLocalDb(result.chats);
|
||||
addEntitiesToLocalDb(result.chats);
|
||||
}
|
||||
|
||||
if ('photos' in result) {
|
||||
@ -313,6 +311,6 @@ function updateLocalDb(result: (GramJs.photos.Photos | GramJs.photos.PhotosSlice
|
||||
}
|
||||
|
||||
if ('users' in result) {
|
||||
addEntitiesWithPhotosToLocalDb(result.users);
|
||||
addEntitiesToLocalDb(result.users);
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ import * as methods from './methods';
|
||||
|
||||
let onUpdate: OnApiUpdate;
|
||||
|
||||
export async function initApi(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs, initialLocalDb?: LocalDb) {
|
||||
export function initApi(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs, initialLocalDb?: LocalDb) {
|
||||
onUpdate = _onUpdate;
|
||||
|
||||
initUpdater(handleUpdate);
|
||||
@ -43,7 +43,7 @@ export async function initApi(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArg
|
||||
|
||||
if (initialLocalDb) updateFullLocalDb(initialLocalDb);
|
||||
|
||||
await initClient(handleUpdate, initialArgs);
|
||||
initClient(handleUpdate, initialArgs);
|
||||
}
|
||||
|
||||
export function callApi<T extends keyof Methods>(fnName: T, ...args: MethodArgs<T>): MethodResponse<T> {
|
||||
|
||||
412
src/api/gramjs/updateManager.ts
Normal file
412
src/api/gramjs/updateManager.ts
Normal file
@ -0,0 +1,412 @@
|
||||
import { Api as GramJs } from '../../lib/gramjs';
|
||||
import type { Update } from './updater';
|
||||
import type { invokeRequest } from './methods/client';
|
||||
|
||||
import { UpdateConnectionState, UpdateServerTimeOffset } from '../../lib/gramjs/network';
|
||||
import { DEBUG } from '../../config';
|
||||
import localDb from './localDb';
|
||||
import SortedQueue from '../../util/SortedQueue';
|
||||
import { dispatchUserAndChatUpdates, requestSync, updater } from './updater';
|
||||
import { addEntitiesToLocalDb } from './helpers';
|
||||
import { buildInputEntity } from './gramjsBuilders';
|
||||
import { buildApiPeerId } from './apiBuilders/peers';
|
||||
|
||||
export type State = {
|
||||
seq: number;
|
||||
date: number;
|
||||
pts: number;
|
||||
qts: number;
|
||||
};
|
||||
type SeqUpdate = GramJs.Updates | GramJs.UpdatesCombined;
|
||||
type PtsUpdate = GramJs.TypeUpdate & { pts: number };
|
||||
|
||||
const COMMON_BOX_QUEUE_ID = '0';
|
||||
const CHANNEL_DIFFERENCE_LIMIT = 1000;
|
||||
const UPDATE_WAIT_TIMEOUT = 500;
|
||||
|
||||
let invoke: typeof invokeRequest;
|
||||
let isInited = false;
|
||||
|
||||
let seqTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||
const PTS_TIMEOUTS = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
|
||||
const SEQ_QUEUE = new SortedQueue<SeqUpdate>(seqComparator);
|
||||
const PTS_QUEUE = new Map<string, SortedQueue<PtsUpdate>>();
|
||||
|
||||
export async function init(invokeReq: typeof invokeRequest) {
|
||||
invoke = invokeReq;
|
||||
|
||||
await loadRemoteState();
|
||||
isInited = true;
|
||||
|
||||
scheduleGetDifference();
|
||||
}
|
||||
|
||||
export function applyState(state: State) {
|
||||
localDb.commonBoxState.seq = state.seq;
|
||||
localDb.commonBoxState.date = state.date;
|
||||
localDb.commonBoxState.pts = state.pts;
|
||||
localDb.commonBoxState.qts = state.qts;
|
||||
}
|
||||
|
||||
export function processUpdate(update: Update) {
|
||||
if (update instanceof UpdateConnectionState) {
|
||||
if (update.state === UpdateConnectionState.connected && isInited) {
|
||||
scheduleGetDifference();
|
||||
}
|
||||
|
||||
updater(update);
|
||||
return;
|
||||
}
|
||||
|
||||
if (update instanceof UpdateServerTimeOffset) {
|
||||
updater(update);
|
||||
return;
|
||||
}
|
||||
|
||||
if (localDb.commonBoxState.seq === undefined) {
|
||||
// Drop updates received before first sync
|
||||
return;
|
||||
}
|
||||
|
||||
if (update instanceof GramJs.Updates || update instanceof GramJs.UpdatesCombined) {
|
||||
saveSeqUpdate(update);
|
||||
return;
|
||||
}
|
||||
|
||||
if ('pts' in update) {
|
||||
if (update instanceof GramJs.UpdateChannelTooLong) {
|
||||
getChannelDifference(getUpdateChannelId(update));
|
||||
return;
|
||||
}
|
||||
savePtsUpdate(update);
|
||||
return;
|
||||
}
|
||||
|
||||
updater(update);
|
||||
}
|
||||
|
||||
export function updateChannelState(channelId: string, pts: number) {
|
||||
const channel = localDb.chats[channelId];
|
||||
if (!(channel instanceof GramJs.Channel)) {
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`[UpdateManager] Channel ${channelId} not found in localDb`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const currentState = localDb.channelPtsById[channelId];
|
||||
|
||||
if (currentState && currentState < pts) {
|
||||
scheduleGetChannelDifference(channelId);
|
||||
return;
|
||||
}
|
||||
|
||||
localDb.channelPtsById[channelId] = pts;
|
||||
}
|
||||
|
||||
function applyUpdate(updateObject: SeqUpdate | PtsUpdate) {
|
||||
if ('seq' in updateObject) {
|
||||
if (updateObject.seq) localDb.commonBoxState.seq = updateObject.seq;
|
||||
localDb.commonBoxState.date = updateObject.date;
|
||||
}
|
||||
|
||||
if ('qts' in updateObject) {
|
||||
localDb.commonBoxState.qts = updateObject.qts;
|
||||
}
|
||||
|
||||
if ('pts' in updateObject) {
|
||||
const channelId = getUpdateChannelId(updateObject);
|
||||
if (channelId !== COMMON_BOX_QUEUE_ID) {
|
||||
localDb.channelPtsById[channelId] = updateObject.pts;
|
||||
} else {
|
||||
localDb.commonBoxState.pts = updateObject.pts;
|
||||
}
|
||||
}
|
||||
|
||||
if (updateObject instanceof GramJs.UpdatesCombined || updateObject instanceof GramJs.Updates) {
|
||||
const entities = updateObject.users.concat(updateObject.chats);
|
||||
|
||||
updateObject.updates.forEach((update) => {
|
||||
if (entities) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
(update as any)._entities = entities;
|
||||
}
|
||||
|
||||
processUpdate(update);
|
||||
});
|
||||
} else {
|
||||
updater(updateObject);
|
||||
}
|
||||
}
|
||||
|
||||
function saveSeqUpdate(update: GramJs.Updates | GramJs.UpdatesCombined) {
|
||||
SEQ_QUEUE.add(update);
|
||||
|
||||
popSeqQueue();
|
||||
}
|
||||
|
||||
function savePtsUpdate(update: PtsUpdate) {
|
||||
const channelId = getUpdateChannelId(update);
|
||||
|
||||
const ptsQueue = PTS_QUEUE.get(channelId) || new SortedQueue<PtsUpdate>(ptsComparator);
|
||||
ptsQueue.add(update);
|
||||
|
||||
PTS_QUEUE.set(channelId, ptsQueue);
|
||||
|
||||
popPtsQueue(channelId);
|
||||
}
|
||||
|
||||
function popSeqQueue() {
|
||||
if (!SEQ_QUEUE.size) return;
|
||||
|
||||
const update = SEQ_QUEUE.pop()!;
|
||||
const localSeq = localDb.commonBoxState.seq;
|
||||
const seqStart = 'seqStart' in update ? update.seqStart : update.seq;
|
||||
|
||||
if (seqStart === 0 || seqStart === localSeq + 1) {
|
||||
clearTimeout(seqTimeout);
|
||||
seqTimeout = undefined;
|
||||
|
||||
applyUpdate(update);
|
||||
} else if (seqStart > localSeq + 1) {
|
||||
SEQ_QUEUE.add(update); // Return update to queue
|
||||
scheduleGetDifference();
|
||||
return; // Prevent endless loop
|
||||
}
|
||||
|
||||
popSeqQueue();
|
||||
}
|
||||
|
||||
function popPtsQueue(channelId: string) {
|
||||
const ptsQueue = PTS_QUEUE.get(channelId);
|
||||
if (!ptsQueue?.size) return;
|
||||
|
||||
const update = ptsQueue.pop()!;
|
||||
const localPts = channelId === COMMON_BOX_QUEUE_ID ? localDb.commonBoxState.pts : localDb.channelPtsById[channelId];
|
||||
const pts = update.pts;
|
||||
const ptsCount = getPtsCount(update);
|
||||
|
||||
if (localPts === undefined) {
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('[UpdateManager] Got pts update without local state', channelId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (pts === localPts + ptsCount) {
|
||||
clearTimeout(PTS_TIMEOUTS.get(channelId));
|
||||
PTS_TIMEOUTS.delete(channelId);
|
||||
|
||||
applyUpdate(update);
|
||||
} else if (pts > localPts + ptsCount) {
|
||||
ptsQueue.add(update); // Return update to queue
|
||||
if (channelId === COMMON_BOX_QUEUE_ID) {
|
||||
scheduleGetDifference();
|
||||
} else {
|
||||
scheduleGetChannelDifference(channelId);
|
||||
}
|
||||
return; // Prevent endless loop
|
||||
}
|
||||
|
||||
popPtsQueue(channelId);
|
||||
}
|
||||
|
||||
function scheduleGetChannelDifference(channelId: string) {
|
||||
if (PTS_TIMEOUTS.has(channelId)) return;
|
||||
|
||||
const timeout = setTimeout(async () => {
|
||||
await getChannelDifference(channelId);
|
||||
PTS_TIMEOUTS.delete(channelId);
|
||||
}, UPDATE_WAIT_TIMEOUT);
|
||||
PTS_TIMEOUTS.set(channelId, timeout);
|
||||
}
|
||||
|
||||
function scheduleGetDifference() {
|
||||
if (seqTimeout) return;
|
||||
|
||||
seqTimeout = setTimeout(async () => {
|
||||
await getDifference();
|
||||
seqTimeout = undefined;
|
||||
}, UPDATE_WAIT_TIMEOUT);
|
||||
}
|
||||
|
||||
function getUpdateChannelId(update: Update) {
|
||||
if ('channelId' in update && 'pts' in update) {
|
||||
return buildApiPeerId(update.channelId, 'channel');
|
||||
}
|
||||
|
||||
if (update instanceof GramJs.UpdateNewChannelMessage || update instanceof GramJs.UpdateEditChannelMessage) {
|
||||
const peer = update.message.peerId as GramJs.PeerChannel;
|
||||
return buildApiPeerId(peer.channelId, 'channel');
|
||||
}
|
||||
|
||||
return COMMON_BOX_QUEUE_ID;
|
||||
}
|
||||
|
||||
export async function getDifference() {
|
||||
if (!isInited) {
|
||||
throw new Error('UpdatesManager not initialized');
|
||||
}
|
||||
|
||||
if (!localDb.commonBoxState?.date) {
|
||||
forceSync();
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await invoke(new GramJs.updates.GetDifference({
|
||||
pts: localDb.commonBoxState.pts,
|
||||
date: localDb.commonBoxState.date,
|
||||
qts: localDb.commonBoxState.qts,
|
||||
}));
|
||||
|
||||
SEQ_QUEUE.clear();
|
||||
PTS_QUEUE.get(COMMON_BOX_QUEUE_ID)?.clear();
|
||||
|
||||
if (!response || response instanceof GramJs.updates.DifferenceTooLong) {
|
||||
forceSync();
|
||||
return;
|
||||
}
|
||||
|
||||
if (response instanceof GramJs.updates.DifferenceEmpty) {
|
||||
localDb.commonBoxState.seq = response.seq;
|
||||
localDb.commonBoxState.date = response.date;
|
||||
return;
|
||||
}
|
||||
|
||||
processDifference(response);
|
||||
|
||||
const newState = response instanceof GramJs.updates.DifferenceSlice ? response.intermediateState : response.state;
|
||||
applyState(newState);
|
||||
|
||||
if (response instanceof GramJs.updates.DifferenceSlice) {
|
||||
getDifference();
|
||||
}
|
||||
}
|
||||
|
||||
async function getChannelDifference(channelId: string) {
|
||||
const channel = localDb.chats[channelId];
|
||||
if (!channel || !(channel instanceof GramJs.Channel) || !channel.accessHash || !localDb.channelPtsById[channelId]) {
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('[UpdateManager] Channel for difference not found', channelId, channel);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await invoke(new GramJs.updates.GetChannelDifference({
|
||||
channel: buildInputEntity(channelId, channel.accessHash.toString()) as GramJs.InputChannel,
|
||||
pts: localDb.channelPtsById[channelId],
|
||||
filter: new GramJs.ChannelMessagesFilterEmpty(),
|
||||
limit: CHANNEL_DIFFERENCE_LIMIT,
|
||||
}));
|
||||
|
||||
PTS_QUEUE.delete(channelId);
|
||||
|
||||
if (!response) {
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[UpdatesManager] Failed to get ChannelDifference', channelId, channel);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (response instanceof GramJs.updates.ChannelDifferenceTooLong) {
|
||||
forceSync();
|
||||
return;
|
||||
}
|
||||
|
||||
localDb.channelPtsById[channelId] = response.pts;
|
||||
|
||||
if (response instanceof GramJs.updates.ChannelDifferenceEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
processDifference(response);
|
||||
|
||||
if (!response.final) {
|
||||
getChannelDifference(channelId);
|
||||
}
|
||||
}
|
||||
|
||||
function forceSync() {
|
||||
reset();
|
||||
|
||||
requestSync();
|
||||
|
||||
loadRemoteState();
|
||||
}
|
||||
|
||||
export function reset() {
|
||||
PTS_QUEUE.clear();
|
||||
SEQ_QUEUE.clear();
|
||||
|
||||
clearTimeout(seqTimeout);
|
||||
seqTimeout = undefined;
|
||||
|
||||
PTS_TIMEOUTS.forEach((timeout) => {
|
||||
clearTimeout(timeout);
|
||||
});
|
||||
PTS_TIMEOUTS.clear();
|
||||
|
||||
localDb.commonBoxState = {};
|
||||
|
||||
Object.keys(localDb.channelPtsById).forEach((channelId) => {
|
||||
localDb.channelPtsById[channelId] = 0;
|
||||
});
|
||||
|
||||
isInited = false;
|
||||
}
|
||||
|
||||
async function loadRemoteState() {
|
||||
const remoteState = await invoke(new GramJs.updates.GetState());
|
||||
if (!remoteState) return;
|
||||
|
||||
applyState(remoteState);
|
||||
|
||||
isInited = true;
|
||||
}
|
||||
|
||||
function processDifference(
|
||||
difference: GramJs.updates.Difference | GramJs.updates.DifferenceSlice | GramJs.updates.ChannelDifference,
|
||||
) {
|
||||
difference.newMessages.forEach((message) => {
|
||||
updater(new GramJs.UpdateNewMessage({
|
||||
message,
|
||||
pts: 0,
|
||||
ptsCount: 0,
|
||||
}));
|
||||
});
|
||||
|
||||
addEntitiesToLocalDb(difference.users);
|
||||
addEntitiesToLocalDb(difference.chats);
|
||||
|
||||
dispatchUserAndChatUpdates(difference.users);
|
||||
dispatchUserAndChatUpdates(difference.chats);
|
||||
|
||||
difference.otherUpdates.forEach((update) => {
|
||||
processUpdate(update);
|
||||
});
|
||||
}
|
||||
|
||||
function getPtsCount(update: PtsUpdate) {
|
||||
return 'ptsCount' in update ? update.ptsCount : 0;
|
||||
}
|
||||
|
||||
function seqComparator(a: SeqUpdate, b: SeqUpdate) {
|
||||
const seqA = 'seqStart' in a ? a.seqStart : a.seq;
|
||||
const seqB = 'seqStart' in b ? b.seqStart : b.seq;
|
||||
|
||||
return seqA - seqB;
|
||||
}
|
||||
|
||||
function ptsComparator(a: PtsUpdate, b: PtsUpdate) {
|
||||
const diff = a.pts - b.pts;
|
||||
if (diff !== 0) {
|
||||
return diff;
|
||||
}
|
||||
|
||||
return getPtsCount(b) - getPtsCount(a);
|
||||
}
|
||||
@ -42,7 +42,7 @@ import localDb from './localDb';
|
||||
import { omitVirtualClassFields } from './apiBuilders/helpers';
|
||||
import {
|
||||
addMessageToLocalDb,
|
||||
addEntitiesWithPhotosToLocalDb,
|
||||
addEntitiesToLocalDb,
|
||||
addPhotoToLocalDb,
|
||||
resolveMessageApiChatId,
|
||||
serializeBytes,
|
||||
@ -68,7 +68,7 @@ import { buildApiEmojiInteraction, buildStickerSet } from './apiBuilders/symbols
|
||||
import { buildApiBotMenuButton } from './apiBuilders/bots';
|
||||
import { scheduleMutedTopicUpdate, scheduleMutedChatUpdate } from './scheduleUnmute';
|
||||
|
||||
type Update = (
|
||||
export type Update = (
|
||||
(GramJs.TypeUpdate | GramJs.TypeUpdates) & { _entities?: (GramJs.TypeUser | GramJs.TypeChat)[] }
|
||||
) | typeof connection.UpdateConnectionState;
|
||||
|
||||
@ -82,7 +82,7 @@ export function init(_onUpdate: OnApiUpdate) {
|
||||
|
||||
const sentMessageIds = new Set();
|
||||
|
||||
function dispatchUserAndChatUpdates(entities: (GramJs.TypeUser | GramJs.TypeChat)[]) {
|
||||
export function dispatchUserAndChatUpdates(entities: (GramJs.TypeUser | GramJs.TypeChat)[]) {
|
||||
entities
|
||||
.filter((e) => e instanceof GramJs.User)
|
||||
.map(buildApiUser)
|
||||
@ -117,6 +117,12 @@ function dispatchUserAndChatUpdates(entities: (GramJs.TypeUser | GramJs.TypeChat
|
||||
});
|
||||
}
|
||||
|
||||
export function requestSync() {
|
||||
onUpdate({
|
||||
'@type': 'requestSync',
|
||||
});
|
||||
}
|
||||
|
||||
export function updater(update: Update) {
|
||||
if (update instanceof connection.UpdateServerTimeOffset) {
|
||||
setServerTimeOffset(update.timeOffset);
|
||||
@ -160,7 +166,7 @@ export function updater(update: Update) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const entities = update._entities;
|
||||
if (entities) {
|
||||
addEntitiesWithPhotosToLocalDb(entities);
|
||||
addEntitiesToLocalDb(entities);
|
||||
dispatchUserAndChatUpdates(entities);
|
||||
}
|
||||
|
||||
@ -930,7 +936,7 @@ export function updater(update: Update) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const entities = update._entities;
|
||||
if (entities) {
|
||||
addEntitiesWithPhotosToLocalDb(entities);
|
||||
addEntitiesToLocalDb(entities);
|
||||
dispatchUserAndChatUpdates(entities);
|
||||
}
|
||||
|
||||
@ -948,7 +954,7 @@ export function updater(update: Update) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const entities = update._entities;
|
||||
if (entities) {
|
||||
addEntitiesWithPhotosToLocalDb(entities);
|
||||
addEntitiesToLocalDb(entities);
|
||||
dispatchUserAndChatUpdates(entities);
|
||||
}
|
||||
|
||||
@ -961,7 +967,7 @@ export function updater(update: Update) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const entities = update._entities;
|
||||
if (entities) {
|
||||
addEntitiesWithPhotosToLocalDb(entities);
|
||||
addEntitiesToLocalDb(entities);
|
||||
dispatchUserAndChatUpdates(entities);
|
||||
}
|
||||
|
||||
@ -975,7 +981,7 @@ export function updater(update: Update) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const entities = update._entities;
|
||||
if (entities) {
|
||||
addEntitiesWithPhotosToLocalDb(entities);
|
||||
addEntitiesToLocalDb(entities);
|
||||
dispatchUserAndChatUpdates(entities);
|
||||
}
|
||||
|
||||
@ -1013,7 +1019,7 @@ export function updater(update: Update) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const entities = update._entities;
|
||||
if (entities) {
|
||||
addEntitiesWithPhotosToLocalDb(entities);
|
||||
addEntitiesToLocalDb(entities);
|
||||
dispatchUserAndChatUpdates(entities);
|
||||
}
|
||||
|
||||
@ -1027,7 +1033,7 @@ export function updater(update: Update) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const entities = update._entities;
|
||||
if (entities) {
|
||||
addEntitiesWithPhotosToLocalDb(entities);
|
||||
addEntitiesToLocalDb(entities);
|
||||
dispatchUserAndChatUpdates(entities);
|
||||
}
|
||||
onUpdate({ '@type': 'updateConfig' });
|
||||
|
||||
@ -10,6 +10,7 @@ import { DATA_BROADCAST_CHANNEL_NAME, DEBUG } from '../../../config';
|
||||
import generateIdFor from '../../../util/generateIdFor';
|
||||
import { pause } from '../../../util/schedulers';
|
||||
import { getCurrentTabId, subscribeToMasterChange } from '../../../util/establishMultitabRole';
|
||||
import Deferred from '../../../util/Deferred';
|
||||
|
||||
type RequestStates = {
|
||||
messageId: string;
|
||||
@ -32,6 +33,9 @@ const savedLocalDb: LocalDb = {
|
||||
stickerSets: {},
|
||||
photos: {},
|
||||
webDocuments: {},
|
||||
|
||||
commonBoxState: {},
|
||||
channelPtsById: {},
|
||||
};
|
||||
|
||||
// TODO Re-use `util/WorkerConnector.ts`
|
||||
@ -57,6 +61,10 @@ export function initApiOnMasterTab(initialArgs: ApiInitialArgs) {
|
||||
|
||||
let updateCallback: OnApiUpdate;
|
||||
|
||||
let localApiRequestsQueue: { fnName: any; args: any; deferred: Deferred<any> }[] = [];
|
||||
let apiRequestsQueue: { fnName: any; args: any; deferred: Deferred<any> }[] = [];
|
||||
let isInited = false;
|
||||
|
||||
export function initApi(onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) {
|
||||
updateCallback = onUpdate;
|
||||
|
||||
@ -82,6 +90,22 @@ export function initApi(onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) {
|
||||
return makeRequest({
|
||||
type: 'initApi',
|
||||
args: [initialArgs, savedLocalDb],
|
||||
}).then(() => {
|
||||
isInited = true;
|
||||
|
||||
apiRequestsQueue.forEach((request) => {
|
||||
callApi(request.fnName, ...request.args)
|
||||
.then(request.deferred.resolve)
|
||||
.catch(request.deferred.reject);
|
||||
});
|
||||
apiRequestsQueue = [];
|
||||
|
||||
localApiRequestsQueue.forEach((request) => {
|
||||
callApiLocal(request.fnName, ...request.args)
|
||||
.then(request.deferred.resolve)
|
||||
.catch(request.deferred.reject);
|
||||
});
|
||||
localApiRequestsQueue = [];
|
||||
});
|
||||
}
|
||||
|
||||
@ -108,13 +132,11 @@ export function callApiOnMasterTab(payload: any) {
|
||||
* Mostly needed to disconnect worker when re-electing master
|
||||
*/
|
||||
export function callApiLocal<T extends keyof Methods>(fnName: T, ...args: MethodArgs<T>) {
|
||||
if (!worker) {
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('API is not initialized');
|
||||
}
|
||||
if (!isInited) {
|
||||
const deferred = new Deferred();
|
||||
localApiRequestsQueue.push({ fnName, args, deferred });
|
||||
|
||||
return undefined;
|
||||
return deferred.promise as MethodResponse<T>;
|
||||
}
|
||||
|
||||
const promise = makeRequest({
|
||||
@ -150,13 +172,11 @@ export function callApiLocal<T extends keyof Methods>(fnName: T, ...args: Method
|
||||
}
|
||||
|
||||
export function callApi<T extends keyof Methods>(fnName: T, ...args: MethodArgs<T>) {
|
||||
if (!worker && isMasterTab) {
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('API is not initialized');
|
||||
}
|
||||
if (!isInited && isMasterTab) {
|
||||
const deferred = new Deferred();
|
||||
apiRequestsQueue.push({ fnName, args, deferred });
|
||||
|
||||
return undefined;
|
||||
return deferred.promise as MethodResponse<T>;
|
||||
}
|
||||
|
||||
const promise = isMasterTab ? makeRequest({
|
||||
@ -236,7 +256,8 @@ function subscribeToWorker(onUpdate: OnApiUpdate) {
|
||||
});
|
||||
}
|
||||
|
||||
export function handleMethodResponse(data: { messageId: string;
|
||||
export function handleMethodResponse(data: {
|
||||
messageId: string;
|
||||
response?: ThenArg<MethodResponse<keyof Methods>>;
|
||||
error?: { message: string };
|
||||
}) {
|
||||
@ -250,7 +271,8 @@ export function handleMethodResponse(data: { messageId: string;
|
||||
}
|
||||
}
|
||||
|
||||
export function handleMethodCallback(data: { messageId: string;
|
||||
export function handleMethodCallback(data: {
|
||||
messageId: string;
|
||||
callbackArgs: any[];
|
||||
}) {
|
||||
requestStates.get(data.messageId)?.callback?.(...data.callbackArgs);
|
||||
|
||||
@ -21,7 +21,15 @@ onmessage = async (message: OriginMessageEvent) => {
|
||||
|
||||
switch (data.type) {
|
||||
case 'initApi': {
|
||||
await initApi(onUpdate, data.args[0], data.args[1]);
|
||||
const { messageId, args } = data;
|
||||
await initApi(onUpdate, args[0], args[1]);
|
||||
if (messageId) {
|
||||
sendToOrigin({
|
||||
type: 'methodResponse',
|
||||
messageId,
|
||||
response: true,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'callMethod': {
|
||||
|
||||
@ -619,6 +619,10 @@ export type ApiRequestInitApi = {
|
||||
'@type': 'requestInitApi';
|
||||
};
|
||||
|
||||
export type ApiRequestSync = {
|
||||
'@type': 'requestSync';
|
||||
};
|
||||
|
||||
export type ApiUpdate = (
|
||||
ApiUpdateReady | ApiUpdateSession | ApiUpdateWebAuthTokenFailed | ApiUpdateRequestUserUpdate |
|
||||
ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser |
|
||||
@ -645,7 +649,7 @@ export type ApiUpdate = (
|
||||
ApiUpdatePhoneCallConnectionState | ApiUpdateBotMenuButton | ApiUpdateTranscribedAudio | ApiUpdateUserEmojiStatus |
|
||||
ApiUpdateMessageExtendedMedia | ApiUpdateConfig | ApiUpdateTopicNotifyExceptions | ApiUpdatePinnedTopic |
|
||||
ApiUpdatePinnedTopicsOrder | ApiUpdateTopic | ApiUpdateTopics | ApiUpdateRecentEmojiStatuses |
|
||||
ApiUpdateRecentReactions | ApiRequestInitApi
|
||||
ApiUpdateRecentReactions | ApiRequestInitApi | ApiRequestSync
|
||||
);
|
||||
|
||||
export type OnApiUpdate = (update: ApiUpdate) => void;
|
||||
|
||||
@ -12,11 +12,11 @@ import AnimatedIconWithPreview from './AnimatedIconWithPreview';
|
||||
|
||||
type OwnProps =
|
||||
Partial<AnimatedIconProps>
|
||||
& { sticker?: ApiSticker; noLoad?: boolean; forcePreview?: boolean; lastSyncTime?: number };
|
||||
& { sticker?: ApiSticker; noLoad?: boolean; forcePreview?: boolean };
|
||||
|
||||
function AnimatedIconFromSticker(props: OwnProps) {
|
||||
const {
|
||||
sticker, noLoad, forcePreview, lastSyncTime, ...otherProps
|
||||
sticker, noLoad, forcePreview, ...otherProps
|
||||
} = props;
|
||||
|
||||
const thumbDataUri = sticker?.thumbnail?.dataUri;
|
||||
@ -25,9 +25,8 @@ function AnimatedIconFromSticker(props: OwnProps) {
|
||||
sticker ? getStickerPreviewHash(sticker.id) : undefined,
|
||||
noLoad && !forcePreview,
|
||||
ApiMediaFormat.BlobUrl,
|
||||
lastSyncTime,
|
||||
);
|
||||
const tgsUrl = useMedia(localMediaHash, noLoad, undefined, lastSyncTime);
|
||||
const tgsUrl = useMedia(localMediaHash, noLoad);
|
||||
|
||||
return (
|
||||
<AnimatedIconWithPreview
|
||||
|
||||
@ -50,7 +50,6 @@ type OwnProps = {
|
||||
uploadProgress?: number;
|
||||
origin: AudioOrigin;
|
||||
date?: number;
|
||||
lastSyncTime?: number;
|
||||
noAvatars?: boolean;
|
||||
className?: string;
|
||||
isSelectable?: boolean;
|
||||
@ -84,7 +83,6 @@ const Audio: FC<OwnProps> = ({
|
||||
uploadProgress,
|
||||
origin,
|
||||
date,
|
||||
lastSyncTime,
|
||||
noAvatars,
|
||||
className,
|
||||
isSelectable,
|
||||
@ -114,7 +112,7 @@ const Audio: FC<OwnProps> = ({
|
||||
|
||||
const { isMobile } = useAppLayout();
|
||||
const [isActivated, setIsActivated] = useState(false);
|
||||
const shouldLoad = (isActivated || PRELOAD) && lastSyncTime;
|
||||
const shouldLoad = isActivated || PRELOAD;
|
||||
const coverHash = getMessageMediaHash(message, 'pictogram');
|
||||
const coverBlobUrl = useMedia(coverHash, false, ApiMediaFormat.BlobUrl);
|
||||
|
||||
|
||||
@ -53,7 +53,6 @@ type OwnProps = {
|
||||
withVideo?: boolean;
|
||||
loopIndefinitely?: boolean;
|
||||
noPersonalPhoto?: boolean;
|
||||
lastSyncTime?: number;
|
||||
observeIntersection?: ObserveFn;
|
||||
onClick?: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>, hasMedia: boolean) => void;
|
||||
};
|
||||
@ -69,7 +68,6 @@ const Avatar: FC<OwnProps> = ({
|
||||
isSavedMessages,
|
||||
withVideo,
|
||||
loopIndefinitely,
|
||||
lastSyncTime,
|
||||
noPersonalPhoto,
|
||||
onClick,
|
||||
}) => {
|
||||
@ -98,8 +96,8 @@ const Avatar: FC<OwnProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
const imgBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl, lastSyncTime);
|
||||
const videoBlobUrl = useMedia(videoHash, !shouldLoadVideo, ApiMediaFormat.BlobUrl, lastSyncTime);
|
||||
const imgBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl);
|
||||
const videoBlobUrl = useMedia(videoHash, !shouldLoadVideo, ApiMediaFormat.BlobUrl);
|
||||
const hasBlobUrl = Boolean(imgBlobUrl || videoBlobUrl);
|
||||
// `videoBlobUrl` can be taken from memory cache, so we need to check `shouldLoadVideo` again
|
||||
const shouldPlayVideo = Boolean(videoBlobUrl && shouldLoadVideo);
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useEffect, useMemo, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { GlobalState } from '../../global/types';
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import type {
|
||||
ApiChat, ApiCountryCode, ApiUser, ApiUsername,
|
||||
} from '../../api/types';
|
||||
@ -56,13 +55,11 @@ type StateProps =
|
||||
topicId?: number;
|
||||
description?: string;
|
||||
chatInviteLink?: string;
|
||||
}
|
||||
& Pick<GlobalState, 'lastSyncTime'>;
|
||||
};
|
||||
|
||||
const runDebounced = debounce((cb) => cb(), 500, false);
|
||||
|
||||
const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
lastSyncTime,
|
||||
user,
|
||||
chat,
|
||||
forceShowSelf,
|
||||
@ -96,10 +93,9 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
}, [isMuted]);
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && userId) {
|
||||
loadFullUser({ userId });
|
||||
}
|
||||
}, [loadFullUser, userId, lastSyncTime]);
|
||||
if (!userId) return;
|
||||
loadFullUser({ userId });
|
||||
}, [userId]);
|
||||
|
||||
const isTopicInfo = Boolean(topicId && topicId !== MAIN_THREAD_ID);
|
||||
|
||||
@ -264,7 +260,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { chatOrUserId }): StateProps => {
|
||||
const { lastSyncTime, countryList: { phoneCodes: phoneCodeList } } = global;
|
||||
const { countryList: { phoneCodes: phoneCodeList } } = global;
|
||||
|
||||
const chat = chatOrUserId ? selectChat(global, chatOrUserId) : undefined;
|
||||
const user = isUserId(chatOrUserId) ? selectUser(global, chatOrUserId) : undefined;
|
||||
@ -284,7 +280,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
);
|
||||
|
||||
return {
|
||||
lastSyncTime,
|
||||
phoneCodeList,
|
||||
chat,
|
||||
user,
|
||||
|
||||
@ -93,7 +93,7 @@ const Document: FC<OwnProps> = ({
|
||||
|
||||
const documentHash = getMessageMediaHash(message, 'download');
|
||||
const { loadProgress: downloadProgress, mediaData } = useMediaWithLoadProgress(
|
||||
documentHash, !shouldDownload, getMessageMediaFormat(message, 'download'), undefined, undefined, true,
|
||||
documentHash, !shouldDownload, getMessageMediaFormat(message, 'download'), undefined, true,
|
||||
);
|
||||
const isLoaded = Boolean(mediaData);
|
||||
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import type { MouseEvent as ReactMouseEvent } from 'react';
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, { useEffect, memo, useMemo } from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import type {
|
||||
ApiChat, ApiTopic, ApiThreadInfo, ApiTypingStatus,
|
||||
} from '../../api/types';
|
||||
import type { GlobalState } from '../../global/types';
|
||||
import type { LangFn } from '../../hooks/useLang';
|
||||
import { MediaViewerOrigin } from '../../types';
|
||||
|
||||
@ -62,8 +60,7 @@ type StateProps =
|
||||
onlineCount?: number;
|
||||
areMessagesLoaded: boolean;
|
||||
messagesCount?: number;
|
||||
}
|
||||
& Pick<GlobalState, 'lastSyncTime'>;
|
||||
};
|
||||
|
||||
const GroupChatInfo: FC<OwnProps & StateProps> = ({
|
||||
typingStatus,
|
||||
@ -82,7 +79,6 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
|
||||
chat,
|
||||
onlineCount,
|
||||
areMessagesLoaded,
|
||||
lastSyncTime,
|
||||
topic,
|
||||
messagesCount,
|
||||
onClick,
|
||||
@ -93,19 +89,21 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
|
||||
loadProfilePhotos,
|
||||
} = getActions();
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
const isSuperGroup = chat && isChatSuperGroup(chat);
|
||||
const isTopic = Boolean(chat?.isForum && threadInfo && topic);
|
||||
const { id: chatId, isMin, isRestricted } = chat || {};
|
||||
|
||||
useEffect(() => {
|
||||
if (chatId && !isMin && lastSyncTime) {
|
||||
if (chatId && !isMin) {
|
||||
if (withFullInfo) loadFullChat({ chatId });
|
||||
if (withMediaViewer) loadProfilePhotos({ profileId: chatId });
|
||||
}
|
||||
}, [chatId, isMin, lastSyncTime, withFullInfo, loadFullChat, loadProfilePhotos, isSuperGroup, withMediaViewer]);
|
||||
}, [chatId, isMin, withFullInfo, loadFullChat, loadProfilePhotos, isSuperGroup, withMediaViewer]);
|
||||
|
||||
const handleAvatarViewerOpen = useLastCallback(
|
||||
(e: ReactMouseEvent<HTMLDivElement, MouseEvent>, hasMedia: boolean) => {
|
||||
(e: React.MouseEvent<HTMLDivElement, MouseEvent>, hasMedia: boolean) => {
|
||||
if (chat && hasMedia) {
|
||||
e.stopPropagation();
|
||||
openMediaViewer({
|
||||
@ -117,7 +115,6 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
|
||||
},
|
||||
);
|
||||
|
||||
const lang = useLang();
|
||||
const mainUsername = useMemo(() => chat && withUsername && getMainUsername(chat), [chat, withUsername]);
|
||||
|
||||
if (!chat) {
|
||||
@ -225,7 +222,6 @@ function getGroupStatus(lang: LangFn, chat: ApiChat) {
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { chatId, threadId }): StateProps => {
|
||||
const { lastSyncTime } = global;
|
||||
const chat = selectChat(global, chatId);
|
||||
const threadInfo = threadId ? selectThreadInfo(global, chatId, threadId) : undefined;
|
||||
const onlineCount = chat ? selectChatOnlineCount(global, chat) : undefined;
|
||||
@ -234,7 +230,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
const messagesCount = topic && selectThreadMessagesCount(global, chatId, threadId!);
|
||||
|
||||
return {
|
||||
lastSyncTime,
|
||||
chat,
|
||||
threadInfo,
|
||||
onlineCount,
|
||||
|
||||
@ -5,7 +5,6 @@ import type { FC } from '../../lib/teact/teact';
|
||||
import type {
|
||||
ApiUser, ApiTypingStatus, ApiUserStatus, ApiChatMember,
|
||||
} from '../../api/types';
|
||||
import type { GlobalState } from '../../global/types';
|
||||
import { MediaViewerOrigin } from '../../types';
|
||||
|
||||
import {
|
||||
@ -49,8 +48,7 @@ type StateProps =
|
||||
userStatus?: ApiUserStatus;
|
||||
isSavedMessages?: boolean;
|
||||
areMessagesLoaded: boolean;
|
||||
}
|
||||
& Pick<GlobalState, 'lastSyncTime'>;
|
||||
};
|
||||
|
||||
const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
typingStatus,
|
||||
@ -69,7 +67,6 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
userStatus,
|
||||
isSavedMessages,
|
||||
areMessagesLoaded,
|
||||
lastSyncTime,
|
||||
adminMember,
|
||||
}) => {
|
||||
const {
|
||||
@ -78,14 +75,16 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
loadProfilePhotos,
|
||||
} = getActions();
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
const { id: userId } = user || {};
|
||||
|
||||
useEffect(() => {
|
||||
if (userId && lastSyncTime) {
|
||||
if (userId) {
|
||||
if (withFullInfo) loadFullUser({ userId });
|
||||
if (withMediaViewer) loadProfilePhotos({ profileId: userId });
|
||||
}
|
||||
}, [userId, loadFullUser, loadProfilePhotos, lastSyncTime, withFullInfo, withMediaViewer]);
|
||||
}, [userId, withFullInfo, withMediaViewer]);
|
||||
|
||||
const handleAvatarViewerOpen = useLastCallback(
|
||||
(e: React.MouseEvent<HTMLDivElement, MouseEvent>, hasMedia: boolean) => {
|
||||
@ -100,7 +99,6 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
},
|
||||
);
|
||||
|
||||
const lang = useLang();
|
||||
const mainUsername = useMemo(() => user && withUsername && getMainUsername(user), [user, withUsername]);
|
||||
|
||||
if (!user) {
|
||||
@ -189,14 +187,12 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { userId, forceShowSelf }): StateProps => {
|
||||
const { lastSyncTime } = global;
|
||||
const user = selectUser(global, userId);
|
||||
const userStatus = selectUserStatus(global, userId);
|
||||
const isSavedMessages = !forceShowSelf && user && user.isSelf;
|
||||
const areMessagesLoaded = Boolean(selectChatMessages(global, userId));
|
||||
|
||||
return {
|
||||
lastSyncTime,
|
||||
user,
|
||||
userStatus,
|
||||
isSavedMessages,
|
||||
|
||||
@ -33,7 +33,6 @@ type OwnProps = {
|
||||
user?: ApiUser;
|
||||
isSavedMessages?: boolean;
|
||||
photo?: ApiPhoto;
|
||||
lastSyncTime?: number;
|
||||
canPlayVideo: boolean;
|
||||
onClick: NoneToVoidFunction;
|
||||
};
|
||||
@ -44,7 +43,6 @@ const ProfilePhoto: FC<OwnProps> = ({
|
||||
photo,
|
||||
isSavedMessages,
|
||||
canPlayVideo,
|
||||
lastSyncTime,
|
||||
onClick,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
@ -60,13 +58,13 @@ const ProfilePhoto: FC<OwnProps> = ({
|
||||
const { isVideo } = photo || {};
|
||||
|
||||
const avatarHash = canHaveMedia && getChatAvatarHash(userOrChat, 'normal');
|
||||
const avatarBlobUrl = useMedia(avatarHash, undefined, undefined, lastSyncTime);
|
||||
const avatarBlobUrl = useMedia(avatarHash);
|
||||
|
||||
const photoHash = canHaveMedia && photo && !isVideo && `photo${photo.id}?size=c`;
|
||||
const photoBlobUrl = useMedia(photoHash, undefined, undefined, lastSyncTime);
|
||||
const photoBlobUrl = useMedia(photoHash);
|
||||
|
||||
const videoHash = canHaveMedia && photo && isVideo && getVideoAvatarMediaHash(photo);
|
||||
const videoBlobUrl = useMedia(videoHash, undefined, undefined, lastSyncTime);
|
||||
const videoBlobUrl = useMedia(videoHash);
|
||||
|
||||
const fullMediaData = videoBlobUrl || photoBlobUrl;
|
||||
const [isVideoReady, markVideoReady] = useFlag();
|
||||
|
||||
@ -48,7 +48,6 @@ type OwnProps = {
|
||||
withSharedAnimation?: boolean;
|
||||
sharedCanvasRef?: React.RefObject<HTMLCanvasElement>;
|
||||
withTranslucentThumb?: boolean; // With shared canvas thumbs are opaque by default to provide better transition effect
|
||||
cacheBuster?: number;
|
||||
onVideoEnded?: AnyToVoidFunction;
|
||||
onAnimatedStickerLoop?: AnyToVoidFunction;
|
||||
};
|
||||
@ -77,7 +76,6 @@ const StickerView: FC<OwnProps> = ({
|
||||
withSharedAnimation,
|
||||
withTranslucentThumb,
|
||||
sharedCanvasRef,
|
||||
cacheBuster,
|
||||
onVideoEnded,
|
||||
onAnimatedStickerLoop,
|
||||
}) => {
|
||||
@ -102,9 +100,7 @@ const StickerView: FC<OwnProps> = ({
|
||||
const thumbDataUri = useThumbnail(sticker);
|
||||
// Use preview instead of thumb but only if it's already loaded or when playing an animation is disabled
|
||||
const previewMediaDataFromCache: string | undefined = mediaLoader.getFromMemory(previewMediaHash);
|
||||
const previewMediaData = useMedia(
|
||||
previewMediaHash, Boolean(previewMediaDataFromCache || !noPlay), undefined, cacheBuster,
|
||||
);
|
||||
const previewMediaData = useMedia(previewMediaHash, Boolean(previewMediaDataFromCache || !noPlay));
|
||||
const thumbData = customColor ? thumbDataUri : (previewMediaData || thumbDataUri);
|
||||
|
||||
const shouldForcePreview = isUnsupportedVideo || (isStatic && isSmall);
|
||||
@ -113,7 +109,7 @@ const StickerView: FC<OwnProps> = ({
|
||||
// If preloaded preview is forced, it will render as thumb, so no need to load it again
|
||||
const shouldSkipFullMedia = Boolean(fullMediaHash === previewMediaHash && previewMediaData);
|
||||
|
||||
const fullMediaData = useMedia(fullMediaHash, !shouldLoad || shouldSkipFullMedia, undefined, cacheBuster);
|
||||
const fullMediaData = useMedia(fullMediaHash, !shouldLoad || shouldSkipFullMedia);
|
||||
// If Lottie data is loaded we will only render thumb if it's good enough (from preview)
|
||||
const [isPlayerReady, markPlayerReady] = useFlag(Boolean(isLottie && fullMediaData && !previewMediaData));
|
||||
// Delay mounting on Android until heavy animation ends
|
||||
@ -129,7 +125,7 @@ const StickerView: FC<OwnProps> = ({
|
||||
const coords = useCoordsInSharedCanvas(containerRef, sharedCanvasRef);
|
||||
|
||||
// Preload preview for Message Input and local message
|
||||
useMedia(previewMediaHash, !shouldLoad || !shouldPreloadPreview, undefined, cacheBuster);
|
||||
useMedia(previewMediaHash, !shouldLoad || !shouldPreloadPreview);
|
||||
|
||||
const randomIdPrefix = useMemo(() => generateIdFor(ID_STORE, true), []);
|
||||
const renderId = [
|
||||
|
||||
@ -88,7 +88,6 @@ type StateProps = {
|
||||
isSelectedForum?: boolean;
|
||||
canScrollDown?: boolean;
|
||||
canChangeFolder?: boolean;
|
||||
lastSyncTime?: number;
|
||||
lastMessageTopic?: ApiTopic;
|
||||
typingStatus?: ApiTypingStatus;
|
||||
withInterfaceAnimations?: boolean;
|
||||
@ -117,7 +116,6 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
isSelectedForum,
|
||||
canScrollDown,
|
||||
canChangeFolder,
|
||||
lastSyncTime,
|
||||
lastMessageTopic,
|
||||
typingStatus,
|
||||
onDragEnter,
|
||||
@ -219,10 +217,10 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
|
||||
// Load the forum topics to display unread count badge
|
||||
useEffect(() => {
|
||||
if (isIntersecting && lastSyncTime && isForum && chat && chat.listedTopicIds === undefined) {
|
||||
if (isIntersecting && isForum && chat && chat.listedTopicIds === undefined) {
|
||||
loadTopics({ chatId });
|
||||
}
|
||||
}, [chat, chatId, isForum, isIntersecting, lastSyncTime, loadTopics]);
|
||||
}, [chat, chatId, isForum, isIntersecting]);
|
||||
|
||||
if (!chat) {
|
||||
return undefined;
|
||||
@ -254,7 +252,6 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
user={user}
|
||||
userStatus={userStatus}
|
||||
isSavedMessages={user?.isSelf}
|
||||
lastSyncTime={lastSyncTime}
|
||||
/>
|
||||
<AvatarBadge chatId={chatId} />
|
||||
{chat.isCallActive && chat.isCallNotEmpty && (
|
||||
@ -363,7 +360,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
isSelectedForum,
|
||||
canScrollDown: isSelected && messageListType === 'thread',
|
||||
canChangeFolder: (global.chatFolders.orderedIds?.length || 0) > 1,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
...(isOutgoing && chat.lastMessage && {
|
||||
lastMessageOutgoingStatus: selectOutgoingStatus(global, chat.lastMessage),
|
||||
}),
|
||||
|
||||
@ -43,7 +43,6 @@ type StateProps = {
|
||||
orderedFolderIds?: number[];
|
||||
activeChatFolder: number;
|
||||
currentUserId?: string;
|
||||
lastSyncTime?: number;
|
||||
shouldSkipHistoryAnimations?: boolean;
|
||||
maxFolders: number;
|
||||
maxFolderInvites: number;
|
||||
@ -63,7 +62,6 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
|
||||
activeChatFolder,
|
||||
currentUserId,
|
||||
isForumPanelOpen,
|
||||
lastSyncTime,
|
||||
shouldSkipHistoryAnimations,
|
||||
maxFolders,
|
||||
shouldHideFolderTabs,
|
||||
@ -88,10 +86,8 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
|
||||
const lang = useLang();
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSyncTime) {
|
||||
loadChatFolders();
|
||||
}
|
||||
}, [lastSyncTime, loadChatFolders]);
|
||||
loadChatFolders();
|
||||
}, []);
|
||||
|
||||
const allChatsFolder: ApiChatFolder = useMemo(() => {
|
||||
return {
|
||||
@ -275,7 +271,6 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
|
||||
folderId={isFolder ? activeFolder.id : undefined}
|
||||
isActive={isActive}
|
||||
isForumPanelOpen={isForumPanelOpen}
|
||||
lastSyncTime={lastSyncTime}
|
||||
foldersDispatch={foldersDispatch}
|
||||
onSettingsScreenSelect={onSettingsScreenSelect}
|
||||
onLeftColumnContentChange={onLeftColumnContentChange}
|
||||
@ -331,7 +326,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
},
|
||||
},
|
||||
currentUserId,
|
||||
lastSyncTime,
|
||||
archiveSettings,
|
||||
} = global;
|
||||
const { shouldSkipHistoryAnimations, activeChatFolder } = selectTabState(global);
|
||||
@ -342,7 +336,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
orderedFolderIds,
|
||||
activeChatFolder,
|
||||
currentUserId,
|
||||
lastSyncTime,
|
||||
shouldSkipHistoryAnimations,
|
||||
hasArchivedChats: Boolean(archived?.length),
|
||||
maxFolders: selectCurrentLimit(global, 'dialogFilters'),
|
||||
|
||||
@ -41,7 +41,6 @@ type OwnProps = {
|
||||
canDisplayArchive?: boolean;
|
||||
archiveSettings: GlobalState['archiveSettings'];
|
||||
isForumPanelOpen?: boolean;
|
||||
lastSyncTime?: number;
|
||||
foldersDispatch: FolderEditDispatch;
|
||||
onSettingsScreenSelect: (screen: SettingsScreens) => void;
|
||||
onLeftColumnContentChange: (content: LeftColumnContent) => void;
|
||||
|
||||
@ -54,7 +54,6 @@ type OwnProps = {
|
||||
type StateProps = {
|
||||
chat?: ApiChat;
|
||||
currentTopicId?: number;
|
||||
lastSyncTime?: number;
|
||||
withInterfaceAnimations?: boolean;
|
||||
};
|
||||
|
||||
@ -65,7 +64,6 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
||||
currentTopicId,
|
||||
isOpen,
|
||||
isHidden,
|
||||
lastSyncTime,
|
||||
onTopicSearch,
|
||||
onCloseAnimationEnd,
|
||||
onOpenAnimationStart,
|
||||
@ -85,10 +83,10 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
||||
const { isMobile } = useAppLayout();
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && chat && !chat.topics) {
|
||||
if (chat && !chat.topics) {
|
||||
loadTopics({ chatId: chat.id });
|
||||
}
|
||||
}, [chat, lastSyncTime, loadTopics]);
|
||||
}, [chat, loadTopics]);
|
||||
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const lang = useLang();
|
||||
@ -126,7 +124,7 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
||||
const { orderDiffById, getAnimationType } = useOrderDiff(orderedIds, chat?.id);
|
||||
|
||||
const [viewportIds, getMore] = useInfiniteScroll(() => {
|
||||
if (!chat || !lastSyncTime) return;
|
||||
if (!chat) return;
|
||||
loadTopics({ chatId: chat.id });
|
||||
}, orderedIds, !chat?.topicsCount || orderedIds.length >= chat.topicsCount, TOPICS_SLICE);
|
||||
|
||||
@ -294,7 +292,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
return {
|
||||
chat,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
currentTopicId: chatId === currentChatId ? currentThreadId : undefined,
|
||||
withInterfaceAnimations: selectCanAnimateInterface(global),
|
||||
};
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useCallback, useMemo } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import { AudioOrigin, LoadMoreDirection } from '../../../types';
|
||||
import type { StateProps } from './helpers/createMapStateToProps';
|
||||
|
||||
import { SLIDE_TRANSITION_DURATION } from '../../../config';
|
||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||
import type { StateProps } from './helpers/createMapStateToProps';
|
||||
import { createMapStateToProps } from './helpers/createMapStateToProps';
|
||||
import { formatMonthAndYear, toYearMonth } from '../../../util/dateFormat';
|
||||
import { getSenderName } from './helpers/getSenderName';
|
||||
import { throttle } from '../../../util/schedulers';
|
||||
|
||||
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
|
||||
@ -35,7 +36,6 @@ const AudioResults: FC<OwnProps & StateProps> = ({
|
||||
usersById,
|
||||
globalMessagesByChatId,
|
||||
foundIds,
|
||||
lastSyncTime,
|
||||
activeDownloads,
|
||||
}) => {
|
||||
const {
|
||||
@ -47,7 +47,7 @@ const AudioResults: FC<OwnProps & StateProps> = ({
|
||||
const lang = useLang();
|
||||
const currentType = isVoice ? 'voice' : 'audio';
|
||||
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
||||
if (direction === LoadMoreDirection.Backwards) {
|
||||
runThrottled(() => {
|
||||
searchMessagesGlobal({
|
||||
type: currentType,
|
||||
@ -55,7 +55,7 @@ const AudioResults: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps -- `searchQuery` is required to prevent infinite message loading
|
||||
}, [currentType, lastSyncTime, searchMessagesGlobal, searchQuery]);
|
||||
}, [currentType, searchMessagesGlobal, searchQuery]);
|
||||
|
||||
const foundMessages = useMemo(() => {
|
||||
if (!foundIds || !globalMessagesByChatId) {
|
||||
@ -98,7 +98,6 @@ const AudioResults: FC<OwnProps & StateProps> = ({
|
||||
origin={AudioOrigin.Search}
|
||||
senderTitle={getSenderName(lang, message, chatsById, usersById)}
|
||||
date={message.date}
|
||||
lastSyncTime={lastSyncTime}
|
||||
className="scroll-item"
|
||||
onPlay={handlePlayAudio}
|
||||
onDateClick={handleMessageFocus}
|
||||
|
||||
@ -44,7 +44,6 @@ type StateProps = {
|
||||
chat?: ApiChat;
|
||||
privateChatUser?: ApiUser;
|
||||
lastMessageOutgoingStatus?: ApiMessageOutgoingStatus;
|
||||
lastSyncTime?: number;
|
||||
};
|
||||
|
||||
const ChatMessage: FC<OwnProps & StateProps> = ({
|
||||
@ -53,7 +52,6 @@ const ChatMessage: FC<OwnProps & StateProps> = ({
|
||||
chatId,
|
||||
chat,
|
||||
privateChatUser,
|
||||
lastSyncTime,
|
||||
}) => {
|
||||
const { focusMessage } = getActions();
|
||||
|
||||
@ -85,7 +83,6 @@ const ChatMessage: FC<OwnProps & StateProps> = ({
|
||||
chat={chat}
|
||||
user={privateChatUser}
|
||||
isSavedMessages={privateChatUser?.isSelf}
|
||||
lastSyncTime={lastSyncTime}
|
||||
/>
|
||||
<div className="info">
|
||||
<div className="info-row">
|
||||
@ -147,7 +144,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
return {
|
||||
chat,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
...(privateChatUserId && { privateChatUser }),
|
||||
};
|
||||
},
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useCallback, useMemo } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { ApiChat, ApiMessage } from '../../../api/types';
|
||||
import { LoadMoreDirection } from '../../../types';
|
||||
|
||||
@ -9,6 +9,7 @@ import { selectTabState } from '../../../global/selectors';
|
||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||
import { throttle } from '../../../util/schedulers';
|
||||
import { renderMessageSummary } from '../../common/helpers/renderMessageText';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useAppLayout from '../../../hooks/useAppLayout';
|
||||
|
||||
@ -33,7 +34,6 @@ type StateProps = {
|
||||
fetchingStatus?: { chats?: boolean; messages?: boolean };
|
||||
foundTopicIds?: number[];
|
||||
searchChatId?: string;
|
||||
lastSyncTime?: number;
|
||||
};
|
||||
|
||||
const runThrottled = throttle((cb) => cb(), 500, true);
|
||||
@ -45,7 +45,6 @@ const ChatMessageResults: FC<OwnProps & StateProps> = ({
|
||||
globalMessagesByChatId,
|
||||
chatsById,
|
||||
fetchingStatus,
|
||||
lastSyncTime,
|
||||
foundTopicIds,
|
||||
searchChatId,
|
||||
onSearchDateSelect,
|
||||
@ -57,7 +56,7 @@ const ChatMessageResults: FC<OwnProps & StateProps> = ({
|
||||
const { isMobile } = useAppLayout();
|
||||
|
||||
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
||||
if (direction === LoadMoreDirection.Backwards) {
|
||||
runThrottled(() => {
|
||||
searchMessagesGlobal({
|
||||
type: 'text',
|
||||
@ -65,7 +64,7 @@ const ChatMessageResults: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps -- `searchQuery` is required to prevent infinite message loading
|
||||
}, [lastSyncTime, searchMessagesGlobal, searchQuery]);
|
||||
}, [searchQuery]);
|
||||
|
||||
const handleTopicClick = useCallback(
|
||||
(id: number) => {
|
||||
@ -167,7 +166,7 @@ const ChatMessageResults: FC<OwnProps & StateProps> = ({
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
const { byId: chatsById } = global.chats;
|
||||
const { currentUserId, messages: { byChatId: globalMessagesByChatId }, lastSyncTime } = global;
|
||||
const { currentUserId, messages: { byChatId: globalMessagesByChatId } } = global;
|
||||
const {
|
||||
fetchingStatus, resultsByType, foundTopicIds, chatId: searchChatId,
|
||||
} = selectTabState(global).globalSearch;
|
||||
@ -181,7 +180,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
chatsById,
|
||||
fetchingStatus,
|
||||
foundTopicIds,
|
||||
lastSyncTime,
|
||||
searchChatId,
|
||||
};
|
||||
},
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useCallback, useMemo, useRef, useState,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, getGlobal, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { ApiChat, ApiMessage } from '../../../api/types';
|
||||
import { LoadMoreDirection } from '../../../types';
|
||||
|
||||
@ -49,7 +49,6 @@ type StateProps = {
|
||||
globalMessagesByChatId?: Record<string, { byId: Record<number, ApiMessage> }>;
|
||||
chatsById: Record<string, ApiChat>;
|
||||
fetchingStatus?: { chats?: boolean; messages?: boolean };
|
||||
lastSyncTime?: number;
|
||||
};
|
||||
|
||||
const MIN_QUERY_LENGTH_FOR_GLOBAL_SEARCH = 4;
|
||||
@ -58,10 +57,21 @@ const LESS_LIST_ITEMS_AMOUNT = 5;
|
||||
const runThrottled = throttle((cb) => cb(), 500, false);
|
||||
|
||||
const ChatResults: FC<OwnProps & StateProps> = ({
|
||||
searchQuery, searchDate, dateSearchQuery, currentUserId,
|
||||
localContactIds, localChatIds, localUserIds, globalChatIds, globalUserIds,
|
||||
foundIds, globalMessagesByChatId, chatsById, fetchingStatus, lastSyncTime,
|
||||
onReset, onSearchDateSelect,
|
||||
searchQuery,
|
||||
searchDate,
|
||||
dateSearchQuery,
|
||||
currentUserId,
|
||||
localContactIds,
|
||||
localChatIds,
|
||||
localUserIds,
|
||||
globalChatIds,
|
||||
globalUserIds,
|
||||
foundIds,
|
||||
globalMessagesByChatId,
|
||||
chatsById,
|
||||
fetchingStatus,
|
||||
onReset,
|
||||
onSearchDateSelect,
|
||||
}) => {
|
||||
const {
|
||||
openChat, addRecentlyFoundChatId, searchMessagesGlobal, setGlobalSearchChatId,
|
||||
@ -77,7 +87,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
|
||||
const [shouldShowMoreGlobal, setShouldShowMoreGlobal] = useState<boolean>(false);
|
||||
|
||||
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
||||
if (direction === LoadMoreDirection.Backwards) {
|
||||
runThrottled(() => {
|
||||
searchMessagesGlobal({
|
||||
type: 'text',
|
||||
@ -85,7 +95,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps -- `searchQuery` is required to prevent infinite message loading
|
||||
}, [lastSyncTime, searchMessagesGlobal, searchQuery]);
|
||||
}, [searchQuery]);
|
||||
|
||||
const handleChatClick = useCallback(
|
||||
(id: string) => {
|
||||
@ -293,6 +303,9 @@ export default memo(withGlobal<OwnProps>(
|
||||
const { byId: chatsById } = global.chats;
|
||||
|
||||
const { userIds: localContactIds } = global.contactList || {};
|
||||
const {
|
||||
currentUserId, messages,
|
||||
} = global;
|
||||
|
||||
if (!localContactIds) {
|
||||
return {
|
||||
@ -300,9 +313,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
currentUserId, messages, lastSyncTime,
|
||||
} = global;
|
||||
const {
|
||||
fetchingStatus, globalResults, localResults, resultsByType,
|
||||
} = selectTabState(global).globalSearch;
|
||||
@ -322,7 +332,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
globalMessagesByChatId,
|
||||
chatsById,
|
||||
fetchingStatus,
|
||||
lastSyncTime,
|
||||
};
|
||||
},
|
||||
)(ChatResults));
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useCallback, useMemo, useRef,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { ApiMessage } from '../../../api/types';
|
||||
import type { StateProps } from './helpers/createMapStateToProps';
|
||||
import { LoadMoreDirection } from '../../../types';
|
||||
|
||||
import { SLIDE_TRANSITION_DURATION } from '../../../config';
|
||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||
import type { StateProps } from './helpers/createMapStateToProps';
|
||||
import { createMapStateToProps } from './helpers/createMapStateToProps';
|
||||
import { formatMonthAndYear, toYearMonth } from '../../../util/dateFormat';
|
||||
import { getSenderName } from './helpers/getSenderName';
|
||||
import { throttle } from '../../../util/schedulers';
|
||||
import { getMessageDocument } from '../../../global/helpers';
|
||||
|
||||
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||
@ -41,7 +42,6 @@ const FileResults: FC<OwnProps & StateProps> = ({
|
||||
globalMessagesByChatId,
|
||||
foundIds,
|
||||
activeDownloads,
|
||||
lastSyncTime,
|
||||
}) => {
|
||||
const {
|
||||
searchMessagesGlobal,
|
||||
@ -59,7 +59,7 @@ const FileResults: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
||||
if (direction === LoadMoreDirection.Backwards) {
|
||||
runThrottled(() => {
|
||||
searchMessagesGlobal({
|
||||
type: CURRENT_TYPE,
|
||||
@ -67,7 +67,7 @@ const FileResults: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps -- `searchQuery` is required to prevent infinite message loading
|
||||
}, [lastSyncTime, searchMessagesGlobal, searchQuery]);
|
||||
}, [searchQuery]);
|
||||
|
||||
const foundMessages = useMemo(() => {
|
||||
if (!foundIds || !globalMessagesByChatId) {
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useCallback, useMemo, useRef,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import { LoadMoreDirection } from '../../../types';
|
||||
import type { StateProps } from './helpers/createMapStateToProps';
|
||||
|
||||
import { SLIDE_TRANSITION_DURATION } from '../../../config';
|
||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||
import type { StateProps } from './helpers/createMapStateToProps';
|
||||
import { createMapStateToProps } from './helpers/createMapStateToProps';
|
||||
import { formatMonthAndYear, toYearMonth } from '../../../util/dateFormat';
|
||||
import { getSenderName } from './helpers/getSenderName';
|
||||
import { throttle } from '../../../util/schedulers';
|
||||
|
||||
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||
@ -38,7 +39,6 @@ const LinkResults: FC<OwnProps & StateProps> = ({
|
||||
usersById,
|
||||
globalMessagesByChatId,
|
||||
foundIds,
|
||||
lastSyncTime,
|
||||
isChatProtected,
|
||||
}) => {
|
||||
const {
|
||||
@ -57,7 +57,7 @@ const LinkResults: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
||||
if (direction === LoadMoreDirection.Backwards) {
|
||||
runThrottled(() => {
|
||||
searchMessagesGlobal({
|
||||
type: CURRENT_TYPE,
|
||||
@ -65,7 +65,7 @@ const LinkResults: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps -- `searchQuery` is required to prevent infinite message loading
|
||||
}, [lastSyncTime, searchMessagesGlobal, searchQuery]);
|
||||
}, [searchQuery]);
|
||||
|
||||
const foundMessages = useMemo(() => {
|
||||
if (!foundIds || !globalMessagesByChatId) {
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useCallback, useMemo, useRef,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { StateProps } from './helpers/createMapStateToProps';
|
||||
import { LoadMoreDirection, MediaViewerOrigin } from '../../../types';
|
||||
|
||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||
import { SLIDE_TRANSITION_DURATION } from '../../../config';
|
||||
import type { StateProps } from './helpers/createMapStateToProps';
|
||||
import { createMapStateToProps } from './helpers/createMapStateToProps';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { throttle } from '../../../util/schedulers';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
||||
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||
@ -36,7 +37,6 @@ const MediaResults: FC<OwnProps & StateProps> = ({
|
||||
isLoading,
|
||||
globalMessagesByChatId,
|
||||
foundIds,
|
||||
lastSyncTime,
|
||||
isChatProtected,
|
||||
}) => {
|
||||
const {
|
||||
@ -55,7 +55,7 @@ const MediaResults: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
||||
if (direction === LoadMoreDirection.Backwards) {
|
||||
runThrottled(() => {
|
||||
searchMessagesGlobal({
|
||||
type: CURRENT_TYPE,
|
||||
@ -63,7 +63,7 @@ const MediaResults: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps -- `searchQuery` is required to prevent infinite message loading
|
||||
}, [lastSyncTime, searchMessagesGlobal, searchQuery]);
|
||||
}, [searchMessagesGlobal, searchQuery]);
|
||||
|
||||
const foundMessages = useMemo(() => {
|
||||
if (!foundIds || !globalMessagesByChatId) {
|
||||
|
||||
@ -13,7 +13,6 @@ export type StateProps = {
|
||||
usersById: Record<string, ApiUser>;
|
||||
globalMessagesByChatId?: Record<string, { byId: Record<number, ApiMessage> }>;
|
||||
foundIds?: string[];
|
||||
lastSyncTime?: number;
|
||||
searchChatId?: string;
|
||||
activeDownloads: TabState['activeDownloads']['byChatId'];
|
||||
isChatProtected?: boolean;
|
||||
@ -49,7 +48,6 @@ export function createMapStateToProps(type: ApiGlobalMessageSearchType) {
|
||||
searchChatId: chatId,
|
||||
activeDownloads,
|
||||
isChatProtected: chatId ? selectChat(global, chatId)?.isProtected : undefined,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@ -26,9 +26,7 @@ type OwnProps = {
|
||||
onScreenSelect: (screen: SettingsScreens) => void;
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
lastSyncTime?: number;
|
||||
} & Pick<ISettings, 'languages' | 'language' | 'canTranslate' | 'doNotTranslate'>;
|
||||
type StateProps = Pick<ISettings, 'languages' | 'language' | 'canTranslate' | 'doNotTranslate'>;
|
||||
|
||||
const SettingsLanguage: FC<OwnProps & StateProps> = ({
|
||||
isActive,
|
||||
@ -36,7 +34,6 @@ const SettingsLanguage: FC<OwnProps & StateProps> = ({
|
||||
language,
|
||||
canTranslate,
|
||||
doNotTranslate,
|
||||
lastSyncTime,
|
||||
onScreenSelect,
|
||||
onReset,
|
||||
}) => {
|
||||
@ -52,10 +49,10 @@ const SettingsLanguage: FC<OwnProps & StateProps> = ({
|
||||
const lang = useLang();
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && !languages?.length) {
|
||||
if (!languages?.length) {
|
||||
loadLanguages();
|
||||
}
|
||||
}, [languages, lastSyncTime, loadLanguages]);
|
||||
}, [languages]);
|
||||
|
||||
const handleChange = useCallback((langCode: string) => {
|
||||
setSelectedLanguage(langCode);
|
||||
@ -161,7 +158,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
} = global.settings.byKey;
|
||||
|
||||
return {
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
languages,
|
||||
language,
|
||||
canTranslate,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useEffect } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import { SettingsScreens } from '../../../types';
|
||||
import type { ApiUser } from '../../../api/types';
|
||||
|
||||
@ -23,7 +23,6 @@ type OwnProps = {
|
||||
type StateProps = {
|
||||
sessionCount: number;
|
||||
currentUser?: ApiUser;
|
||||
lastSyncTime?: number;
|
||||
canBuyPremium?: boolean;
|
||||
};
|
||||
|
||||
@ -33,7 +32,6 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
||||
onReset,
|
||||
currentUser,
|
||||
sessionCount,
|
||||
lastSyncTime,
|
||||
canBuyPremium,
|
||||
}) => {
|
||||
const {
|
||||
@ -46,10 +44,10 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
||||
const profileId = currentUser?.id;
|
||||
|
||||
useEffect(() => {
|
||||
if (profileId && lastSyncTime) {
|
||||
if (profileId) {
|
||||
loadProfilePhotos({ profileId });
|
||||
}
|
||||
}, [lastSyncTime, profileId, loadProfilePhotos]);
|
||||
}, [profileId, loadProfilePhotos]);
|
||||
|
||||
useHistoryBack({
|
||||
isActive,
|
||||
@ -57,10 +55,8 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSyncTime) {
|
||||
loadAuthorizations();
|
||||
}
|
||||
}, [lastSyncTime, loadAuthorizations]);
|
||||
loadAuthorizations();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="settings-content custom-scroll">
|
||||
@ -160,12 +156,11 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
const { currentUserId, lastSyncTime } = global;
|
||||
const { currentUserId } = global;
|
||||
|
||||
return {
|
||||
sessionCount: global.activeSessions.orderedHashes.length,
|
||||
currentUser: currentUserId ? selectUser(global, currentUserId) : undefined,
|
||||
lastSyncTime,
|
||||
canBuyPremium: !selectIsPremiumPurchaseBlocked(global),
|
||||
};
|
||||
},
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
useEffect, memo, useState, useRef, useLayoutEffect,
|
||||
} from '../../lib/teact/teact';
|
||||
@ -6,6 +5,7 @@ import { addExtraClass } from '../../lib/teact/teact-dom';
|
||||
import { requestNextMutation } from '../../lib/fasterdom/fasterdom';
|
||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import type { LangCode } from '../../types';
|
||||
import type {
|
||||
ApiAttachBot,
|
||||
@ -45,7 +45,6 @@ import { Bundles, loadBundle } from '../../util/moduleLoader';
|
||||
import updateIcon from '../../util/updateIcon';
|
||||
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps';
|
||||
import useBackgroundMode from '../../hooks/useBackgroundMode';
|
||||
import useBeforeUnload from '../../hooks/useBeforeUnload';
|
||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||
@ -104,7 +103,6 @@ export interface OwnProps {
|
||||
type StateProps = {
|
||||
isMasterTab?: boolean;
|
||||
chat?: ApiChat;
|
||||
lastSyncTime?: number;
|
||||
isLeftColumnOpen: boolean;
|
||||
isMiddleColumnOpen: boolean;
|
||||
isRightColumnOpen: boolean;
|
||||
@ -148,6 +146,7 @@ type StateProps = {
|
||||
chatlistModal?: TabState['chatlistModal'];
|
||||
noRightColumnAnimation?: boolean;
|
||||
withInterfaceAnimations?: boolean;
|
||||
isSynced?: boolean;
|
||||
};
|
||||
|
||||
const APP_OUTDATED_TIMEOUT_MS = 5 * 60 * 1000; // 5 min
|
||||
@ -158,7 +157,6 @@ const REACTION_PICKER_LOADING_DELAY_MS = 7000; // 7 sec
|
||||
let DEBUG_isLogged = false;
|
||||
|
||||
const Main: FC<OwnProps & StateProps> = ({
|
||||
lastSyncTime,
|
||||
isMobile,
|
||||
isLeftColumnOpen,
|
||||
isMiddleColumnOpen,
|
||||
@ -204,6 +202,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
isMasterTab,
|
||||
chatlistModal,
|
||||
noRightColumnAnimation,
|
||||
isSynced,
|
||||
}) => {
|
||||
const {
|
||||
initMain,
|
||||
@ -299,7 +298,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
|
||||
// Initial API calls
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && isMasterTab) {
|
||||
if (isMasterTab && isSynced) {
|
||||
updateIsOnline(true);
|
||||
loadConfig();
|
||||
loadAppConfig();
|
||||
@ -320,45 +319,40 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
loadRecentReactions();
|
||||
loadFeaturedEmojiStickers();
|
||||
}
|
||||
}, [
|
||||
lastSyncTime, loadAnimatedEmojis, loadEmojiKeywords, loadNotificationExceptions, loadNotificationSettings,
|
||||
loadTopInlineBots, updateIsOnline, loadAvailableReactions, loadAppConfig, loadAttachBots, loadContactList,
|
||||
loadPremiumGifts, checkAppVersion, loadConfig, loadGenericEmojiEffects, loadDefaultTopicIcons, loadTopReactions,
|
||||
loadDefaultStatusIcons, loadRecentReactions, loadRecentEmojiStatuses, isCurrentUserPremium, isMasterTab, initMain,
|
||||
]);
|
||||
}, [isMasterTab, isSynced]);
|
||||
|
||||
// Initial Premium API calls
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && isMasterTab && isCurrentUserPremium) {
|
||||
if (isMasterTab && isCurrentUserPremium) {
|
||||
loadDefaultStatusIcons();
|
||||
loadRecentEmojiStatuses();
|
||||
}
|
||||
}, [isCurrentUserPremium, isMasterTab, lastSyncTime, loadDefaultStatusIcons, loadRecentEmojiStatuses]);
|
||||
}, [isCurrentUserPremium, isMasterTab]);
|
||||
|
||||
// Language-based API calls
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && isMasterTab) {
|
||||
if (isMasterTab) {
|
||||
if (language !== BASE_EMOJI_KEYWORD_LANG) {
|
||||
loadEmojiKeywords({ language: language! });
|
||||
}
|
||||
|
||||
loadCountryList({ langCode: language });
|
||||
}
|
||||
}, [language, lastSyncTime, loadCountryList, loadEmojiKeywords, isMasterTab]);
|
||||
}, [language, isMasterTab]);
|
||||
|
||||
// Re-fetch cached saved emoji for `localDb`
|
||||
useEffectWithPrevDeps(([prevLastSyncTime]) => {
|
||||
if (!prevLastSyncTime && lastSyncTime && isMasterTab) {
|
||||
useEffect(() => {
|
||||
if (isMasterTab) {
|
||||
loadCustomEmojis({
|
||||
ids: Object.keys(getGlobal().customEmojis.byId),
|
||||
ignoreCache: true,
|
||||
});
|
||||
}
|
||||
}, [lastSyncTime, isMasterTab, loadCustomEmojis]);
|
||||
}, [isMasterTab]);
|
||||
|
||||
// Sticker sets
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && isMasterTab) {
|
||||
if (isMasterTab && isSynced) {
|
||||
if (!addedSetIds || !addedCustomEmojiIds) {
|
||||
loadStickerSets();
|
||||
loadFavoriteStickers();
|
||||
@ -368,45 +362,40 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
loadAddedStickers();
|
||||
}
|
||||
}
|
||||
}, [
|
||||
lastSyncTime, addedSetIds, loadStickerSets, loadFavoriteStickers, loadAddedStickers, addedCustomEmojiIds,
|
||||
isMasterTab,
|
||||
]);
|
||||
}, [addedSetIds, addedCustomEmojiIds, isMasterTab, isSynced]);
|
||||
|
||||
// Check version when service chat is ready
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && isServiceChatReady && isMasterTab) {
|
||||
if (isServiceChatReady && isMasterTab) {
|
||||
checkVersionNotification();
|
||||
}
|
||||
}, [lastSyncTime, isServiceChatReady, checkVersionNotification, isMasterTab]);
|
||||
}, [isServiceChatReady, isMasterTab]);
|
||||
|
||||
// Ensure time format
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && !wasTimeFormatSetManually) {
|
||||
if (!wasTimeFormatSetManually) {
|
||||
ensureTimeFormat();
|
||||
}
|
||||
}, [lastSyncTime, wasTimeFormatSetManually, ensureTimeFormat]);
|
||||
}, [wasTimeFormatSetManually]);
|
||||
|
||||
// Parse deep link
|
||||
useEffect(() => {
|
||||
const parsedInitialLocationHash = parseInitialLocationHash();
|
||||
if (lastSyncTime && parsedInitialLocationHash?.tgaddr) {
|
||||
if (parsedInitialLocationHash?.tgaddr) {
|
||||
processDeepLink(decodeURIComponent(parsedInitialLocationHash.tgaddr));
|
||||
}
|
||||
}, [lastSyncTime]);
|
||||
}, []);
|
||||
|
||||
useEffectWithPrevDeps(([prevLastSyncTime]) => {
|
||||
useEffect(() => {
|
||||
const parsedLocationHash = parseLocationHash();
|
||||
if (!parsedLocationHash) return;
|
||||
|
||||
if (!prevLastSyncTime && lastSyncTime) {
|
||||
openChat({
|
||||
id: parsedLocationHash.chatId,
|
||||
threadId: parsedLocationHash.threadId,
|
||||
type: parsedLocationHash.type,
|
||||
});
|
||||
}
|
||||
}, [lastSyncTime, openChat]);
|
||||
openChat({
|
||||
id: parsedLocationHash.chatId,
|
||||
threadId: parsedLocationHash.threadId,
|
||||
type: parsedLocationHash.type,
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Restore Transition slide class after async rendering
|
||||
useLayoutEffect(() => {
|
||||
@ -579,7 +568,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
language, wasTimeFormatSetManually,
|
||||
},
|
||||
},
|
||||
lastSyncTime,
|
||||
} = global;
|
||||
|
||||
const {
|
||||
@ -623,7 +611,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
const deleteFolderDialog = deleteFolderDialogModal ? selectChatFolder(global, deleteFolderDialogModal) : undefined;
|
||||
|
||||
return {
|
||||
lastSyncTime,
|
||||
isLeftColumnOpen: isLeftColumnShown,
|
||||
isMiddleColumnOpen: Boolean(chatId),
|
||||
isRightColumnOpen: selectIsRightColumnShown(global, isMobile),
|
||||
@ -668,6 +655,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
requestedDraft,
|
||||
chatlistModal,
|
||||
noRightColumnAnimation,
|
||||
isSynced: global.isSynced,
|
||||
};
|
||||
},
|
||||
)(Main));
|
||||
|
||||
@ -33,7 +33,6 @@ type UseMediaProps = {
|
||||
message?: ApiMessage;
|
||||
avatarOwner?: ApiChat | ApiUser;
|
||||
origin?: MediaViewerOrigin;
|
||||
lastSyncTime?: number;
|
||||
delay: number | false;
|
||||
};
|
||||
|
||||
@ -88,7 +87,6 @@ export const useMediaProps = ({
|
||||
&& getMessageMediaHash(message, 'pictogram'),
|
||||
undefined,
|
||||
ApiMediaFormat.BlobUrl,
|
||||
undefined,
|
||||
delay,
|
||||
);
|
||||
const previewMediaHash = getMediaHash();
|
||||
@ -96,7 +94,6 @@ export const useMediaProps = ({
|
||||
previewMediaHash,
|
||||
undefined,
|
||||
ApiMediaFormat.BlobUrl,
|
||||
undefined,
|
||||
delay,
|
||||
);
|
||||
const {
|
||||
@ -106,7 +103,6 @@ export const useMediaProps = ({
|
||||
getMediaHash(true),
|
||||
undefined,
|
||||
message && getMessageMediaFormat(message, 'full'),
|
||||
undefined,
|
||||
delay,
|
||||
);
|
||||
|
||||
|
||||
@ -589,8 +589,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const userFullInfo = isPrivate ? selectUserFullInfo(global, chatId) : undefined;
|
||||
const chatFullInfo = !isPrivate ? selectChatFullInfo(global, chatId) : undefined;
|
||||
const canGiftPremium = Boolean(
|
||||
global.lastSyncTime
|
||||
&& userFullInfo?.premiumGifts?.length
|
||||
userFullInfo?.premiumGifts?.length
|
||||
&& !selectIsPremiumPurchaseBlocked(global),
|
||||
);
|
||||
|
||||
|
||||
@ -128,7 +128,6 @@ type StateProps = {
|
||||
botInfo?: ApiBotInfo;
|
||||
threadTopMessageId?: number;
|
||||
hasLinkedChat?: boolean;
|
||||
lastSyncTime?: number;
|
||||
topic?: ApiTopic;
|
||||
noMessageSendingAnimation?: boolean;
|
||||
isServiceNotificationsChat?: boolean;
|
||||
@ -178,7 +177,6 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
botInfo,
|
||||
threadTopMessageId,
|
||||
hasLinkedChat,
|
||||
lastSyncTime,
|
||||
withBottomShift,
|
||||
withDefaultBg,
|
||||
topic,
|
||||
@ -238,10 +236,10 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
}, [firstUnreadId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCurrentUserPremium && isChannelChat && isReady && lastSyncTime) {
|
||||
if (!isCurrentUserPremium && isChannelChat && isReady) {
|
||||
loadSponsoredMessages({ chatId });
|
||||
}
|
||||
}, [isCurrentUserPremium, chatId, isReady, isChannelChat, lastSyncTime, loadSponsoredMessages]);
|
||||
}, [isCurrentUserPremium, chatId, isReady, isChannelChat]);
|
||||
|
||||
// Updated only once when messages are loaded (as we want the unread divider to keep its position)
|
||||
useSyncEffect(() => {
|
||||
@ -738,7 +736,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
botInfo,
|
||||
threadTopMessageId,
|
||||
hasLinkedChat: chatFullInfo ? Boolean(chatFullInfo.linkedChatId) : undefined,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
topic,
|
||||
noMessageSendingAnimation: !selectPerformanceSettingsValue(global, 'messageSendingAnimations'),
|
||||
isServiceNotificationsChat: chatId === SERVICE_NOTIFICATIONS_USER_ID,
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import type { RefObject } from 'react';
|
||||
import React, {
|
||||
useEffect, useState, memo, useMemo,
|
||||
} from '../../lib/teact/teact';
|
||||
@ -93,7 +92,7 @@ import './MiddleColumn.scss';
|
||||
import styles from './MiddleColumn.module.scss';
|
||||
|
||||
interface OwnProps {
|
||||
leftColumnRef: RefObject<HTMLDivElement>;
|
||||
leftColumnRef: React.RefObject<HTMLDivElement>;
|
||||
isMobile?: boolean;
|
||||
}
|
||||
|
||||
@ -138,7 +137,6 @@ type StateProps = {
|
||||
activeEmojiInteractions?: ActiveEmojiInteraction[];
|
||||
shouldJoinToSend?: boolean;
|
||||
shouldSendJoinRequest?: boolean;
|
||||
lastSyncTime?: number;
|
||||
pinnedIds?: number[];
|
||||
topMessageId?: number;
|
||||
};
|
||||
@ -192,7 +190,6 @@ function MiddleColumn({
|
||||
shouldJoinToSend,
|
||||
shouldSendJoinRequest,
|
||||
shouldLoadFullChat,
|
||||
lastSyncTime,
|
||||
pinnedIds,
|
||||
topMessageId,
|
||||
}: OwnProps & StateProps) {
|
||||
@ -320,10 +317,10 @@ function MiddleColumn({
|
||||
}, [chatId, isPrivate, loadUser]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!areChatSettingsLoaded && lastSyncTime) {
|
||||
if (!areChatSettingsLoaded) {
|
||||
loadChatSettings({ chatId: chatId! });
|
||||
}
|
||||
}, [chatId, isPrivate, areChatSettingsLoaded, lastSyncTime, loadChatSettings]);
|
||||
}, [chatId, isPrivate, areChatSettingsLoaded]);
|
||||
|
||||
useEffect(() => {
|
||||
if (chatId && shouldLoadFullChat && isReady) {
|
||||
@ -662,7 +659,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
messageLanguageModal,
|
||||
} = selectTabState(global);
|
||||
const currentMessageList = selectCurrentMessageList(global);
|
||||
const { leftColumnWidth, lastSyncTime } = global;
|
||||
const { leftColumnWidth } = global;
|
||||
|
||||
const state: StateProps = {
|
||||
theme,
|
||||
@ -682,7 +679,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
currentTransitionKey: Math.max(0, messageLists.length - 1),
|
||||
activeEmojiInteractions,
|
||||
leftColumnWidth,
|
||||
lastSyncTime,
|
||||
};
|
||||
|
||||
if (!currentMessageList) {
|
||||
@ -711,7 +707,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const canRestartBot = Boolean(bot && selectIsUserBlocked(global, bot.id));
|
||||
const canStartBot = !canRestartBot && isBotNotStarted;
|
||||
const shouldLoadFullChat = Boolean(
|
||||
chat && isChatGroup(chat) && !selectChatFullInfo(global, chat.id) && lastSyncTime,
|
||||
chat && isChatGroup(chat) && !selectChatFullInfo(global, chat.id),
|
||||
);
|
||||
const replyingToId = selectReplyingToId(global, chatId, threadId);
|
||||
const shouldBlockSendInForum = chat?.isForum
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useEffect, useLayoutEffect, useRef,
|
||||
} from '../../lib/teact/teact';
|
||||
import { requestMutation } from '../../lib/fasterdom/fasterdom';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import type { GlobalState, MessageListType } from '../../global/types';
|
||||
import type { Signal } from '../../util/signals';
|
||||
import type {
|
||||
@ -101,7 +101,6 @@ type StateProps = {
|
||||
messagesCount?: number;
|
||||
isComments?: boolean;
|
||||
isChatWithSelf?: boolean;
|
||||
lastSyncTime?: number;
|
||||
hasButtonInHeader?: boolean;
|
||||
shouldSkipHistoryAnimations?: boolean;
|
||||
currentTransitionKey: number;
|
||||
@ -128,7 +127,6 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
messagesCount,
|
||||
isComments,
|
||||
isChatWithSelf,
|
||||
lastSyncTime,
|
||||
hasButtonInHeader,
|
||||
shouldSkipHistoryAnimations,
|
||||
currentTransitionKey,
|
||||
@ -166,10 +164,10 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
const isForum = chat?.isForum;
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && isReady && (threadId === MAIN_THREAD_ID || isForum)) {
|
||||
if (isReady && (threadId === MAIN_THREAD_ID || isForum)) {
|
||||
loadPinnedMessages({ chatId, threadId });
|
||||
}
|
||||
}, [chatId, loadPinnedMessages, lastSyncTime, threadId, isReady, isForum]);
|
||||
}, [chatId, threadId, isReady, isForum]);
|
||||
|
||||
useEnsureMessage(chatId, pinnedMessageId, pinnedMessage);
|
||||
|
||||
@ -484,7 +482,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
const {
|
||||
isLeftColumnShown, shouldSkipHistoryAnimations, audioPlayer, messageLists,
|
||||
} = selectTabState(global);
|
||||
const { lastSyncTime } = global;
|
||||
const chat = selectChat(global, chatId);
|
||||
|
||||
const { chatId: audioChatId, messageId: audioMessageId } = audioPlayer;
|
||||
@ -523,7 +520,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
chat,
|
||||
messagesCount,
|
||||
isChatWithSelf: selectIsChatWithSelf(global, chatId),
|
||||
lastSyncTime,
|
||||
shouldSkipHistoryAnimations,
|
||||
currentTransitionKey: Math.max(0, messageLists.length - 1),
|
||||
connectionState: global.connectionState,
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useEffect, useLayoutEffect, useMemo, useRef, useState,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { requestMeasure, requestNextMutation } from '../../../lib/fasterdom/fasterdom';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type {
|
||||
TabState, MessageListType, GlobalState, ApiDraft, MessageList,
|
||||
} from '../../../global/types';
|
||||
@ -179,7 +179,6 @@ type StateProps =
|
||||
groupChatMembers?: ApiChatMember[];
|
||||
currentUserId?: string;
|
||||
recentEmojis: string[];
|
||||
lastSyncTime?: number;
|
||||
contentToBeScheduled?: TabState['contentToBeScheduled'];
|
||||
shouldSuggestStickers?: boolean;
|
||||
shouldSuggestCustomEmoji?: boolean;
|
||||
@ -207,8 +206,7 @@ type StateProps =
|
||||
attachmentSettings: GlobalState['attachmentSettings'];
|
||||
slowMode?: ApiChatFullInfo['slowMode'];
|
||||
shouldUpdateStickerSetOrder?: boolean;
|
||||
}
|
||||
& Pick<GlobalState, 'connectionState'>;
|
||||
};
|
||||
|
||||
enum MainButtonState {
|
||||
Send = 'send',
|
||||
@ -251,7 +249,6 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
isForCurrentMessageList,
|
||||
isCurrentUserPremium,
|
||||
canSendVoiceByPrivacy,
|
||||
connectionState,
|
||||
isChatWithBot,
|
||||
isChatWithSelf,
|
||||
isChannel,
|
||||
@ -269,7 +266,6 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
topInlineBotIds,
|
||||
currentUserId,
|
||||
captionLimit,
|
||||
lastSyncTime,
|
||||
contentToBeScheduled,
|
||||
shouldSuggestStickers,
|
||||
shouldSuggestCustomEmoji,
|
||||
@ -349,16 +345,16 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
}, [chatId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (chatId && lastSyncTime && isReady) {
|
||||
if (chatId && isReady) {
|
||||
loadScheduledHistory({ chatId });
|
||||
}
|
||||
}, [isReady, chatId, loadScheduledHistory, lastSyncTime, threadId]);
|
||||
}, [isReady, chatId, loadScheduledHistory, threadId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (chatId && chat && lastSyncTime && !sendAsPeerIds && isReady && isChatSuperGroup(chat)) {
|
||||
if (chatId && chat && !sendAsPeerIds && isReady && isChatSuperGroup(chat)) {
|
||||
loadSendAs({ chatId });
|
||||
}
|
||||
}, [chat, chatId, isReady, lastSyncTime, loadSendAs, sendAsPeerIds]);
|
||||
}, [chat, chatId, isReady, loadSendAs, sendAsPeerIds]);
|
||||
|
||||
const shouldAnimateSendAsButtonRef = useRef(false);
|
||||
useSyncEffect(([prevChatId, prevSendAsPeerIds]) => {
|
||||
@ -509,7 +505,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
help: inlineBotHelp,
|
||||
loadMore: loadMoreForInlineBot,
|
||||
} = useInlineBotTooltip(
|
||||
Boolean(isReady && isForCurrentMessageList && !hasAttachments && lastSyncTime),
|
||||
Boolean(isReady && isForCurrentMessageList && !hasAttachments),
|
||||
chatId,
|
||||
getHtml,
|
||||
inlineBots,
|
||||
@ -564,7 +560,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
insertHtmlAndUpdateCursor(buildCustomEmojiHtml(emoji), inputId);
|
||||
});
|
||||
|
||||
useDraft(draft, chatId, threadId, getHtml, setHtml, editingMessage, lastSyncTime);
|
||||
useDraft(draft, chatId, threadId, getHtml, setHtml, editingMessage);
|
||||
|
||||
const resetComposer = useLastCallback((shouldPreserveInput = false) => {
|
||||
if (!shouldPreserveInput) {
|
||||
@ -742,7 +738,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
isSilent?: boolean;
|
||||
scheduledAt?: number;
|
||||
}) => {
|
||||
if (connectionState !== 'connectionStateReady' || !currentMessageList) {
|
||||
if (!currentMessageList) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -790,7 +786,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
const handleSend = useLastCallback(async (isSilent = false, scheduledAt?: number) => {
|
||||
if (connectionState !== 'connectionStateReady' || !currentMessageList) {
|
||||
if (!currentMessageList) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1008,7 +1004,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
const handleInlineBotSelect = useLastCallback((
|
||||
inlineResult: ApiBotInlineResult | ApiBotInlineMediaResult, isSilent?: boolean, isScheduleRequested?: boolean,
|
||||
) => {
|
||||
if (connectionState !== 'connectionStateReady' || !currentMessageList) {
|
||||
if (!currentMessageList) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1627,7 +1623,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
return {
|
||||
isOnActiveTab: !tabState.isBlurred,
|
||||
editingMessage: selectEditingMessage(global, chatId, threadId, messageListType),
|
||||
connectionState: global.connectionState,
|
||||
replyingToId,
|
||||
draft: selectDraft(global, chatId, threadId),
|
||||
chat,
|
||||
@ -1652,7 +1647,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
groupChatMembers: chatFullInfo?.members,
|
||||
topInlineBotIds: global.topInlineBots?.userIds,
|
||||
currentUserId,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
contentToBeScheduled: tabState.contentToBeScheduled,
|
||||
shouldSuggestStickers,
|
||||
shouldSuggestCustomEmoji,
|
||||
|
||||
@ -68,7 +68,6 @@ export type OwnProps = {
|
||||
type StateProps = {
|
||||
isLeftColumnShown: boolean;
|
||||
isCurrentUserPremium?: boolean;
|
||||
lastSyncTime?: number;
|
||||
isBackgroundTranslucent?: boolean;
|
||||
};
|
||||
|
||||
@ -82,7 +81,6 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
||||
canSendGifs,
|
||||
isLeftColumnShown,
|
||||
isCurrentUserPremium,
|
||||
lastSyncTime,
|
||||
onLoad,
|
||||
onClose,
|
||||
onEmojiSelect,
|
||||
@ -112,6 +110,8 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
||||
const [handleMouseEnter, handleMouseLeave] = useMouseInside(isOpen, onClose, undefined, isMobile);
|
||||
const { shouldRender, transitionClassNames } = useShowTransition(isOpen, onClose, false, false);
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
if (!isActivated && isOpen) {
|
||||
isActivated = true;
|
||||
}
|
||||
@ -127,10 +127,10 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
||||
}, [canSendPlainText]);
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && isCurrentUserPremium) {
|
||||
if (isCurrentUserPremium) {
|
||||
loadPremiumSetStickers();
|
||||
}
|
||||
}, [isCurrentUserPremium, lastSyncTime, loadPremiumSetStickers]);
|
||||
}, [isCurrentUserPremium, loadPremiumSetStickers]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!isMobile || !isOpen || isAttachmentModal) {
|
||||
@ -204,8 +204,6 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
||||
onStickerSelect?.(sticker, isSilent, shouldSchedule, true, canUpdateStickerSetsOrder);
|
||||
});
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
function renderContent(isActive: boolean, isFrom: boolean) {
|
||||
switch (activeTab) {
|
||||
case SymbolMenuTabs.Emoji:
|
||||
@ -350,7 +348,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
return {
|
||||
isLeftColumnShown: selectTabState(global).isLeftColumnShown,
|
||||
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
isBackgroundTranslucent: selectIsContextMenuTranslucent(global),
|
||||
};
|
||||
},
|
||||
|
||||
@ -37,14 +37,13 @@ const useDraft = (
|
||||
getHtml: Signal<string>,
|
||||
setHtml: (html: string) => void,
|
||||
editedMessage: ApiMessage | undefined,
|
||||
lastSyncTime?: number,
|
||||
) => {
|
||||
const { saveDraft, clearDraft, loadCustomEmojis } = getActions();
|
||||
|
||||
const isEditing = Boolean(editedMessage);
|
||||
|
||||
const updateDraft = useLastCallback((prevState: { chatId?: string; threadId?: number } = {}, shouldForce = false) => {
|
||||
if (isEditing || !lastSyncTime) return;
|
||||
if (isEditing) return;
|
||||
|
||||
const html = getHtml();
|
||||
|
||||
|
||||
@ -32,7 +32,6 @@ type OwnProps = {
|
||||
album: IAlbum;
|
||||
observeIntersection: ObserveFn;
|
||||
hasCustomAppendix?: boolean;
|
||||
lastSyncTime?: number;
|
||||
isOwn: boolean;
|
||||
isProtected?: boolean;
|
||||
albumLayout: IAlbumLayout;
|
||||
@ -49,7 +48,6 @@ const Album: FC<OwnProps & StateProps> = ({
|
||||
album,
|
||||
observeIntersection,
|
||||
hasCustomAppendix,
|
||||
lastSyncTime,
|
||||
isOwn,
|
||||
isProtected,
|
||||
albumLayout,
|
||||
@ -107,7 +105,6 @@ const Album: FC<OwnProps & StateProps> = ({
|
||||
canAutoLoad={canAutoLoad}
|
||||
canAutoPlay={canAutoPlay}
|
||||
uploadProgress={uploadProgress}
|
||||
lastSyncTime={lastSyncTime}
|
||||
dimensions={dimensions}
|
||||
isProtected={isProtected}
|
||||
onClick={onMediaClick}
|
||||
|
||||
@ -24,7 +24,6 @@ type OwnProps = {
|
||||
customEmojiId: string;
|
||||
withEffects?: boolean;
|
||||
isOwn?: boolean;
|
||||
lastSyncTime?: number;
|
||||
forceLoadPreview?: boolean;
|
||||
messageId?: number;
|
||||
chatId?: string;
|
||||
|
||||
@ -25,7 +25,6 @@ type OwnProps = {
|
||||
withEffects?: boolean;
|
||||
isOwn?: boolean;
|
||||
observeIntersection?: ObserveFn;
|
||||
lastSyncTime?: number;
|
||||
forceLoadPreview?: boolean;
|
||||
messageId?: number;
|
||||
chatId?: string;
|
||||
@ -43,7 +42,6 @@ const QUALITY = 1;
|
||||
const AnimatedEmoji: FC<OwnProps & StateProps> = ({
|
||||
isOwn,
|
||||
observeIntersection,
|
||||
lastSyncTime,
|
||||
forceLoadPreview,
|
||||
messageId,
|
||||
chatId,
|
||||
@ -67,7 +65,6 @@ const AnimatedEmoji: FC<OwnProps & StateProps> = ({
|
||||
quality={QUALITY}
|
||||
noLoad={!isIntersecting}
|
||||
forcePreview={forceLoadPreview}
|
||||
lastSyncTime={lastSyncTime}
|
||||
play={isIntersecting}
|
||||
forceOnHeavyAnimation
|
||||
ref={ref}
|
||||
|
||||
@ -20,13 +20,11 @@ const DEFAULT_PREVIEW_DIMENSIONS = {
|
||||
type OwnProps = {
|
||||
message: ApiMessage;
|
||||
canAutoLoadMedia?: boolean;
|
||||
lastSyncTime?: number;
|
||||
};
|
||||
|
||||
const Game: FC<OwnProps> = ({
|
||||
message,
|
||||
canAutoLoadMedia,
|
||||
lastSyncTime,
|
||||
}) => {
|
||||
const { clickBotInlineButton } = getActions();
|
||||
const game = message.content.game!;
|
||||
@ -34,8 +32,8 @@ const Game: FC<OwnProps> = ({
|
||||
title, description,
|
||||
} = game;
|
||||
|
||||
const photoHash = Boolean(lastSyncTime) && getGamePreviewPhotoHash(game);
|
||||
const videoHash = Boolean(lastSyncTime) && getGamePreviewVideoHash(game);
|
||||
const photoHash = getGamePreviewPhotoHash(game);
|
||||
const videoHash = getGamePreviewVideoHash(game);
|
||||
const photoBlobUrl = useMedia(photoHash, !canAutoLoadMedia);
|
||||
const videoBlobUrl = useMedia(videoHash, !canAutoLoadMedia);
|
||||
|
||||
|
||||
@ -19,14 +19,14 @@ import styles from './InvoiceMediaPreview.module.scss';
|
||||
|
||||
type OwnProps = {
|
||||
message: ApiMessage;
|
||||
lastSyncTime?: number;
|
||||
isConnected: boolean;
|
||||
};
|
||||
|
||||
const POLLING_INTERVAL = 30000;
|
||||
|
||||
const InvoiceMediaPreview: FC<OwnProps> = ({
|
||||
message,
|
||||
lastSyncTime,
|
||||
isConnected,
|
||||
}) => {
|
||||
const { openInvoice, loadExtendedMedia } = getActions();
|
||||
const lang = useLang();
|
||||
@ -38,7 +38,7 @@ const InvoiceMediaPreview: FC<OwnProps> = ({
|
||||
loadExtendedMedia({ chatId, ids: [id] });
|
||||
});
|
||||
|
||||
useInterval(refreshExtendedMedia, lastSyncTime ? POLLING_INTERVAL : undefined);
|
||||
useInterval(refreshExtendedMedia, isConnected ? POLLING_INTERVAL : undefined);
|
||||
|
||||
const {
|
||||
amount,
|
||||
|
||||
@ -54,7 +54,6 @@ const SVG_PIN = { __html: '<svg version="1.1" class="round-pin" xmlns="http://ww
|
||||
type OwnProps = {
|
||||
message: ApiMessage;
|
||||
peer?: ApiUser | ApiChat;
|
||||
lastSyncTime?: number;
|
||||
isInSelectMode?: boolean;
|
||||
isSelected?: boolean;
|
||||
theme: ISettings['theme'];
|
||||
@ -63,7 +62,6 @@ type OwnProps = {
|
||||
const Location: FC<OwnProps> = ({
|
||||
message,
|
||||
peer,
|
||||
lastSyncTime,
|
||||
isInSelectMode,
|
||||
isSelected,
|
||||
theme,
|
||||
@ -91,7 +89,7 @@ const Location: FC<OwnProps> = ({
|
||||
width, height, zoom, scale,
|
||||
} = DEFAULT_MAP_CONFIG;
|
||||
|
||||
const mediaHash = Boolean(lastSyncTime) && buildStaticMapHash(point, width, height, zoom, scale);
|
||||
const mediaHash = buildStaticMapHash(point, width, height, zoom, scale);
|
||||
const mediaBlobUrl = useMedia(mediaHash);
|
||||
const prevMediaBlobUrl = usePrevious(mediaBlobUrl);
|
||||
const mapBlobUrl = mediaBlobUrl || prevMediaBlobUrl;
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
memo,
|
||||
useEffect,
|
||||
@ -8,6 +7,7 @@ import React, {
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type {
|
||||
ActiveEmojiInteraction, ActiveReaction, ChatTranslatedMessages, MessageListType,
|
||||
} from '../../../global/types';
|
||||
@ -220,7 +220,6 @@ type StateProps = {
|
||||
isChannel?: boolean;
|
||||
isGroup?: boolean;
|
||||
canReply?: boolean;
|
||||
lastSyncTime?: number;
|
||||
highlight?: string;
|
||||
animatedEmoji?: string;
|
||||
animatedCustomEmoji?: string;
|
||||
@ -256,6 +255,7 @@ type StateProps = {
|
||||
requestedTranslationLanguage?: string;
|
||||
withReactionEffects?: boolean;
|
||||
withStickerEffects?: boolean;
|
||||
isConnected: boolean;
|
||||
};
|
||||
|
||||
type MetaPosition =
|
||||
@ -330,7 +330,6 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
isChannel,
|
||||
isGroup,
|
||||
canReply,
|
||||
lastSyncTime,
|
||||
highlight,
|
||||
animatedEmoji,
|
||||
animatedCustomEmoji,
|
||||
@ -364,6 +363,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
requestedTranslationLanguage,
|
||||
withReactionEffects,
|
||||
withStickerEffects,
|
||||
isConnected,
|
||||
onPinnedIntersectionChange,
|
||||
}) => {
|
||||
const {
|
||||
@ -789,7 +789,6 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
user={avatarUser}
|
||||
chat={avatarChat}
|
||||
text={hiddenName}
|
||||
lastSyncTime={lastSyncTime}
|
||||
onClick={(avatarUser || avatarChat) ? handleAvatarClick : undefined}
|
||||
/>
|
||||
);
|
||||
@ -915,7 +914,6 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
observeIntersection={observeIntersectionForLoading}
|
||||
observeIntersectionForPlaying={observeIntersectionForPlaying}
|
||||
shouldLoop={shouldLoopStickers}
|
||||
lastSyncTime={lastSyncTime}
|
||||
shouldPlayEffect={(
|
||||
sticker.hasEffect && ((
|
||||
memoFirstUnreadIdRef.current && messageId >= memoFirstUnreadIdRef.current
|
||||
@ -932,7 +930,6 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
withEffects={withStickerEffects && isUserId(chatId)}
|
||||
isOwn={isOwn}
|
||||
observeIntersection={observeIntersectionForLoading}
|
||||
lastSyncTime={lastSyncTime}
|
||||
forceLoadPreview={isLocal}
|
||||
messageId={messageId}
|
||||
chatId={chatId}
|
||||
@ -945,7 +942,6 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
withEffects={withStickerEffects && isUserId(chatId)}
|
||||
isOwn={isOwn}
|
||||
observeIntersection={observeIntersectionForLoading}
|
||||
lastSyncTime={lastSyncTime}
|
||||
forceLoadPreview={isLocal}
|
||||
messageId={messageId}
|
||||
chatId={chatId}
|
||||
@ -960,7 +956,6 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
isOwn={isOwn}
|
||||
isProtected={isProtected}
|
||||
hasCustomAppendix={hasCustomAppendix}
|
||||
lastSyncTime={lastSyncTime}
|
||||
onMediaClick={handleAlbumMediaClick}
|
||||
/>
|
||||
)}
|
||||
@ -993,7 +988,6 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
message={message}
|
||||
observeIntersection={observeIntersectionForLoading}
|
||||
canAutoLoad={canAutoLoadMedia}
|
||||
lastSyncTime={lastSyncTime}
|
||||
isDownloading={isDownloading}
|
||||
/>
|
||||
)}
|
||||
@ -1007,7 +1001,6 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
canAutoLoad={canAutoLoadMedia}
|
||||
canAutoPlay={canAutoPlayMedia}
|
||||
uploadProgress={uploadProgress}
|
||||
lastSyncTime={lastSyncTime}
|
||||
isDownloading={isDownloading}
|
||||
isProtected={isProtected}
|
||||
asForwarded={asForwarded}
|
||||
@ -1021,7 +1014,6 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
message={message}
|
||||
origin={AudioOrigin.Inline}
|
||||
uploadProgress={uploadProgress}
|
||||
lastSyncTime={lastSyncTime}
|
||||
isSelectable={isInDocumentGroup}
|
||||
isSelected={isSelected}
|
||||
noAvatars={noAvatars}
|
||||
@ -1062,13 +1054,12 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
<Game
|
||||
message={message}
|
||||
canAutoLoadMedia={canAutoLoadMedia}
|
||||
lastSyncTime={lastSyncTime}
|
||||
/>
|
||||
)}
|
||||
{invoice?.extendedMedia && (
|
||||
<InvoiceMediaPreview
|
||||
message={message}
|
||||
lastSyncTime={lastSyncTime}
|
||||
isConnected={isConnected}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -1108,7 +1099,6 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
canAutoLoad={canAutoLoadMedia}
|
||||
canAutoPlay={canAutoPlayMedia}
|
||||
asForwarded={asForwarded}
|
||||
lastSyncTime={lastSyncTime}
|
||||
isDownloading={isDownloading}
|
||||
isProtected={isProtected}
|
||||
theme={theme}
|
||||
@ -1129,7 +1119,6 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
{location && (
|
||||
<Location
|
||||
message={message}
|
||||
lastSyncTime={lastSyncTime}
|
||||
isInSelectMode={isInSelectMode}
|
||||
isSelected={isSelected}
|
||||
theme={theme}
|
||||
@ -1346,7 +1335,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
const {
|
||||
focusedMessage, forwardMessages, activeReactions, activeEmojiInteractions,
|
||||
} = selectTabState(global);
|
||||
const { lastSyncTime } = global;
|
||||
const {
|
||||
message, album, withSenderName, withAvatar, threadId, messageListType, isLastInDocumentGroup, isFirstInGroup,
|
||||
} = ownProps;
|
||||
@ -1431,6 +1419,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
const chatTranslations = selectChatTranslations(global, chatId);
|
||||
const requestedTranslationLanguage = selectRequestedTranslationLanguage(global, chatId, message.id);
|
||||
|
||||
const isConnected = global.connectionState === 'connectionStateReady';
|
||||
|
||||
return {
|
||||
theme: selectTheme(global),
|
||||
chatUsernames,
|
||||
@ -1453,7 +1443,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
isChannel,
|
||||
isGroup,
|
||||
canReply,
|
||||
lastSyncTime,
|
||||
highlight,
|
||||
animatedEmoji,
|
||||
animatedCustomEmoji,
|
||||
@ -1492,6 +1481,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
hasLinkedChat: Boolean(chatFullInfo?.linkedChatId),
|
||||
withReactionEffects: selectPerformanceSettingsValue(global, 'reactionEffects'),
|
||||
withStickerEffects: selectPerformanceSettingsValue(global, 'stickerEffects'),
|
||||
isConnected,
|
||||
...((canShowSender || isLocation) && { sender }),
|
||||
...(isOutgoing && { outgoingStatus: selectOutgoingStatus(global, message, messageListType === 'scheduled') }),
|
||||
...(typeof uploadProgress === 'number' && { uploadProgress }),
|
||||
|
||||
@ -39,7 +39,6 @@ type OwnProps = {
|
||||
message: ApiMessage;
|
||||
observeIntersection: ObserveFn;
|
||||
canAutoLoad?: boolean;
|
||||
lastSyncTime?: number;
|
||||
isDownloading?: boolean;
|
||||
};
|
||||
|
||||
@ -51,7 +50,6 @@ const RoundVideo: FC<OwnProps> = ({
|
||||
message,
|
||||
observeIntersection,
|
||||
canAutoLoad,
|
||||
lastSyncTime,
|
||||
isDownloading,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
@ -66,19 +64,17 @@ const RoundVideo: FC<OwnProps> = ({
|
||||
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
||||
|
||||
const [isLoadAllowed, setIsLoadAllowed] = useState(canAutoLoad);
|
||||
const shouldLoad = Boolean(isLoadAllowed && isIntersecting && lastSyncTime);
|
||||
const shouldLoad = Boolean(isLoadAllowed && isIntersecting);
|
||||
const { mediaData, loadProgress } = useMediaWithLoadProgress(
|
||||
getMessageMediaHash(message, 'inline'),
|
||||
!shouldLoad,
|
||||
getMessageMediaFormat(message, 'inline'),
|
||||
lastSyncTime,
|
||||
);
|
||||
|
||||
const { loadProgress: downloadProgress } = useMediaWithLoadProgress(
|
||||
getMessageMediaHash(message, 'download'),
|
||||
!isDownloading,
|
||||
ApiMediaFormat.BlobUrl,
|
||||
lastSyncTime,
|
||||
);
|
||||
|
||||
const [isPlayerReady, markPlayerReady] = useFlag();
|
||||
|
||||
@ -32,7 +32,6 @@ type OwnProps = {
|
||||
observeIntersection: ObserveFn;
|
||||
observeIntersectionForPlaying: ObserveFn;
|
||||
shouldLoop?: boolean;
|
||||
lastSyncTime?: number;
|
||||
shouldPlayEffect?: boolean;
|
||||
withEffect?: boolean;
|
||||
onPlayEffect?: VoidFunction;
|
||||
@ -40,7 +39,7 @@ type OwnProps = {
|
||||
};
|
||||
|
||||
const Sticker: FC<OwnProps> = ({
|
||||
message, observeIntersection, observeIntersectionForPlaying, shouldLoop, lastSyncTime,
|
||||
message, observeIntersection, observeIntersectionForPlaying, shouldLoop,
|
||||
shouldPlayEffect, withEffect, onPlayEffect, onStopEffect,
|
||||
}) => {
|
||||
const { showNotification, openStickerSet } = getActions();
|
||||
@ -65,7 +64,6 @@ const Sticker: FC<OwnProps> = ({
|
||||
mediaHashEffect,
|
||||
!canLoad || !hasEffect,
|
||||
ApiMediaFormat.BlobUrl,
|
||||
lastSyncTime,
|
||||
);
|
||||
const [isPlayingEffect, startPlayingEffect, stopPlayingEffect] = useFlag();
|
||||
|
||||
@ -137,7 +135,6 @@ const Sticker: FC<OwnProps> = ({
|
||||
noLoad={!canLoad}
|
||||
noPlay={!canPlay}
|
||||
withSharedAnimation
|
||||
cacheBuster={lastSyncTime}
|
||||
/>
|
||||
{hasEffect && withEffect && canLoad && isPlayingEffect && (
|
||||
<AnimatedSticker
|
||||
|
||||
@ -48,7 +48,6 @@ export type OwnProps = {
|
||||
forcedWidth?: number;
|
||||
dimensions?: IMediaDimensions;
|
||||
asForwarded?: boolean;
|
||||
lastSyncTime?: number;
|
||||
isDownloading?: boolean;
|
||||
isProtected?: boolean;
|
||||
onClick?: (id: number) => void;
|
||||
@ -65,7 +64,6 @@ const Video: FC<OwnProps> = ({
|
||||
canAutoPlay,
|
||||
uploadProgress,
|
||||
forcedWidth,
|
||||
lastSyncTime,
|
||||
dimensions,
|
||||
asForwarded,
|
||||
isDownloading,
|
||||
@ -95,13 +93,13 @@ const Video: FC<OwnProps> = ({
|
||||
|
||||
const { isMobile } = useAppLayout();
|
||||
const [isLoadAllowed, setIsLoadAllowed] = useState(canAutoLoad);
|
||||
const shouldLoad = Boolean(isLoadAllowed && isIntersectingForLoading && lastSyncTime);
|
||||
const shouldLoad = Boolean(isLoadAllowed && isIntersectingForLoading);
|
||||
const [isPlayAllowed, setIsPlayAllowed] = useState(Boolean(canAutoPlay && !isSpoilerShown));
|
||||
|
||||
const fullMediaHash = getMessageMediaHash(message, 'inline');
|
||||
const [isFullMediaPreloaded] = useState(Boolean(fullMediaHash && mediaLoader.getFromMemory(fullMediaHash)));
|
||||
const { mediaData, loadProgress } = useMediaWithLoadProgress(
|
||||
fullMediaHash, !shouldLoad, getMessageMediaFormat(message, 'inline'), lastSyncTime,
|
||||
fullMediaHash, !shouldLoad, getMessageMediaFormat(message, 'inline'),
|
||||
);
|
||||
const fullMediaData = localBlobUrl || mediaData;
|
||||
const [isPlayerReady, markPlayerReady] = useFlag();
|
||||
@ -112,8 +110,8 @@ const Video: FC<OwnProps> = ({
|
||||
|
||||
const previewMediaHash = getMessageMediaHash(message, 'preview');
|
||||
const [isPreviewPreloaded] = useState(Boolean(previewMediaHash && mediaLoader.getFromMemory(previewMediaHash)));
|
||||
const canLoadPreview = isIntersectingForLoading && lastSyncTime;
|
||||
const previewBlobUrl = useMedia(previewMediaHash, !canLoadPreview, undefined, lastSyncTime);
|
||||
const canLoadPreview = isIntersectingForLoading;
|
||||
const previewBlobUrl = useMedia(previewMediaHash, !canLoadPreview);
|
||||
const previewClassNames = useMediaTransition((hasThumb || previewBlobUrl) && !isPlayerReady);
|
||||
|
||||
const noThumb = !hasThumb || previewBlobUrl || isPlayerReady;
|
||||
@ -127,7 +125,6 @@ const Video: FC<OwnProps> = ({
|
||||
getMessageMediaHash(message, 'download'),
|
||||
!isDownloading,
|
||||
getMessageMediaFormat(message, 'download'),
|
||||
lastSyncTime,
|
||||
);
|
||||
|
||||
const { isUploading, isTransferring, transferProgress } = getMediaTransferState(
|
||||
|
||||
@ -34,7 +34,6 @@ type OwnProps = {
|
||||
canAutoPlay?: boolean;
|
||||
inPreview?: boolean;
|
||||
asForwarded?: boolean;
|
||||
lastSyncTime?: number;
|
||||
isDownloading?: boolean;
|
||||
isProtected?: boolean;
|
||||
theme: ISettings['theme'];
|
||||
@ -50,7 +49,6 @@ const WebPage: FC<OwnProps> = ({
|
||||
canAutoPlay,
|
||||
inPreview,
|
||||
asForwarded,
|
||||
lastSyncTime,
|
||||
isDownloading = false,
|
||||
isProtected,
|
||||
theme,
|
||||
@ -162,7 +160,6 @@ const WebPage: FC<OwnProps> = ({
|
||||
noAvatars={noAvatars}
|
||||
canAutoLoad={canAutoLoad}
|
||||
canAutoPlay={canAutoPlay}
|
||||
lastSyncTime={lastSyncTime}
|
||||
asForwarded={asForwarded}
|
||||
isDownloading={isDownloading}
|
||||
isProtected={isProtected}
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, { memo } from '../../lib/teact/teact';
|
||||
import { withGlobal } from '../../global';
|
||||
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import type { ApiMessage, ApiChat } from '../../api/types';
|
||||
|
||||
import { selectChat, selectChatMessage, selectTabState } from '../../global/selectors';
|
||||
import { buildCollectionByKey } from '../../util/iteratees';
|
||||
import { getMessagePoll } from '../../global/helpers';
|
||||
import renderText from '../common/helpers/renderText';
|
||||
|
||||
import useLang from '../../hooks/useLang';
|
||||
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||
|
||||
@ -23,7 +25,6 @@ type OwnProps = {
|
||||
type StateProps = {
|
||||
chat?: ApiChat;
|
||||
message?: ApiMessage;
|
||||
lastSyncTime?: number;
|
||||
};
|
||||
|
||||
const PollResults: FC<OwnProps & StateProps> = ({
|
||||
@ -31,9 +32,9 @@ const PollResults: FC<OwnProps & StateProps> = ({
|
||||
isActive,
|
||||
chat,
|
||||
message,
|
||||
lastSyncTime,
|
||||
}) => {
|
||||
const lang = useLang();
|
||||
|
||||
useHistoryBack({
|
||||
isActive,
|
||||
onBack: onClose,
|
||||
@ -54,7 +55,7 @@ const PollResults: FC<OwnProps & StateProps> = ({
|
||||
<div className="PollResults" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
<h3 className="poll-question" dir="auto">{renderText(summary.question, ['emoji', 'br'])}</h3>
|
||||
<div className="poll-results-list custom-scroll">
|
||||
{Boolean(lastSyncTime) && summary.answers.map((answer) => (
|
||||
{summary.answers.map((answer) => (
|
||||
<PollAnswerResults
|
||||
key={`${message.id}-${answer.option}`}
|
||||
chat={chat}
|
||||
@ -64,7 +65,6 @@ const PollResults: FC<OwnProps & StateProps> = ({
|
||||
totalVoters={results.totalVoters!}
|
||||
/>
|
||||
))}
|
||||
{!lastSyncTime && <Loading />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -75,7 +75,6 @@ export default memo(withGlobal(
|
||||
const {
|
||||
pollResults: { chatId, messageId },
|
||||
} = selectTabState(global);
|
||||
const { lastSyncTime } = global;
|
||||
|
||||
if (!chatId || !messageId) {
|
||||
return {};
|
||||
@ -87,7 +86,6 @@ export default memo(withGlobal(
|
||||
return {
|
||||
chat,
|
||||
message,
|
||||
lastSyncTime,
|
||||
};
|
||||
},
|
||||
)(PollResults));
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
useEffect, useMemo, useRef, useState, memo,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import type {
|
||||
ApiMessage,
|
||||
ApiChat,
|
||||
@ -98,7 +98,6 @@ type StateProps = {
|
||||
userStatusesById: Record<string, ApiUserStatus>;
|
||||
isRightColumnShown: boolean;
|
||||
isRestricted?: boolean;
|
||||
lastSyncTime?: number;
|
||||
activeDownloadIds?: number[];
|
||||
isChatProtected?: boolean;
|
||||
};
|
||||
@ -138,7 +137,6 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
chatsById,
|
||||
isRightColumnShown,
|
||||
isRestricted,
|
||||
lastSyncTime,
|
||||
activeDownloadIds,
|
||||
isChatProtected,
|
||||
}) => {
|
||||
@ -190,7 +188,6 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
chatsById,
|
||||
messagesById,
|
||||
foundIds,
|
||||
lastSyncTime,
|
||||
topicId,
|
||||
);
|
||||
const isFirstTab = resultType === 'members' || (!hasMembersTab && resultType === 'media');
|
||||
@ -224,10 +221,8 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
const profileId = resolvedUserId || chatId;
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSyncTime) {
|
||||
loadProfilePhotos({ profileId });
|
||||
}
|
||||
}, [loadProfilePhotos, profileId, lastSyncTime]);
|
||||
loadProfilePhotos({ profileId });
|
||||
}, [profileId]);
|
||||
|
||||
const handleSelectMedia = useLastCallback((mediaId: number) => {
|
||||
openMediaViewer({
|
||||
@ -398,7 +393,6 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
message={messagesById[id]}
|
||||
origin={AudioOrigin.SharedMedia}
|
||||
date={messagesById[id].date}
|
||||
lastSyncTime={lastSyncTime}
|
||||
className="scroll-item"
|
||||
onPlay={handlePlayAudio}
|
||||
onDateClick={handleMessageFocus}
|
||||
@ -415,7 +409,6 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
senderTitle={getSenderName(lang, messagesById[id], chatsById, usersById)}
|
||||
origin={AudioOrigin.SharedMedia}
|
||||
date={messagesById[id].date}
|
||||
lastSyncTime={lastSyncTime}
|
||||
className="scroll-item"
|
||||
onPlay={handlePlayAudio}
|
||||
onDateClick={handleMessageFocus}
|
||||
@ -565,7 +558,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
currentUserId: global.currentUserId,
|
||||
isRightColumnShown: selectIsRightColumnShown(global, isMobile),
|
||||
isRestricted: chat?.isRestricted,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
activeDownloadIds: activeDownloads?.ids,
|
||||
usersById,
|
||||
userStatusesById,
|
||||
|
||||
@ -23,7 +23,6 @@ export default function useProfileViewportIds(
|
||||
chatsById?: Record<string, ApiChat>,
|
||||
chatMessages?: Record<number, ApiMessage>,
|
||||
foundIds?: number[],
|
||||
lastSyncTime?: number,
|
||||
topicId?: number,
|
||||
) {
|
||||
const resultType = tabType === 'members' || !mediaSearchType ? tabType : mediaSearchType;
|
||||
@ -49,31 +48,31 @@ export default function useProfileViewportIds(
|
||||
}, [chatsById, commonChatIds]);
|
||||
|
||||
const [memberViewportIds, getMoreMembers, noProfileInfoForMembers] = useInfiniteScrollForLoadableItems(
|
||||
resultType, loadMoreMembers, lastSyncTime, memberIds,
|
||||
loadMoreMembers, memberIds,
|
||||
);
|
||||
|
||||
const [mediaViewportIds, getMoreMedia, noProfileInfoForMedia] = useInfiniteScrollForSharedMedia(
|
||||
'media', resultType, searchMessages, lastSyncTime, chatMessages, foundIds, topicId,
|
||||
'media', resultType, searchMessages, chatMessages, foundIds, topicId,
|
||||
);
|
||||
|
||||
const [documentViewportIds, getMoreDocuments, noProfileInfoForDocuments] = useInfiniteScrollForSharedMedia(
|
||||
'documents', resultType, searchMessages, lastSyncTime, chatMessages, foundIds, topicId,
|
||||
'documents', resultType, searchMessages, chatMessages, foundIds, topicId,
|
||||
);
|
||||
|
||||
const [linkViewportIds, getMoreLinks, noProfileInfoForLinks] = useInfiniteScrollForSharedMedia(
|
||||
'links', resultType, searchMessages, lastSyncTime, chatMessages, foundIds, topicId,
|
||||
'links', resultType, searchMessages, chatMessages, foundIds, topicId,
|
||||
);
|
||||
|
||||
const [audioViewportIds, getMoreAudio, noProfileInfoForAudio] = useInfiniteScrollForSharedMedia(
|
||||
'audio', resultType, searchMessages, lastSyncTime, chatMessages, foundIds, topicId,
|
||||
'audio', resultType, searchMessages, chatMessages, foundIds, topicId,
|
||||
);
|
||||
|
||||
const [voiceViewportIds, getMoreVoices, noProfileInfoForVoices] = useInfiniteScrollForSharedMedia(
|
||||
'voice', resultType, searchMessages, lastSyncTime, chatMessages, foundIds, topicId,
|
||||
'voice', resultType, searchMessages, chatMessages, foundIds, topicId,
|
||||
);
|
||||
|
||||
const [commonChatViewportIds, getMoreCommonChats, noProfileInfoForCommonChats] = useInfiniteScrollForLoadableItems(
|
||||
resultType, loadCommonChats, lastSyncTime, chatIds,
|
||||
loadCommonChats, chatIds,
|
||||
);
|
||||
|
||||
let viewportIds: number[] | string[] | undefined;
|
||||
@ -122,13 +121,11 @@ export default function useProfileViewportIds(
|
||||
}
|
||||
|
||||
function useInfiniteScrollForLoadableItems(
|
||||
currentResultType?: ProfileTabType,
|
||||
handleLoadMore?: AnyToVoidFunction,
|
||||
lastSyncTime?: number,
|
||||
itemIds?: string[],
|
||||
) {
|
||||
const [viewportIds, getMore] = useInfiniteScroll(
|
||||
lastSyncTime ? handleLoadMore : undefined,
|
||||
handleLoadMore,
|
||||
itemIds,
|
||||
undefined,
|
||||
MEMBERS_SLICE,
|
||||
@ -143,7 +140,6 @@ function useInfiniteScrollForSharedMedia(
|
||||
forSharedMediaType: SharedMediaType,
|
||||
currentResultType?: ProfileTabType,
|
||||
handleLoadMore?: AnyToVoidFunction,
|
||||
lastSyncTime?: number,
|
||||
chatMessages?: Record<number, ApiMessage>,
|
||||
foundIds?: number[],
|
||||
topicId?: number,
|
||||
@ -165,7 +161,7 @@ function useInfiniteScrollForSharedMedia(
|
||||
}, [chatMessages, foundIds, currentResultType, forSharedMediaType]);
|
||||
|
||||
const [viewportIds, getMore] = useInfiniteScroll(
|
||||
lastSyncTime ? handleLoadMore : undefined,
|
||||
handleLoadMore,
|
||||
messageIdsRef.current,
|
||||
undefined,
|
||||
forSharedMediaType === 'media' ? SHARED_MEDIA_SLICE : MESSAGE_SEARCH_SLICE,
|
||||
|
||||
@ -45,7 +45,6 @@ type StateProps = {
|
||||
canChangeInfo?: boolean;
|
||||
canInvite?: boolean;
|
||||
exportedInvites?: ApiExportedInvite[];
|
||||
lastSyncTime?: number;
|
||||
availableReactions?: ApiAvailableReaction[];
|
||||
};
|
||||
|
||||
@ -61,7 +60,6 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
canChangeInfo,
|
||||
canInvite,
|
||||
exportedInvites,
|
||||
lastSyncTime,
|
||||
isActive,
|
||||
availableReactions,
|
||||
onScreenSelect,
|
||||
@ -98,12 +96,10 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSyncTime) {
|
||||
loadExportedChatInvites({ chatId });
|
||||
loadExportedChatInvites({ chatId, isRevoked: true });
|
||||
loadChatJoinRequests({ chatId });
|
||||
}
|
||||
}, [chatId, loadExportedChatInvites, lastSyncTime, loadChatJoinRequests]);
|
||||
loadExportedChatInvites({ chatId });
|
||||
loadExportedChatInvites({ chatId, isRevoked: true });
|
||||
loadChatJoinRequests({ chatId });
|
||||
}, [chatId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (progress === ManagementProgress.Complete) {
|
||||
@ -378,7 +374,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
isSignaturesShown,
|
||||
canChangeInfo: getHasAdminRight(chat, 'changeInfo'),
|
||||
canInvite: getHasAdminRight(chat, 'inviteUsers'),
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
exportedInvites: invites,
|
||||
availableReactions: global.availableReactions,
|
||||
};
|
||||
|
||||
@ -56,7 +56,6 @@ type StateProps = {
|
||||
canInvite?: boolean;
|
||||
canEditForum?: boolean;
|
||||
exportedInvites?: ApiExportedInvite[];
|
||||
lastSyncTime?: number;
|
||||
isChannelsPremiumLimitReached: boolean;
|
||||
availableReactions?: ApiAvailableReaction[];
|
||||
};
|
||||
@ -98,7 +97,6 @@ const ManageGroup: FC<OwnProps & StateProps> = ({
|
||||
canEditForum,
|
||||
isActive,
|
||||
exportedInvites,
|
||||
lastSyncTime,
|
||||
isChannelsPremiumLimitReached,
|
||||
availableReactions,
|
||||
onScreenSelect,
|
||||
@ -140,12 +138,12 @@ const ManageGroup: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && canInvite) {
|
||||
if (canInvite) {
|
||||
loadExportedChatInvites({ chatId });
|
||||
loadExportedChatInvites({ chatId, isRevoked: true });
|
||||
loadChatJoinRequests({ chatId });
|
||||
}
|
||||
}, [chatId, loadExportedChatInvites, lastSyncTime, canInvite, loadChatJoinRequests]);
|
||||
}, [chatId, canInvite]);
|
||||
|
||||
// Resetting `isForum` switch on flood wait error
|
||||
useEffect(() => {
|
||||
@ -509,7 +507,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
canBanUsers: isBasicGroup ? chat.isCreator : getHasAdminRight(chat, 'banUsers'),
|
||||
canInvite: isBasicGroup ? chat.isCreator : getHasAdminRight(chat, 'inviteUsers'),
|
||||
exportedInvites: invites,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
isChannelsPremiumLimitReached: limitReachedModal?.limit === 'channels',
|
||||
availableReactions: global.availableReactions,
|
||||
canEditForum,
|
||||
|
||||
@ -1,18 +1,3 @@
|
||||
import './ui/initial';
|
||||
import './ui/chats';
|
||||
import './ui/messages';
|
||||
import './ui/globalSearch';
|
||||
import './ui/localSearch';
|
||||
import './ui/stickerSearch';
|
||||
import './ui/users';
|
||||
import './ui/settings';
|
||||
import './ui/misc';
|
||||
import './ui/payments';
|
||||
import './ui/calls';
|
||||
import './ui/mediaViewer';
|
||||
import './ui/passcode';
|
||||
import './ui/reactions';
|
||||
|
||||
import './api/initial';
|
||||
import './api/chats';
|
||||
import './api/messages';
|
||||
@ -30,6 +15,21 @@ import './api/payments';
|
||||
import './api/reactions';
|
||||
import './api/statistics';
|
||||
|
||||
import './ui/initial';
|
||||
import './ui/chats';
|
||||
import './ui/messages';
|
||||
import './ui/globalSearch';
|
||||
import './ui/localSearch';
|
||||
import './ui/stickerSearch';
|
||||
import './ui/users';
|
||||
import './ui/settings';
|
||||
import './ui/misc';
|
||||
import './ui/payments';
|
||||
import './ui/calls';
|
||||
import './ui/mediaViewer';
|
||||
import './ui/passcode';
|
||||
import './ui/reactions';
|
||||
|
||||
import './apiUpdaters/initial';
|
||||
import './apiUpdaters/chats';
|
||||
import './apiUpdaters/messages';
|
||||
|
||||
@ -129,8 +129,34 @@ addActionHandler('preloadTopChatMessages', async (global, actions): Promise<void
|
||||
|
||||
addActionHandler('openChat', (global, actions, payload): ActionReturnType => {
|
||||
const {
|
||||
id, threadId = MAIN_THREAD_ID, noRequestThreadInfoUpdate,
|
||||
id, threadId = MAIN_THREAD_ID, noRequestThreadInfoUpdate, tabId = getCurrentTabId(),
|
||||
} = payload;
|
||||
|
||||
const currentMessageList = selectCurrentMessageList(global, tabId);
|
||||
const currentChatId = currentMessageList?.chatId;
|
||||
const currentThreadId = currentMessageList?.threadId;
|
||||
|
||||
if (currentChatId && (currentChatId !== id || currentThreadId !== threadId)) {
|
||||
const [isChatOpened, isThreadOpened] = Object.values(global.byTabId)
|
||||
.reduce(([accHasChatOpened, accHasThreadOpened], { id: otherTabId }) => {
|
||||
if (otherTabId === tabId || (accHasChatOpened && accHasThreadOpened)) {
|
||||
return [accHasChatOpened, accHasThreadOpened];
|
||||
}
|
||||
|
||||
const otherMessageList = selectCurrentMessageList(global, otherTabId);
|
||||
const isSameChat = otherMessageList?.chatId === currentChatId;
|
||||
const isSameThread = isSameChat && otherMessageList?.threadId === currentThreadId;
|
||||
|
||||
return [accHasChatOpened || isSameChat, accHasThreadOpened || isSameThread];
|
||||
}, [currentChatId === id, false]);
|
||||
|
||||
const shouldAbortChatRequests = !isChatOpened || !isThreadOpened;
|
||||
|
||||
if (shouldAbortChatRequests) {
|
||||
callApi('abortChatRequests', { chatId: currentChatId, threadId: isChatOpened ? currentThreadId : undefined });
|
||||
}
|
||||
}
|
||||
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -573,6 +573,7 @@ addActionHandler('reportMessages', async (global, actions, payload): Promise<voi
|
||||
|
||||
addActionHandler('sendMessageAction', async (global, actions, payload): Promise<void> => {
|
||||
const { action, chatId, threadId } = payload!;
|
||||
if (global.connectionState !== 'connectionStateReady') return;
|
||||
if (chatId === global.currentUserId) return; // Message actions are disabled in Saved Messages
|
||||
|
||||
const chat = selectChat(global, chatId)!;
|
||||
|
||||
@ -84,6 +84,7 @@ addActionHandler('sendEmojiInteraction', (global, actions, payload): ActionRetur
|
||||
const {
|
||||
messageId, chatId, emoji, interactions,
|
||||
} = payload!;
|
||||
if (global.connectionState !== 'connectionStateReady') return;
|
||||
|
||||
const chat = selectChat(global, chatId);
|
||||
|
||||
@ -296,7 +297,9 @@ addActionHandler('sendWatchingEmojiInteraction', (global, actions, payload): Act
|
||||
return undefined;
|
||||
}
|
||||
|
||||
callApi('sendWatchingEmojiInteraction', { chat, emoticon });
|
||||
if (global.connectionState === 'connectionStateReady') {
|
||||
callApi('sendWatchingEmojiInteraction', { chat, emoticon });
|
||||
}
|
||||
|
||||
return updateTabState(global, {
|
||||
activeEmojiInteractions: tabState.activeEmojiInteractions.map((activeEmojiInteraction) => {
|
||||
|
||||
@ -594,6 +594,7 @@ function buildInputPrivacyRules(global: GlobalState, {
|
||||
}
|
||||
|
||||
addActionHandler('updateIsOnline', (global, actions, payload): ActionReturnType => {
|
||||
if (global.connectionState !== 'connectionStateReady') return;
|
||||
callApi('updateIsOnline', payload);
|
||||
});
|
||||
|
||||
|
||||
@ -70,8 +70,8 @@ addActionHandler('sync', (global, actions): ActionReturnType => {
|
||||
global = getGlobal();
|
||||
global = {
|
||||
...global,
|
||||
lastSyncTime: Date.now(),
|
||||
isSyncing: false,
|
||||
isSynced: true,
|
||||
};
|
||||
setGlobal(global);
|
||||
|
||||
@ -244,9 +244,9 @@ function loadTopMessages(chat: ApiChat, threadId: number, lastReadInboxId?: numb
|
||||
let previousGlobal: GlobalState | undefined;
|
||||
// RAF can be unreliable when device goes into sleep mode, so sync logic is handled outside any component
|
||||
addCallback((global: GlobalState) => {
|
||||
const { connectionState, authState } = global;
|
||||
const { connectionState, authState, isSynced } = global;
|
||||
const { isMasterTab } = selectTabState(global);
|
||||
if (!isMasterTab || (previousGlobal?.connectionState === connectionState
|
||||
if (!isMasterTab || isSynced || (previousGlobal?.connectionState === connectionState
|
||||
&& previousGlobal?.authState === authState)) {
|
||||
previousGlobal = global;
|
||||
return;
|
||||
|
||||
@ -62,6 +62,10 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||
actions.initApi();
|
||||
break;
|
||||
|
||||
case 'requestSync':
|
||||
actions.sync();
|
||||
break;
|
||||
|
||||
case 'error': {
|
||||
Object.values(global.byTabId).forEach(({ id: tabId }) => {
|
||||
const paymentShippingError = getShippingError(update.error);
|
||||
|
||||
@ -589,7 +589,7 @@ export type GlobalState = {
|
||||
currentUserId?: string;
|
||||
isSyncing?: boolean;
|
||||
isUpdateAvailable?: boolean;
|
||||
lastSyncTime?: number;
|
||||
isSynced?: boolean;
|
||||
leftColumnWidth?: number;
|
||||
lastIsChatInfoShown?: boolean;
|
||||
initialUnreadNotifications?: number;
|
||||
|
||||
@ -3,8 +3,6 @@ import { addCustomEmojiInputRenderCallback } from '../util/customEmojiManager';
|
||||
|
||||
import { throttle } from '../util/schedulers';
|
||||
|
||||
import useLastSyncTime from './useLastSyncTime';
|
||||
|
||||
let LOAD_QUEUE = new Set<string>();
|
||||
const RENDER_HISTORY = new Set<string>();
|
||||
const THROTTLE = 200;
|
||||
@ -44,7 +42,6 @@ function notifyCustomEmojiRender(emojiId: string) {
|
||||
addCustomEmojiInputRenderCallback(notifyCustomEmojiRender);
|
||||
|
||||
export default function useEnsureCustomEmoji(id?: string) {
|
||||
const lastSyncTime = useLastSyncTime();
|
||||
if (!id) return;
|
||||
notifyCustomEmojiRender(id);
|
||||
|
||||
@ -53,7 +50,5 @@ export default function useEnsureCustomEmoji(id?: string) {
|
||||
}
|
||||
|
||||
LOAD_QUEUE.add(id);
|
||||
if (lastSyncTime) {
|
||||
loadFromQueue();
|
||||
}
|
||||
loadFromQueue();
|
||||
}
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
import { useEffect, useState } from '../lib/teact/teact';
|
||||
import { addCallback } from '../lib/teact/teactn';
|
||||
import { getGlobal } from '../global';
|
||||
|
||||
import type { GlobalState } from '../global/types';
|
||||
|
||||
type LastSyncTimeSetter = (time: number) => void;
|
||||
|
||||
const handlers = new Set<LastSyncTimeSetter>();
|
||||
let prevGlobal: GlobalState | undefined;
|
||||
|
||||
addCallback((global: GlobalState) => {
|
||||
if (global.lastSyncTime && global.lastSyncTime !== prevGlobal?.lastSyncTime) {
|
||||
for (const handler of handlers) {
|
||||
handler(global.lastSyncTime);
|
||||
}
|
||||
}
|
||||
|
||||
prevGlobal = global;
|
||||
});
|
||||
|
||||
export default function useLastSyncTime() {
|
||||
const [lastSyncTime, setLastSyncTime] = useState(getGlobal().lastSyncTime);
|
||||
|
||||
useEffect(() => {
|
||||
handlers.add(setLastSyncTime);
|
||||
|
||||
return () => {
|
||||
handlers.delete(setLastSyncTime);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return lastSyncTime;
|
||||
}
|
||||
@ -9,7 +9,6 @@ const useMedia = (
|
||||
mediaHash: string | false | undefined,
|
||||
noLoad = false,
|
||||
mediaFormat = ApiMediaFormat.BlobUrl,
|
||||
cacheBuster?: number,
|
||||
delay?: number | false,
|
||||
) => {
|
||||
const mediaData = mediaHash ? mediaLoader.getFromMemory(mediaHash) : undefined;
|
||||
@ -28,7 +27,7 @@ const useMedia = (
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [noLoad, mediaHash, mediaData, mediaFormat, cacheBuster, forceUpdate, delay]);
|
||||
}, [noLoad, mediaHash, mediaData, mediaFormat, forceUpdate, delay]);
|
||||
|
||||
return mediaData;
|
||||
};
|
||||
|
||||
@ -17,7 +17,6 @@ export default function useMediaWithLoadProgress(
|
||||
mediaHash: string | undefined,
|
||||
noLoad = false,
|
||||
mediaFormat = ApiMediaFormat.BlobUrl,
|
||||
cacheBuster?: number,
|
||||
delay?: number | false,
|
||||
isHtmlAllowed = false,
|
||||
) {
|
||||
@ -66,8 +65,7 @@ export default function useMediaWithLoadProgress(
|
||||
}
|
||||
}
|
||||
}, [
|
||||
noLoad, mediaHash, mediaData, mediaFormat, cacheBuster, forceUpdate, isStreaming, delay, handleProgress,
|
||||
isHtmlAllowed, id,
|
||||
noLoad, mediaHash, mediaData, mediaFormat, forceUpdate, isStreaming, delay, handleProgress, isHtmlAllowed, id,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -988,16 +988,12 @@ class TelegramClient {
|
||||
for (const x of [...update.users, ...update.chats]) {
|
||||
entities.push(x);
|
||||
}
|
||||
for (const u of update.updates) {
|
||||
this._processUpdate(u, update.updates, entities);
|
||||
}
|
||||
this._processUpdate(update, entities);
|
||||
} else if (update instanceof constructors.UpdateShort) {
|
||||
this._processUpdate(update.update, undefined);
|
||||
} else {
|
||||
this._processUpdate(update, undefined);
|
||||
}
|
||||
// TODO add caching
|
||||
// this._stateCache.update(update)
|
||||
}
|
||||
|
||||
_processUpdate(update, entities) {
|
||||
|
||||
@ -14,29 +14,14 @@ class Logger {
|
||||
_level = level || 'debug';
|
||||
}
|
||||
|
||||
this.isBrowser = typeof process === 'undefined'
|
||||
|| process.type === 'renderer'
|
||||
|| process.browser === true
|
||||
|| process.__nwjs;
|
||||
if (!this.isBrowser) {
|
||||
this.colors = {
|
||||
start: '\x1b[2m',
|
||||
warn: '\x1b[35m',
|
||||
info: '\x1b[33m',
|
||||
debug: '\x1b[36m',
|
||||
error: '\x1b[31m',
|
||||
end: '\x1b[0m',
|
||||
};
|
||||
} else {
|
||||
this.colors = {
|
||||
start: '%c',
|
||||
warn: 'color : #ff00ff',
|
||||
info: 'color : #ffff00',
|
||||
debug: 'color : #00ffff',
|
||||
error: 'color : #ff0000',
|
||||
end: '',
|
||||
};
|
||||
}
|
||||
this.colors = {
|
||||
start: '%c',
|
||||
warn: 'color : #ff00ff',
|
||||
info: 'color : #ffff00',
|
||||
debug: 'color : #00ffff',
|
||||
error: 'color : #ff0000',
|
||||
end: '',
|
||||
};
|
||||
this.messageFormat = '[%t] [%l] - [%m]';
|
||||
}
|
||||
|
||||
@ -97,13 +82,8 @@ class Logger {
|
||||
return;
|
||||
}
|
||||
if (this.canSend(level)) {
|
||||
if (!this.isBrowser) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(color + this.format(message, level) + this.colors.end);
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(this.colors.start + this.format(message, level), color);
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(this.colors.start + this.format(message, level), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -749,7 +749,7 @@ class MTProtoSender {
|
||||
/**
|
||||
* Handles a server acknowledge about our messages. Normally these can be ignored
|
||||
*/
|
||||
_handleAck() { }
|
||||
_handleAck() {}
|
||||
|
||||
/**
|
||||
* Handles future salt results, which don't come inside a
|
||||
|
||||
2
src/lib/video-preview/mp4box.d.ts
vendored
2
src/lib/video-preview/mp4box.d.ts
vendored
@ -88,6 +88,6 @@ declare module 'mp4box' {
|
||||
|
||||
export function createFile(): MP4File;
|
||||
|
||||
export { };
|
||||
export {};
|
||||
|
||||
}
|
||||
|
||||
44
src/util/SortedQueue.ts
Normal file
44
src/util/SortedQueue.ts
Normal file
@ -0,0 +1,44 @@
|
||||
export default class SortedQueue<T> {
|
||||
private queue: T[];
|
||||
|
||||
constructor(private comparator: (a: T, b: T) => number) {
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
add(item: T): void {
|
||||
const index = this.binarySearch(item);
|
||||
this.queue.splice(index, 0, item);
|
||||
}
|
||||
|
||||
pop(): T | undefined {
|
||||
return this.queue.shift();
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return this.queue.length;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
private binarySearch(item: T): number {
|
||||
let left = 0;
|
||||
let right = this.queue.length;
|
||||
|
||||
while (left < right) {
|
||||
const middle = Math.floor((left + right) / 2);
|
||||
const comparison = this.comparator(item, this.queue[middle]);
|
||||
|
||||
if (comparison === 0) {
|
||||
return middle;
|
||||
} else if (comparison > 0) {
|
||||
left = middle + 1;
|
||||
} else {
|
||||
right = middle;
|
||||
}
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user