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