[Refactoring] TeactN: Support async action handlers

This commit is contained in:
Alexander Zinchuk 2022-03-19 21:19:14 +01:00
parent e204fa36a5
commit 4f3c7a1a69
28 changed files with 1603 additions and 1799 deletions

View File

@ -101,7 +101,7 @@ addActionHandler('sendBotCommand', (global, actions, payload) => {
); );
}); });
addActionHandler('restartBot', (global, actions, payload) => { addActionHandler('restartBot', async (global, actions, payload) => {
const { chatId } = payload; const { chatId } = payload;
const { currentUserId } = global; const { currentUserId } = global;
const chat = selectCurrentChat(global); const chat = selectCurrentChat(global);
@ -110,101 +110,92 @@ addActionHandler('restartBot', (global, actions, payload) => {
return; return;
} }
(async () => { const result = await callApi('unblockContact', bot.id, bot.accessHash);
const result = await callApi('unblockContact', bot.id, bot.accessHash); if (!result) {
if (!result) {
return;
}
setGlobal(removeBlockedContact(getGlobal(), bot.id));
void sendBotCommand(chat, currentUserId, '/start', undefined, selectSendAs(global, chatId));
})();
});
addActionHandler('loadTopInlineBots', (global) => {
const { lastRequestedAt } = global.topInlineBots;
if (lastRequestedAt && getServerTime(global.serverTimeOffset) - lastRequestedAt < TOP_PEERS_REQUEST_COOLDOWN) {
return; return;
} }
(async () => { setGlobal(removeBlockedContact(getGlobal(), bot.id));
const result = await callApi('fetchTopInlineBots'); void sendBotCommand(chat, currentUserId, '/start', undefined, selectSendAs(global, chatId));
if (!result) {
return;
}
const { ids, users } = result;
let newGlobal = getGlobal();
newGlobal = addUsers(newGlobal, buildCollectionByKey(users, 'id'));
newGlobal = {
...newGlobal,
topInlineBots: {
...newGlobal.topInlineBots,
userIds: ids,
lastRequestedAt: getServerTime(global.serverTimeOffset),
},
};
setGlobal(newGlobal);
})();
}); });
addActionHandler('queryInlineBot', ((global, actions, payload) => { addActionHandler('loadTopInlineBots', async (global) => {
const { lastRequestedAt } = global.topInlineBots;
if (lastRequestedAt && getServerTime(global.serverTimeOffset) - lastRequestedAt < TOP_PEERS_REQUEST_COOLDOWN) {
return undefined;
}
const result = await callApi('fetchTopInlineBots');
if (!result) {
return undefined;
}
const { ids, users } = result;
global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = {
...global,
topInlineBots: {
...global.topInlineBots,
userIds: ids,
lastRequestedAt: getServerTime(global.serverTimeOffset),
},
};
return global;
});
addActionHandler('queryInlineBot', async (global, actions, payload) => {
const { const {
chatId, username, query, offset, chatId, username, query, offset,
} = payload; } = payload;
(async () => { let inlineBotData = global.inlineBots.byUsername[username];
let inlineBotData = global.inlineBots.byUsername[username]; if (inlineBotData === false) {
return;
}
if (inlineBotData === false) { if (inlineBotData === undefined) {
const { user: inlineBot, chat } = await callApi('fetchInlineBot', { username }) || {};
global = getGlobal();
if (!inlineBot || !chat) {
setGlobal(replaceInlineBotSettings(global, username, false));
return; return;
} }
if (inlineBotData === undefined) { global = addUsers(global, { [inlineBot.id]: inlineBot });
const { user: inlineBot, chat } = await callApi('fetchInlineBot', { username }) || {}; global = addChats(global, { [chat.id]: chat });
global = getGlobal(); inlineBotData = {
if (!inlineBot || !chat) { id: inlineBot.id,
setGlobal(replaceInlineBotSettings(global, username, false)); query: '',
return; offset: '',
} switchPm: undefined,
canLoadMore: true,
results: [],
};
global = addUsers(global, { [inlineBot.id]: inlineBot }); global = replaceInlineBotSettings(global, username, inlineBotData);
global = addChats(global, { [chat.id]: chat }); setGlobal(global);
inlineBotData = { }
id: inlineBot.id,
query: '',
offset: '',
switchPm: undefined,
canLoadMore: true,
results: [],
};
global = replaceInlineBotSettings(global, username, inlineBotData); if (query === inlineBotData.query && !inlineBotData.canLoadMore) {
setGlobal(global); return;
} }
if (query === inlineBotData.query && !inlineBotData.canLoadMore) { void runDebouncedForSearch(() => {
return; searchInlineBot({
} username,
inlineBotData: inlineBotData as InlineBotSettings,
void runDebouncedForSearch(() => { chatId,
searchInlineBot({ query,
username, offset,
inlineBotData: inlineBotData as InlineBotSettings,
chatId,
query,
offset,
});
}); });
})(); });
})); });
addActionHandler('sendInlineBotResult', (global, actions, payload) => { addActionHandler('sendInlineBotResult', (global, actions, payload) => {
const { id, queryId } = payload; const { id, queryId } = payload;
const currentMessageList = selectCurrentMessageList(global); const currentMessageList = selectCurrentMessageList(global);
if (!currentMessageList || !id) { if (!currentMessageList || !id) {
return; return;
} }
@ -246,7 +237,7 @@ addActionHandler('resetInlineBot', (global, actions, payload) => {
setGlobal(replaceInlineBotSettings(global, username, inlineBotData)); setGlobal(replaceInlineBotSettings(global, username, inlineBotData));
}); });
addActionHandler('startBot', (global, actions, payload) => { addActionHandler('startBot', async (global, actions, payload) => {
const { botId, param } = payload; const { botId, param } = payload;
const bot = selectUser(global, botId); const bot = selectUser(global, botId);
@ -254,12 +245,10 @@ addActionHandler('startBot', (global, actions, payload) => {
return; return;
} }
(async () => { await callApi('startBot', {
await callApi('startBot', { bot,
bot, startParam: param,
startParam: param, });
});
})();
}); });
async function searchInlineBot({ async function searchInlineBot({

View File

@ -9,8 +9,6 @@ import {
handleUpdateGroupCallParticipants, handleUpdateGroupCallConnection, handleUpdateGroupCallParticipants, handleUpdateGroupCallConnection,
} from '../../../lib/secret-sauce'; } from '../../../lib/secret-sauce';
import { ApiUpdate } from '../../../api/types';
import { GROUP_CALL_VOLUME_MULTIPLIER } from '../../../config'; import { GROUP_CALL_VOLUME_MULTIPLIER } from '../../../config';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { selectChat, selectCurrentMessageList, selectUser } from '../../selectors'; import { selectChat, selectCurrentMessageList, selectUser } from '../../selectors';
@ -35,7 +33,7 @@ import callFallbackAvatarPath from '../../../assets/call-fallback-avatar.png';
const FALLBACK_INVITE_EXPIRE_SECONDS = 1800; // 30 min const FALLBACK_INVITE_EXPIRE_SECONDS = 1800; // 30 min
addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => { addActionHandler('apiUpdate', (global, actions, update) => {
const { activeGroupCallId } = global.groupCalls; const { activeGroupCallId } = global.groupCalls;
switch (update['@type']) { switch (update['@type']) {
@ -88,7 +86,7 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
return undefined; return undefined;
}); });
addActionHandler('leaveGroupCall', (global, actions, payload) => { addActionHandler('leaveGroupCall', async (global, actions, payload) => {
const { const {
isFromLibrary, shouldDiscard, shouldRemove, rejoin, isFromLibrary, shouldDiscard, shouldRemove, rejoin,
} = payload || {}; } = payload || {};
@ -99,74 +97,70 @@ addActionHandler('leaveGroupCall', (global, actions, payload) => {
setGlobal(updateActiveGroupCall(global, { connectionState: 'disconnected' }, groupCall.participantsCount - 1)); setGlobal(updateActiveGroupCall(global, { connectionState: 'disconnected' }, groupCall.participantsCount - 1));
(async () => { await callApi('leaveGroupCall', {
await callApi('leaveGroupCall', { call: groupCall,
call: groupCall, });
});
let shouldResetFallbackState = false; let shouldResetFallbackState = false;
if (shouldDiscard) { if (shouldDiscard) {
global = getGlobal(); global = getGlobal();
if (global.groupCalls.fallbackChatId === groupCall.chatId) { if (global.groupCalls.fallbackChatId === groupCall.chatId) {
shouldResetFallbackState = true; shouldResetFallbackState = true;
global.groupCalls.fallbackUserIdsToRemove?.forEach((userId) => { global.groupCalls.fallbackUserIdsToRemove?.forEach((userId) => {
actions.deleteChatMember({ chatId: global.groupCalls.fallbackChatId, userId }); actions.deleteChatMember({ chatId: global.groupCalls.fallbackChatId, userId });
});
}
await callApi('discardGroupCall', {
call: groupCall,
}); });
} }
global = getGlobal(); await callApi('discardGroupCall', {
if (shouldRemove) { call: groupCall,
global = removeGroupCall(global, groupCall.id);
}
removeGroupCallAudioElement();
setGlobal({
...global,
groupCalls: {
...global.groupCalls,
isGroupCallPanelHidden: true,
activeGroupCallId: undefined,
...(shouldResetFallbackState && {
fallbackChatId: undefined,
fallbackUserIdsToRemove: undefined,
}),
},
}); });
}
if (!isFromLibrary) { global = getGlobal();
leaveGroupCall(); if (shouldRemove) {
} global = removeGroupCall(global, groupCall.id);
}
if (rejoin) { removeGroupCallAudioElement();
actions.joinGroupCall(rejoin);
} setGlobal({
})(); ...global,
groupCalls: {
...global.groupCalls,
isGroupCallPanelHidden: true,
activeGroupCallId: undefined,
...(shouldResetFallbackState && {
fallbackChatId: undefined,
fallbackUserIdsToRemove: undefined,
}),
},
});
if (!isFromLibrary) {
leaveGroupCall();
}
if (rejoin) {
actions.joinGroupCall(rejoin);
}
}); });
addActionHandler('toggleGroupCallVideo', (global) => { addActionHandler('toggleGroupCallVideo', async (global) => {
const groupCall = selectActiveGroupCall(global); const groupCall = selectActiveGroupCall(global);
const user = selectUser(global, global.currentUserId!); const user = selectUser(global, global.currentUserId!);
if (!user || !groupCall) { if (!user || !groupCall) {
return; return;
} }
(async () => { await toggleStream('video');
await toggleStream('video');
await callApi('editGroupCallParticipant', { await callApi('editGroupCallParticipant', {
call: groupCall, call: groupCall,
videoStopped: !isStreamEnabled('video'), videoStopped: !isStreamEnabled('video'),
participant: user, participant: user,
}); });
})();
}); });
addActionHandler('requestToSpeak', (global, actions, payload) => { addActionHandler('requestToSpeak', (global, actions, payload) => {
@ -202,7 +196,7 @@ addActionHandler('setGroupCallParticipantVolume', (global, actions, payload) =>
}); });
}); });
addActionHandler('toggleGroupCallMute', (global, actions, payload) => { addActionHandler('toggleGroupCallMute', async (global, actions, payload) => {
const { participantId, value } = payload || {}; const { participantId, value } = payload || {};
const groupCall = selectActiveGroupCall(global); const groupCall = selectActiveGroupCall(global);
const user = selectUser(global, participantId || global.currentUserId!); const user = selectUser(global, participantId || global.currentUserId!);
@ -210,58 +204,54 @@ addActionHandler('toggleGroupCallMute', (global, actions, payload) => {
return; return;
} }
(async () => { const muted = value === undefined ? isStreamEnabled('audio', user.id) : value;
const muted = value === undefined ? isStreamEnabled('audio', user.id) : value;
if (!participantId) { if (!participantId) {
await toggleStream('audio'); await toggleStream('audio');
} else { } else {
setVolume(participantId, muted ? 0 : 1); setVolume(participantId, muted ? 0 : 1);
} }
await callApi('editGroupCallParticipant', { await callApi('editGroupCallParticipant', {
call: groupCall, call: groupCall,
muted, muted,
participant: user, participant: user,
}); });
})();
}); });
addActionHandler('toggleGroupCallPresentation', (global, actions, payload) => { addActionHandler('toggleGroupCallPresentation', async (global, actions, payload) => {
const groupCall = selectActiveGroupCall(global); const groupCall = selectActiveGroupCall(global);
const user = selectUser(global, global.currentUserId!); const user = selectUser(global, global.currentUserId!);
if (!user || !groupCall) { if (!user || !groupCall) {
return; return;
} }
(async () => { const value = payload?.value !== undefined ? payload?.value : !isStreamEnabled('presentation');
const value = payload?.value !== undefined ? payload?.value : !isStreamEnabled('presentation'); if (value) {
if (value) { const params = await startSharingScreen();
const params = await startSharingScreen(); if (!params) {
if (!params) { return;
return;
}
await callApi('joinGroupCallPresentation', {
call: groupCall,
params,
});
} else {
await toggleStream('presentation', false);
await callApi('leaveGroupCallPresentation', {
call: groupCall,
});
} }
await callApi('editGroupCallParticipant', { await callApi('joinGroupCallPresentation', {
call: groupCall, call: groupCall,
presentationPaused: !isStreamEnabled('presentation'), params,
participant: user,
}); });
})(); } else {
await toggleStream('presentation', false);
await callApi('leaveGroupCallPresentation', {
call: groupCall,
});
}
await callApi('editGroupCallParticipant', {
call: groupCall,
presentationPaused: !isStreamEnabled('presentation'),
participant: user,
});
}); });
addActionHandler('connectToActiveGroupCall', (global, actions) => { addActionHandler('connectToActiveGroupCall', async (global, actions) => {
const groupCall = selectActiveGroupCall(global); const groupCall = selectActiveGroupCall(global);
if (!groupCall) return; if (!groupCall) return;
@ -283,28 +273,26 @@ addActionHandler('connectToActiveGroupCall', (global, actions) => {
if (!currentUserId) return; if (!currentUserId) return;
(async () => { const params = await joinGroupCall(currentUserId, audioContext, audioElement, actions.apiUpdate);
const params = await joinGroupCall(currentUserId, audioContext, audioElement, actions.apiUpdate);
const result = await callApi('joinGroupCall', { const result = await callApi('joinGroupCall', {
call: groupCall, call: groupCall,
params, params,
inviteHash: groupCall.inviteHash, inviteHash: groupCall.inviteHash,
}); });
if (!result) return; if (!result) return;
actions.loadMoreGroupCallParticipants(); actions.loadMoreGroupCallParticipants();
if (groupCall.chatId) { if (groupCall.chatId) {
const chat = selectChat(getGlobal(), groupCall.chatId); const chat = selectChat(getGlobal(), groupCall.chatId);
if (!chat) return; if (!chat) return;
await loadFullChat(chat); await loadFullChat(chat);
} }
})();
}); });
addActionHandler('inviteToCallFallback', (global, actions, payload) => { addActionHandler('inviteToCallFallback', async (global, actions, payload) => {
const { chatId } = selectCurrentMessageList(global) || {}; const { chatId } = selectCurrentMessageList(global) || {};
if (!chatId) { if (!chatId) {
return; return;
@ -317,67 +305,65 @@ addActionHandler('inviteToCallFallback', (global, actions, payload) => {
const { shouldRemove } = payload; const { shouldRemove } = payload;
(async () => { const fallbackChannelTitle = selectCallFallbackChannelTitle(global);
const fallbackChannelTitle = selectCallFallbackChannelTitle(global);
let fallbackChannel = Object.values(global.chats.byId).find((channel) => { let fallbackChannel = Object.values(global.chats.byId).find((channel) => {
return ( return (
channel.title === fallbackChannelTitle channel.title === fallbackChannelTitle
&& channel.isCreator && channel.isCreator
&& !channel.isRestricted && !channel.isRestricted
); );
});
if (!fallbackChannel) {
fallbackChannel = await callApi('createChannel', {
title: fallbackChannelTitle,
users: [user],
}); });
if (!fallbackChannel) { if (!fallbackChannel) {
fallbackChannel = await callApi('createChannel', {
title: fallbackChannelTitle,
users: [user],
});
if (!fallbackChannel) {
return;
}
const photo = await fetchFile(callFallbackAvatarPath, 'avatar.png');
void callApi('editChatPhoto', {
chatId: fallbackChannel.id,
accessHash: fallbackChannel.accessHash,
photo,
});
} else {
actions.updateChatMemberBannedRights({
chatId: fallbackChannel.id,
userId: chatId,
bannedRights: {},
});
void callApi('addChatMembers', fallbackChannel, [user], true);
}
const inviteLink = await callApi('updatePrivateLink', {
chat: fallbackChannel,
usageLimit: 1,
expireDate: getServerTime(global.serverTimeOffset) + FALLBACK_INVITE_EXPIRE_SECONDS,
});
if (!inviteLink) {
return; return;
} }
if (shouldRemove) { const photo = await fetchFile(callFallbackAvatarPath, 'avatar.png');
global = getGlobal(); void callApi('editChatPhoto', {
const fallbackUserIdsToRemove = global.groupCalls.fallbackUserIdsToRemove || []; chatId: fallbackChannel.id,
setGlobal({ accessHash: fallbackChannel.accessHash,
...global, photo,
groupCalls: { });
...global.groupCalls, } else {
fallbackChatId: fallbackChannel.id, actions.updateChatMemberBannedRights({
fallbackUserIdsToRemove: [...fallbackUserIdsToRemove, chatId], chatId: fallbackChannel.id,
}, userId: chatId,
}); bannedRights: {},
} });
actions.sendMessage({ text: `Join a call: ${inviteLink}` }); void callApi('addChatMembers', fallbackChannel, [user], true);
actions.openChat({ id: fallbackChannel.id }); }
actions.createGroupCall({ chatId: fallbackChannel.id });
actions.closeCallFallbackConfirm(); const inviteLink = await callApi('updatePrivateLink', {
})(); chat: fallbackChannel,
usageLimit: 1,
expireDate: getServerTime(global.serverTimeOffset) + FALLBACK_INVITE_EXPIRE_SECONDS,
});
if (!inviteLink) {
return;
}
if (shouldRemove) {
global = getGlobal();
const fallbackUserIdsToRemove = global.groupCalls.fallbackUserIdsToRemove || [];
setGlobal({
...global,
groupCalls: {
...global.groupCalls,
fallbackChatId: fallbackChannel.id,
fallbackUserIdsToRemove: [...fallbackUserIdsToRemove, chatId],
},
});
}
actions.sendMessage({ text: `Join a call: ${inviteLink}` });
actions.openChat({ id: fallbackChannel.id });
actions.createGroupCall({ chatId: fallbackChannel.id });
actions.closeCallFallbackConfirm();
}); });

View File

@ -48,25 +48,23 @@ const INFINITE_LOOP_MARKER = 100;
const runThrottledForLoadTopChats = throttle((cb) => cb(), 3000, true); const runThrottledForLoadTopChats = throttle((cb) => cb(), 3000, true);
const runDebouncedForLoadFullChat = debounce((cb) => cb(), 500, false, true); const runDebouncedForLoadFullChat = debounce((cb) => cb(), 500, false, true);
addActionHandler('preloadTopChatMessages', (global, actions) => { addActionHandler('preloadTopChatMessages', async (global, actions) => {
(async () => { const preloadedChatIds = new Set<string>();
const preloadedChatIds = new Set<string>();
for (let i = 0; i < TOP_CHAT_MESSAGES_PRELOAD_LIMIT; i++) { for (let i = 0; i < TOP_CHAT_MESSAGES_PRELOAD_LIMIT; i++) {
await pause(TOP_CHAT_MESSAGES_PRELOAD_INTERVAL); await pause(TOP_CHAT_MESSAGES_PRELOAD_INTERVAL);
const { chatId: currentChatId } = selectCurrentMessageList(global) || {}; const { chatId: currentChatId } = selectCurrentMessageList(global) || {};
const folderAllOrderedIds = getOrderedIds(ALL_FOLDER_ID); const folderAllOrderedIds = getOrderedIds(ALL_FOLDER_ID);
const nextChatId = folderAllOrderedIds?.find((id) => id !== currentChatId && !preloadedChatIds.has(id)); const nextChatId = folderAllOrderedIds?.find((id) => id !== currentChatId && !preloadedChatIds.has(id));
if (!nextChatId) { if (!nextChatId) {
return; return;
}
preloadedChatIds.add(nextChatId);
actions.loadViewportMessages({ chatId: nextChatId, threadId: MAIN_THREAD_ID });
} }
})();
preloadedChatIds.add(nextChatId);
actions.loadViewportMessages({ chatId: nextChatId, threadId: MAIN_THREAD_ID });
}
}); });
addActionHandler('openChat', (global, actions, payload) => { addActionHandler('openChat', (global, actions, payload) => {
@ -107,40 +105,36 @@ addActionHandler('openChat', (global, actions, payload) => {
} }
}); });
addActionHandler('openLinkedChat', (global, actions, payload) => { addActionHandler('openLinkedChat', async (global, actions, payload) => {
const { id } = payload!; const { id } = payload!;
const chat = selectChat(global, id); const chat = selectChat(global, id);
if (!chat) { if (!chat) {
return; return;
} }
(async () => { const chatFullInfo = await callApi('fetchFullChat', chat);
const chatFullInfo = await callApi('fetchFullChat', chat);
if (chatFullInfo?.fullInfo?.linkedChatId) { if (chatFullInfo?.fullInfo?.linkedChatId) {
actions.openChat({ id: chatFullInfo.fullInfo.linkedChatId }); actions.openChat({ id: chatFullInfo.fullInfo.linkedChatId });
} }
})();
}); });
addActionHandler('focusMessageInComments', (global, actions, payload) => { addActionHandler('focusMessageInComments', async (global, actions, payload) => {
const { chatId, threadId, messageId } = payload!; const { chatId, threadId, messageId } = payload!;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat) { if (!chat) {
return; return;
} }
(async () => { const result = await callApi('requestThreadInfoUpdate', { chat, threadId });
const result = await callApi('requestThreadInfoUpdate', { chat, threadId }); if (!result) {
if (!result) { return;
return; }
}
actions.focusMessage({ chatId, threadId, messageId }); actions.focusMessage({ chatId, threadId, messageId });
})();
}); });
addActionHandler('openSupportChat', (global, actions) => { addActionHandler('openSupportChat', async (global, actions) => {
const chat = selectSupportChat(global); const chat = selectSupportChat(global);
if (chat) { if (chat) {
actions.openChat({ id: chat.id, shouldReplaceHistory: true }); actions.openChat({ id: chat.id, shouldReplaceHistory: true });
@ -149,12 +143,10 @@ addActionHandler('openSupportChat', (global, actions) => {
actions.openChat({ id: TMP_CHAT_ID, shouldReplaceHistory: true }); actions.openChat({ id: TMP_CHAT_ID, shouldReplaceHistory: true });
(async () => { const result = await callApi('fetchChat', { type: 'support' });
const result = await callApi('fetchChat', { type: 'support' }); if (result) {
if (result) { actions.openChat({ id: result.chatId, shouldReplaceHistory: true });
actions.openChat({ id: result.chatId, shouldReplaceHistory: true }); }
}
})();
}); });
addActionHandler('openTipsChat', (global, actions, payload) => { addActionHandler('openTipsChat', (global, actions, payload) => {
@ -167,47 +159,45 @@ addActionHandler('openTipsChat', (global, actions, payload) => {
actions.openChatByUsername({ username: `${TIPS_USERNAME}${usernamePostfix}` }); actions.openChatByUsername({ username: `${TIPS_USERNAME}${usernamePostfix}` });
}); });
addActionHandler('loadAllChats', (global, actions, payload) => { addActionHandler('loadAllChats', async (global, actions, payload) => {
const listType = payload.listType as 'active' | 'archived'; const listType = payload.listType as 'active' | 'archived';
const { onReplace } = payload; const { onReplace } = payload;
let { shouldReplace } = payload; let { shouldReplace } = payload;
let i = 0; let i = 0;
(async () => { while (shouldReplace || !getGlobal().chats.isFullyLoaded[listType]) {
while (shouldReplace || !getGlobal().chats.isFullyLoaded[listType]) { if (i++ >= INFINITE_LOOP_MARKER) {
if (i++ >= INFINITE_LOOP_MARKER) { if (DEBUG) {
if (DEBUG) { // eslint-disable-next-line no-console
// eslint-disable-next-line no-console console.error('`actions/loadAllChats`: Infinite loop detected');
console.error('`actions/loadAllChats`: Infinite loop detected');
}
return;
} }
global = getGlobal(); return;
if (global.connectionState !== 'connectionStateReady' || global.authState !== 'authorizationStateReady') {
return;
}
const listIds = !shouldReplace && global.chats.listIds[listType];
const oldestChat = listIds
? listIds
/* eslint-disable @typescript-eslint/no-loop-func */
.map((id) => global.chats.byId[id])
.filter((chat) => Boolean(chat?.lastMessage) && !selectIsChatPinned(global, chat.id))
/* eslint-enable @typescript-eslint/no-loop-func */
.sort((chat1, chat2) => (chat1.lastMessage!.date - chat2.lastMessage!.date))[0]
: undefined;
await loadChats(listType, oldestChat?.id, oldestChat?.lastMessage!.date, shouldReplace);
if (shouldReplace) {
onReplace?.();
shouldReplace = false;
}
} }
})();
global = getGlobal();
if (global.connectionState !== 'connectionStateReady' || global.authState !== 'authorizationStateReady') {
return;
}
const listIds = !shouldReplace && global.chats.listIds[listType];
const oldestChat = listIds
? listIds
/* eslint-disable @typescript-eslint/no-loop-func */
.map((id) => global.chats.byId[id])
.filter((chat) => Boolean(chat?.lastMessage) && !selectIsChatPinned(global, chat.id))
/* eslint-enable @typescript-eslint/no-loop-func */
.sort((chat1, chat2) => (chat1.lastMessage!.date - chat2.lastMessage!.date))[0]
: undefined;
await loadChats(listType, oldestChat?.id, oldestChat?.lastMessage!.date, shouldReplace);
if (shouldReplace) {
onReplace?.();
shouldReplace = false;
}
}
}); });
addActionHandler('loadFullChat', (global, actions, payload) => { addActionHandler('loadFullChat', (global, actions, payload) => {
@ -503,37 +493,33 @@ addActionHandler('toggleChatUnread', (global, actions, payload) => {
} }
}); });
addActionHandler('openChatByInvite', (global, actions, payload) => { addActionHandler('openChatByInvite', async (global, actions, payload) => {
const { hash } = payload!; const { hash } = payload!;
(async () => { const result = await callApi('openChatByInvite', hash);
const result = await callApi('openChatByInvite', hash); if (!result) {
if (!result) { return;
return; }
}
actions.openChat({ id: result.chatId }); actions.openChat({ id: result.chatId });
})();
}); });
addActionHandler('openChatByPhoneNumber', (global, actions, payload) => { addActionHandler('openChatByPhoneNumber', async (global, actions, payload) => {
const { phoneNumber } = payload!; const { phoneNumber } = payload!;
(async () => { // Open temporary empty chat to make the click response feel faster
// Open temporary empty chat to make the click response feel faster actions.openChat({ id: TMP_CHAT_ID });
actions.openChat({ id: TMP_CHAT_ID });
const chat = await fetchChatByPhoneNumber(phoneNumber); const chat = await fetchChatByPhoneNumber(phoneNumber);
if (!chat) { if (!chat) {
actions.openPreviousChat(); actions.openPreviousChat();
actions.showNotification({ actions.showNotification({
message: langProvider.getTranslation('lng_username_by_phone_not_found').replace('{phone}', phoneNumber), message: langProvider.getTranslation('lng_username_by_phone_not_found').replace('{phone}', phoneNumber),
}); });
return; return;
} }
actions.openChat({ id: chat.id }); actions.openChat({ id: chat.id });
})();
}); });
addActionHandler('openTelegramLink', (global, actions, payload) => { addActionHandler('openTelegramLink', (global, actions, payload) => {
@ -604,77 +590,71 @@ addActionHandler('openTelegramLink', (global, actions, payload) => {
} }
}); });
addActionHandler('acceptInviteConfirmation', (global, actions, payload) => { addActionHandler('acceptInviteConfirmation', async (global, actions, payload) => {
const { hash } = payload!; const { hash } = payload!;
(async () => { const result = await callApi('importChatInvite', { hash });
const result = await callApi('importChatInvite', { hash }); if (!result) {
if (!result) { return;
return; }
}
actions.openChat({ id: result.id }); actions.openChat({ id: result.id });
})();
}); });
addActionHandler('openChatByUsername', (global, actions, payload) => { addActionHandler('openChatByUsername', async (global, actions, payload) => {
const { const {
username, messageId, commentId, startParam, username, messageId, commentId, startParam,
} = payload!; } = payload!;
(async () => { const chat = selectCurrentChat(global);
const chat = selectCurrentChat(global);
if (!commentId) { if (!commentId) {
if (chat && chat.username === username) { if (chat && chat.username === username) {
actions.focusMessage({ chatId: chat.id, messageId }); actions.focusMessage({ chatId: chat.id, messageId });
return;
}
await openChatByUsername(actions, username, messageId, startParam);
return; return;
} }
await openChatByUsername(actions, username, messageId, startParam);
return;
}
const { chatId, type } = selectCurrentMessageList(global) || {}; const { chatId, type } = selectCurrentMessageList(global) || {};
const usernameChat = selectChatByUsername(global, username); const usernameChat = selectChatByUsername(global, username);
if (chatId && usernameChat && type === 'thread') { if (chatId && usernameChat && type === 'thread') {
const threadInfo = selectThreadInfo(global, chatId, messageId); const threadInfo = selectThreadInfo(global, chatId, messageId);
if (threadInfo && threadInfo.chatId === chatId) { if (threadInfo && threadInfo.chatId === chatId) {
actions.focusMessage({ actions.focusMessage({
chatId: threadInfo.chatId, chatId: threadInfo.chatId,
threadId: threadInfo.threadId, threadId: threadInfo.threadId,
messageId: commentId, messageId: commentId,
}); });
return; return;
}
} }
}
if (!messageId) return; if (!messageId) return;
await openCommentsByUsername(actions, username, messageId, commentId); void openCommentsByUsername(actions, username, messageId, commentId);
})();
}); });
addActionHandler('togglePreHistoryHidden', (global, actions, payload) => { addActionHandler('togglePreHistoryHidden', async (global, actions, payload) => {
const { chatId, isEnabled } = payload!; const { chatId, isEnabled } = payload!;
let chat = selectChat(global, chatId);
let chat = selectChat(global, chatId);
if (!chat) { if (!chat) {
return; return;
} }
(async () => { if (isChatBasicGroup(chat)) {
if (isChatBasicGroup(chat)) { chat = await callApi('migrateChat', chat);
chat = await callApi('migrateChat', chat);
if (!chat) { if (!chat) {
return; return;
}
actions.openChat({ id: chat.id });
} }
void callApi('togglePreHistoryHidden', { chat, isEnabled }); actions.openChat({ id: chat.id });
})(); }
void callApi('togglePreHistoryHidden', { chat, isEnabled });
}); });
addActionHandler('updateChatDefaultBannedRights', (global, actions, payload) => { addActionHandler('updateChatDefaultBannedRights', (global, actions, payload) => {
@ -688,143 +668,136 @@ addActionHandler('updateChatDefaultBannedRights', (global, actions, payload) =>
void callApi('updateChatDefaultBannedRights', { chat, bannedRights }); void callApi('updateChatDefaultBannedRights', { chat, bannedRights });
}); });
addActionHandler('updateChatMemberBannedRights', (global, actions, payload) => { addActionHandler('updateChatMemberBannedRights', async (global, actions, payload) => {
const { chatId, userId, bannedRights } = payload!; const { chatId, userId, bannedRights } = payload!;
let chat = selectChat(global, chatId); let chat = selectChat(global, chatId);
const user = selectUser(global, userId); const user = selectUser(global, userId);
if (!chat || !user) { if (!chat || !user) {
return; return undefined;
} }
(async () => { if (isChatBasicGroup(chat)) {
if (isChatBasicGroup(chat)) { chat = await callApi('migrateChat', chat);
chat = await callApi('migrateChat', chat);
if (!chat) { if (!chat) {
return; return undefined;
}
actions.openChat({ id: chat.id });
} }
await callApi('updateChatMemberBannedRights', { chat, user, bannedRights }); actions.openChat({ id: chat.id });
}
const newGlobal = getGlobal(); await callApi('updateChatMemberBannedRights', { chat, user, bannedRights });
const chatAfterUpdate = selectChat(newGlobal, chatId);
if (!chatAfterUpdate || !chatAfterUpdate.fullInfo) { global = getGlobal();
return;
}
const { members, kickedMembers } = chatAfterUpdate.fullInfo; const chatAfterUpdate = selectChat(global, chatId);
const isBanned = Boolean(bannedRights.viewMessages); if (!chatAfterUpdate || !chatAfterUpdate.fullInfo) {
const isUnblocked = !Object.keys(bannedRights).length; return undefined;
}
setGlobal(updateChat(newGlobal, chatId, { const { members, kickedMembers } = chatAfterUpdate.fullInfo;
fullInfo: {
...chatAfterUpdate.fullInfo, const isBanned = Boolean(bannedRights.viewMessages);
...(members && isBanned && { const isUnblocked = !Object.keys(bannedRights).length;
members: members.filter((m) => m.userId !== userId),
}), return updateChat(global, chatId, {
...(members && !isBanned && { fullInfo: {
members: members.map((m) => ( ...chatAfterUpdate.fullInfo,
m.userId === userId ...(members && isBanned && {
? { ...m, bannedRights } members: members.filter((m) => m.userId !== userId),
: m }),
)), ...(members && !isBanned && {
}), members: members.map((m) => (
...(isUnblocked && kickedMembers && { m.userId === userId
kickedMembers: kickedMembers.filter((m) => m.userId !== userId), ? { ...m, bannedRights }
}), : m
}, )),
})); }),
})(); ...(isUnblocked && kickedMembers && {
kickedMembers: kickedMembers.filter((m) => m.userId !== userId),
}),
},
});
}); });
addActionHandler('updateChatAdmin', (global, actions, payload) => { addActionHandler('updateChatAdmin', async (global, actions, payload) => {
const { const {
chatId, userId, adminRights, customTitle, chatId, userId, adminRights, customTitle,
} = payload!; } = payload!;
let chat = selectChat(global, chatId); let chat = selectChat(global, chatId);
const user = selectUser(global, userId); const user = selectUser(global, userId);
if (!chat || !user) { if (!chat || !user) {
return; return undefined;
} }
(async () => { if (isChatBasicGroup(chat)) {
if (isChatBasicGroup(chat)) { chat = await callApi('migrateChat', chat);
chat = await callApi('migrateChat', chat); if (!chat) {
return undefined;
if (!chat) {
return;
}
actions.openChat({ id: chat.id });
} }
await callApi('updateChatAdmin', { actions.openChat({ id: chat.id });
chat, user, adminRights, customTitle, }
});
const chatAfterUpdate = await callApi('fetchFullChat', chat); await callApi('updateChatAdmin', {
const newGlobal = getGlobal(); chat, user, adminRights, customTitle,
});
if (!chatAfterUpdate || !chatAfterUpdate.fullInfo) { const chatAfterUpdate = await callApi('fetchFullChat', chat);
return; if (!chatAfterUpdate?.fullInfo) {
} return undefined;
}
const { adminMembers } = chatAfterUpdate.fullInfo; const { adminMembers } = chatAfterUpdate.fullInfo;
const isDismissed = !Object.keys(adminRights).length;
const isDismissed = !Object.keys(adminRights).length; global = getGlobal();
setGlobal(updateChat(newGlobal, chatId, { return updateChat(global, chatId, {
fullInfo: { fullInfo: {
...chatAfterUpdate.fullInfo, ...chatAfterUpdate.fullInfo,
...(adminMembers && isDismissed && { ...(adminMembers && isDismissed && {
adminMembers: adminMembers.filter((m) => m.userId !== userId), adminMembers: adminMembers.filter((m) => m.userId !== userId),
}), }),
...(adminMembers && !isDismissed && { ...(adminMembers && !isDismissed && {
adminMembers: adminMembers.map((m) => ( adminMembers: adminMembers.map((m) => (
m.userId === userId m.userId === userId
? { ...m, adminRights, customTitle } ? { ...m, adminRights, customTitle }
: m : m
)), )),
}), }),
}, },
})); });
})();
}); });
addActionHandler('updateChat', (global, actions, payload) => { addActionHandler('updateChat', async (global, actions, payload) => {
const { const {
chatId, title, about, photo, chatId, title, about, photo,
} = payload!; } = payload!;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat) { if (!chat) {
return; return undefined;
} }
(async () => { setGlobal(updateManagementProgress(getGlobal(), ManagementProgress.InProgress));
setGlobal(updateManagementProgress(getGlobal(), ManagementProgress.InProgress));
await Promise.all([ await Promise.all([
chat.title !== title chat.title !== title
? callApi('updateChatTitle', chat, title) ? callApi('updateChatTitle', chat, title)
: undefined, : undefined,
chat.fullInfo && chat.fullInfo.about !== about chat.fullInfo && chat.fullInfo.about !== about
? callApi('updateChatAbout', chat, about) ? callApi('updateChatAbout', chat, about)
: undefined, : undefined,
photo photo
? callApi('editChatPhoto', { chatId, accessHash: chat.accessHash, photo }) ? callApi('editChatPhoto', { chatId, accessHash: chat.accessHash, photo })
: undefined, : undefined,
]); ]);
setGlobal(updateManagementProgress(getGlobal(), ManagementProgress.Complete)); return updateManagementProgress(getGlobal(), ManagementProgress.Complete);
})();
}); });
addActionHandler('toggleSignatures', (global, actions, payload) => { addActionHandler('toggleSignatures', (global, actions, payload) => {
@ -838,33 +811,32 @@ addActionHandler('toggleSignatures', (global, actions, payload) => {
void callApi('toggleSignatures', { chat, isEnabled }); void callApi('toggleSignatures', { chat, isEnabled });
}); });
addActionHandler('loadGroupsForDiscussion', () => { addActionHandler('loadGroupsForDiscussion', async (global) => {
(async () => { const groups = await callApi('fetchGroupsForDiscussion');
const groups = await callApi('fetchGroupsForDiscussion'); if (!groups) {
if (!groups) { return undefined;
return; }
const addedById = groups.reduce((result, group) => {
if (group) {
result[group.id] = group;
} }
const addedById = groups.reduce((result, group) => { return result;
if (group) { }, {} as Record<string, ApiChat>);
result[group.id] = group;
}
return result; global = getGlobal();
}, {} as Record<string, ApiChat>); global = addChats(global, addedById);
return {
const global = addChats(getGlobal(), addedById); ...global,
setGlobal({ chats: {
...global, ...global.chats,
chats: { forDiscussionIds: Object.keys(addedById),
...global.chats, },
forDiscussionIds: Object.keys(addedById), };
},
});
})();
}); });
addActionHandler('linkDiscussionGroup', (global, actions, payload) => { addActionHandler('linkDiscussionGroup', async (global, actions, payload) => {
const { channelId, chatId } = payload!; const { channelId, chatId } = payload!;
const channel = selectChat(global, channelId); const channel = selectChat(global, channelId);
@ -873,36 +845,34 @@ addActionHandler('linkDiscussionGroup', (global, actions, payload) => {
return; return;
} }
(async () => { if (isChatBasicGroup(chat)) {
if (isChatBasicGroup(chat)) { chat = await callApi('migrateChat', chat);
chat = await callApi('migrateChat', chat);
if (!chat) { if (!chat) {
return; return;
}
actions.openChat({ id: chat.id });
} }
let { fullInfo } = chat; actions.openChat({ id: chat.id });
if (!fullInfo) { }
const fullChat = await callApi('fetchFullChat', chat);
if (!fullChat) {
return;
}
fullInfo = fullChat.fullInfo; let { fullInfo } = chat;
if (!fullInfo) {
const fullChat = await callApi('fetchFullChat', chat);
if (!fullChat) {
return;
} }
if (fullInfo!.isPreHistoryHidden) { fullInfo = fullChat.fullInfo;
await callApi('togglePreHistoryHidden', { chat, isEnabled: false }); }
}
void callApi('setDiscussionGroup', { channel, chat }); if (fullInfo!.isPreHistoryHidden) {
})(); await callApi('togglePreHistoryHidden', { chat, isEnabled: false });
}
void callApi('setDiscussionGroup', { channel, chat });
}); });
addActionHandler('unlinkDiscussionGroup', (global, actions, payload) => { addActionHandler('unlinkDiscussionGroup', async (global, actions, payload) => {
const { channelId } = payload!; const { channelId } = payload!;
const channel = selectChat(global, channelId); const channel = selectChat(global, channelId);
@ -915,12 +885,10 @@ addActionHandler('unlinkDiscussionGroup', (global, actions, payload) => {
chat = selectChat(global, channel.fullInfo.linkedChatId); chat = selectChat(global, channel.fullInfo.linkedChatId);
} }
(async () => { await callApi('setDiscussionGroup', { channel });
await callApi('setDiscussionGroup', { channel }); if (chat) {
if (chat) { loadFullChat(chat);
loadFullChat(chat); }
}
})();
}); });
addActionHandler('setActiveChatFolder', (global, actions, payload) => { addActionHandler('setActiveChatFolder', (global, actions, payload) => {
@ -933,33 +901,31 @@ addActionHandler('setActiveChatFolder', (global, actions, payload) => {
}; };
}); });
addActionHandler('loadMoreMembers', (global) => { addActionHandler('loadMoreMembers', async (global) => {
(async () => { const { chatId } = selectCurrentMessageList(global) || {};
const { chatId } = selectCurrentMessageList(global) || {}; const chat = chatId ? selectChat(global, chatId) : undefined;
const chat = chatId ? selectChat(global, chatId) : undefined; if (!chat || isChatBasicGroup(chat)) {
if (!chat || isChatBasicGroup(chat)) { return undefined;
return; }
}
const offset = (chat.fullInfo?.members?.length) || undefined; const offset = (chat.fullInfo?.members?.length) || undefined;
const result = await callApi('fetchMembers', chat.id, chat.accessHash!, 'recent', offset); const result = await callApi('fetchMembers', chat.id, chat.accessHash!, 'recent', offset);
if (!result) { if (!result) {
return; return undefined;
} }
const { members, users } = result; const { members, users } = result;
if (!members || !members.length) { if (!members || !members.length) {
return; return undefined;
} }
global = getGlobal(); global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id')); global = addUsers(global, buildCollectionByKey(users, 'id'));
global = addChatMembers(global, chat, members); global = addChatMembers(global, chat, members);
setGlobal(global); return global;
})();
}); });
addActionHandler('addChatMembers', (global, actions, payload) => { addActionHandler('addChatMembers', async (global, actions, payload) => {
const { chatId, memberIds } = payload; const { chatId, memberIds } = payload;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
const users = (memberIds as string[]).map((userId) => selectUser(global, userId)).filter<ApiUser>(Boolean as any); const users = (memberIds as string[]).map((userId) => selectUser(global, userId)).filter<ApiUser>(Boolean as any);
@ -969,14 +935,12 @@ addActionHandler('addChatMembers', (global, actions, payload) => {
} }
actions.setNewChatMembersDialogState(NewChatMembersProgress.Loading); actions.setNewChatMembersDialogState(NewChatMembersProgress.Loading);
(async () => { await callApi('addChatMembers', chat, users);
await callApi('addChatMembers', chat, users); actions.setNewChatMembersDialogState(NewChatMembersProgress.Closed);
actions.setNewChatMembersDialogState(NewChatMembersProgress.Closed); loadFullChat(chat);
loadFullChat(chat);
})();
}); });
addActionHandler('deleteChatMember', (global, actions, payload) => { addActionHandler('deleteChatMember', async (global, actions, payload) => {
const { chatId, userId } = payload; const { chatId, userId } = payload;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
const user = selectUser(global, userId); const user = selectUser(global, userId);
@ -985,10 +949,8 @@ addActionHandler('deleteChatMember', (global, actions, payload) => {
return; return;
} }
(async () => { await callApi('deleteChatMember', chat, user);
await callApi('deleteChatMember', chat, user); loadFullChat(chat);
loadFullChat(chat);
})();
}); });
addActionHandler('toggleIsProtected', (global, actions, payload) => { addActionHandler('toggleIsProtected', (global, actions, payload) => {
@ -1002,20 +964,17 @@ addActionHandler('toggleIsProtected', (global, actions, payload) => {
void callApi('toggleIsProtected', { chat, isProtected }); void callApi('toggleIsProtected', { chat, isProtected });
}); });
addActionHandler('setChatEnabledReactions', (global, actions, payload) => { addActionHandler('setChatEnabledReactions', async (global, actions, payload) => {
const { chatId, enabledReactions } = payload; const { chatId, enabledReactions } = payload;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat) return; if (!chat) return;
(async () => { await callApi('setChatEnabledReactions', {
await callApi('setChatEnabledReactions', { chat,
chat, enabledReactions,
enabledReactions, });
});
await loadFullChat(chat); void loadFullChat(chat);
})();
}); });
async function loadChats( async function loadChats(

View File

@ -33,7 +33,8 @@ addActionHandler('setGlobalSearchQuery', (global, actions, payload) => {
addActionHandler('setGlobalSearchDate', (global, actions, payload) => { addActionHandler('setGlobalSearchDate', (global, actions, payload) => {
const { date } = payload!; const { date } = payload!;
const maxDate = date ? timestampPlusDay(date) : date; const maxDate = date ? timestampPlusDay(date) : date;
const newGlobal = updateGlobalSearch(global, {
global = updateGlobalSearch(global, {
date, date,
query: '', query: '',
resultsByType: { resultsByType: {
@ -45,7 +46,8 @@ addActionHandler('setGlobalSearchDate', (global, actions, payload) => {
}, },
}, },
}); });
setGlobal(newGlobal); setGlobal(global);
const { chatId } = global.globalSearch; const { chatId } = global.globalSearch;
const chat = chatId ? selectChat(global, chatId) : undefined; const chat = chatId ? selectChat(global, chatId) : undefined;
searchMessagesGlobal('', 'text', undefined, chat, maxDate, date); searchMessagesGlobal('', 'text', undefined, chat, maxDate, date);

View File

@ -1,9 +1,6 @@
import { import { addActionHandler, getActions, getGlobal } from '../../index';
addActionHandler, getActions, getGlobal, setGlobal,
} from '../../index';
import { initApi, callApi } from '../../../api/gramjs'; import { initApi, callApi } from '../../../api/gramjs';
import { GlobalState } from '../../types';
import { import {
LANG_CACHE_NAME, LANG_CACHE_NAME,
@ -26,22 +23,20 @@ import {
} from '../../../util/sessions'; } from '../../../util/sessions';
import { forceWebsync } from '../../../util/websync'; import { forceWebsync } from '../../../util/websync';
addActionHandler('initApi', (global: GlobalState, actions) => { addActionHandler('initApi', async (global, actions) => {
(async () => { if (!IS_TEST) {
if (!IS_TEST) { await importLegacySession();
await importLegacySession(); void clearLegacySessions();
void clearLegacySessions(); }
}
void initApi(actions.apiUpdate, { void initApi(actions.apiUpdate, {
userAgent: navigator.userAgent, userAgent: navigator.userAgent,
platform: PLATFORM_ENV, platform: PLATFORM_ENV,
sessionData: loadStoredSession(), sessionData: loadStoredSession(),
isTest: window.location.search.includes('test'), isTest: window.location.search.includes('test'),
isMovSupported: IS_MOV_SUPPORTED, isMovSupported: IS_MOV_SUPPORTED,
isWebmSupported: IS_WEBM_SUPPORTED, isWebmSupported: IS_WEBM_SUPPORTED,
}); });
})();
}); });
addActionHandler('setAuthPhoneNumber', (global, actions, payload) => { addActionHandler('setAuthPhoneNumber', (global, actions, payload) => {
@ -127,18 +122,16 @@ addActionHandler('saveSession', (global, actions, payload) => {
} }
}); });
addActionHandler('signOut', () => { addActionHandler('signOut', async () => {
(async () => { try {
try { await unsubscribe();
await unsubscribe(); await callApi('destroy');
await callApi('destroy'); await forceWebsync(false);
await forceWebsync(false); } catch (err) {
} catch (err) { // Do nothing
// Do nothing }
}
getActions().reset(); getActions().reset();
})();
}); });
addActionHandler('reset', () => { addActionHandler('reset', () => {
@ -163,38 +156,35 @@ addActionHandler('reset', () => {
}); });
addActionHandler('disconnect', () => { addActionHandler('disconnect', () => {
(async () => { void callApi('disconnect');
await callApi('disconnect');
})();
}); });
addActionHandler('loadNearestCountry', (global) => { addActionHandler('loadNearestCountry', async (global) => {
if (global.connectionState !== 'connectionStateReady') { if (global.connectionState !== 'connectionStateReady') {
return; return undefined;
} }
(async () => { const authNearestCountry = await callApi('fetchNearestCountry');
const authNearestCountry = await callApi('fetchNearestCountry');
setGlobal({ return {
...getGlobal(), ...getGlobal(),
authNearestCountry, authNearestCountry,
}); };
})();
}); });
addActionHandler('setDeviceToken', (global, actions, deviceToken) => { addActionHandler('setDeviceToken', (global, actions, deviceToken) => {
setGlobal({ return {
...global, ...global,
push: { push: {
deviceToken, deviceToken,
subscribedAt: Date.now(), subscribedAt: Date.now(),
}, },
}); };
}); });
addActionHandler('deleteDeviceToken', (global) => { addActionHandler('deleteDeviceToken', (global) => {
const newGlobal = { ...global }; return {
delete newGlobal.push; ...global,
setGlobal(newGlobal); push: undefined,
};
}); });

View File

@ -6,65 +6,61 @@ import { updateChat, updateManagement, updateManagementProgress } from '../../re
import { selectChat, selectCurrentMessageList, selectUser } from '../../selectors'; import { selectChat, selectCurrentMessageList, selectUser } from '../../selectors';
import { isChatBasicGroup } from '../../helpers'; import { isChatBasicGroup } from '../../helpers';
addActionHandler('checkPublicLink', (global, actions, payload) => { addActionHandler('checkPublicLink', async (global, actions, payload) => {
const { chatId } = selectCurrentMessageList(global) || {}; const { chatId } = selectCurrentMessageList(global) || {};
if (!chatId) { if (!chatId) {
return; return undefined;
} }
// No need to check the username if already in progress // No need to check the username if already in progress
if (global.management.progress === ManagementProgress.InProgress) { if (global.management.progress === ManagementProgress.InProgress) {
return; return undefined;
} }
const { username } = payload!; const { username } = payload!;
(async () => { global = updateManagementProgress(global, ManagementProgress.InProgress);
global = updateManagementProgress(global, ManagementProgress.InProgress); global = updateManagement(global, chatId, { isUsernameAvailable: undefined });
global = updateManagement(global, chatId, { isUsernameAvailable: undefined }); setGlobal(global);
setGlobal(global);
const isUsernameAvailable = await callApi('checkChatUsername', { username })!; const isUsernameAvailable = await callApi('checkChatUsername', { username })!;
global = getGlobal(); global = getGlobal();
global = updateManagementProgress( global = updateManagementProgress(
global, isUsernameAvailable ? ManagementProgress.Complete : ManagementProgress.Error, global, isUsernameAvailable ? ManagementProgress.Complete : ManagementProgress.Error,
); );
global = updateManagement(global, chatId, { isUsernameAvailable }); global = updateManagement(global, chatId, { isUsernameAvailable });
setGlobal(global); return global;
})();
}); });
addActionHandler('updatePublicLink', (global, actions, payload) => { addActionHandler('updatePublicLink', async (global, actions, payload) => {
const { chatId } = selectCurrentMessageList(global) || {}; const { chatId } = selectCurrentMessageList(global) || {};
let chat = chatId && selectChat(global, chatId); let chat = chatId && selectChat(global, chatId);
if (!chatId || !chat) { if (!chatId || !chat) {
return; return undefined;
} }
const { username } = payload!; const { username } = payload!;
(async () => { global = updateManagementProgress(global, ManagementProgress.InProgress);
global = updateManagementProgress(global, ManagementProgress.InProgress); setGlobal(global);
setGlobal(global);
if (isChatBasicGroup(chat)) { if (isChatBasicGroup(chat)) {
chat = await callApi('migrateChat', chat); chat = await callApi('migrateChat', chat);
if (!chat) { if (!chat) {
return; return undefined;
}
actions.openChat({ id: chat.id });
} }
const result = await callApi('setChatUsername', { chat, username }); actions.openChat({ id: chat.id });
}
global = getGlobal(); const result = await callApi('setChatUsername', { chat, username });
global = updateManagementProgress(global, result ? ManagementProgress.Complete : ManagementProgress.Error);
global = updateManagement(global, chatId, { isUsernameAvailable: undefined }); global = getGlobal();
setGlobal(global); global = updateManagementProgress(global, result ? ManagementProgress.Complete : ManagementProgress.Error);
})(); global = updateManagement(global, chatId, { isUsernameAvailable: undefined });
return global;
}); });
addActionHandler('updatePrivateLink', (global) => { addActionHandler('updatePrivateLink', (global) => {
@ -91,275 +87,273 @@ addActionHandler('setOpenedInviteInfo', (global, actions, payload) => {
setGlobal(updateManagement(global, chatId, update)); setGlobal(updateManagement(global, chatId, update));
}); });
addActionHandler('loadExportedChatInvites', (global, actions, payload) => { addActionHandler('loadExportedChatInvites', async (global, actions, payload) => {
const { const {
chatId, adminId, isRevoked, limit, chatId, adminId, isRevoked, limit,
} = payload!; } = payload!;
const peer = selectChat(global, chatId); const peer = selectChat(global, chatId);
const admin = selectUser(global, adminId || global.currentUserId); const admin = selectUser(global, adminId || global.currentUserId);
if (!peer || !admin) return; if (!peer || !admin) return undefined;
(async () => { const result = await callApi('fetchExportedChatInvites', {
const result = await callApi('fetchExportedChatInvites', { peer, admin, isRevoked, limit,
peer, admin, isRevoked, limit, });
}); if (!result) {
if (!result) { return undefined;
return; }
}
const update = isRevoked ? { revokedInvites: result } : { invites: result };
setGlobal(updateManagement(getGlobal(), chatId, update)); const update = isRevoked ? { revokedInvites: result } : { invites: result };
})();
return updateManagement(getGlobal(), chatId, update);
}); });
addActionHandler('editExportedChatInvite', (global, actions, payload) => { addActionHandler('editExportedChatInvite', async (global, actions, payload) => {
const { const {
chatId, link, isRevoked, expireDate, usageLimit, isRequestNeeded, title, chatId, link, isRevoked, expireDate, usageLimit, isRequestNeeded, title,
} = payload!; } = payload!;
const peer = selectChat(global, chatId); const peer = selectChat(global, chatId);
if (!peer) return; if (!peer) return undefined;
(async () => { const result = await callApi('editExportedChatInvite', {
const result = await callApi('editExportedChatInvite', { peer,
peer, link,
link, isRevoked,
isRevoked, expireDate,
expireDate, usageLimit,
usageLimit, isRequestNeeded,
isRequestNeeded, title,
title, });
}); if (!result) {
if (!result) { return undefined;
return; }
}
global = getGlobal(); const { oldInvite, newInvite } = result;
let invites = global.management.byChatId[chatId].invites || [];
const revokedInvites = global.management.byChatId[chatId].revokedInvites || []; global = getGlobal();
const { oldInvite, newInvite } = result; const invites = (global.management.byChatId[chatId].invites || [])
invites = invites.filter((current) => current.link !== oldInvite.link); .filter((current) => current.link !== oldInvite.link);
if (newInvite.isRevoked) { const revokedInvites = [...(global.management.byChatId[chatId].revokedInvites || [])];
revokedInvites.unshift(newInvite);
} else { if (newInvite.isRevoked) {
invites.push(newInvite); revokedInvites.unshift(newInvite);
} } else {
setGlobal(updateManagement(global, chatId, { invites.push(newInvite);
invites, }
revokedInvites,
})); return updateManagement(global, chatId, {
})(); invites,
revokedInvites,
});
}); });
addActionHandler('exportChatInvite', (global, actions, payload) => { addActionHandler('exportChatInvite', async (global, actions, payload) => {
const { const {
chatId, expireDate, usageLimit, isRequestNeeded, title, chatId, expireDate, usageLimit, isRequestNeeded, title,
} = payload!; } = payload!;
const peer = selectChat(global, chatId); const peer = selectChat(global, chatId);
if (!peer) return; if (!peer) return undefined;
(async () => { const result = await callApi('exportChatInvite', {
const result = await callApi('exportChatInvite', { peer,
peer, expireDate,
expireDate, usageLimit,
usageLimit, isRequestNeeded,
isRequestNeeded, title,
title, });
}); if (!result) {
if (!result) { return undefined;
return; }
}
global = getGlobal(); global = getGlobal();
const invites = global.management.byChatId[chatId].invites || []; const invites = global.management.byChatId[chatId].invites || [];
setGlobal(updateManagement(global, chatId, { return updateManagement(global, chatId, {
invites: [...invites, result], invites: [...invites, result],
})); });
})();
}); });
addActionHandler('deleteExportedChatInvite', (global, actions, payload) => { addActionHandler('deleteExportedChatInvite', async (global, actions, payload) => {
const { const {
chatId, link, chatId, link,
} = payload!; } = payload!;
const peer = selectChat(global, chatId); const peer = selectChat(global, chatId);
if (!peer) return; if (!peer) return undefined;
(async () => { const result = await callApi('deleteExportedChatInvite', {
const result = await callApi('deleteExportedChatInvite', { peer,
peer, link,
link, });
}); if (!result) {
if (!result) { return undefined;
return; }
}
global = getGlobal(); global = getGlobal();
const managementState = global.management.byChatId[chatId]; const managementState = global.management.byChatId[chatId];
setGlobal(updateManagement(global, chatId, { return updateManagement(global, chatId, {
invites: managementState?.invites?.filter((invite) => invite.link !== link), invites: managementState?.invites?.filter((invite) => invite.link !== link),
revokedInvites: managementState?.revokedInvites?.filter((invite) => invite.link !== link), revokedInvites: managementState?.revokedInvites?.filter((invite) => invite.link !== link),
})); });
})();
}); });
addActionHandler('deleteRevokedExportedChatInvites', (global, actions, payload) => { addActionHandler('deleteRevokedExportedChatInvites', async (global, actions, payload) => {
const { const {
chatId, adminId, chatId, adminId,
} = payload!; } = payload!;
const peer = selectChat(global, chatId); const peer = selectChat(global, chatId);
const admin = selectUser(global, adminId || global.currentUserId); const admin = selectUser(global, adminId || global.currentUserId);
if (!peer || !admin) return; if (!peer || !admin) return undefined;
(async () => { const result = await callApi('deleteRevokedExportedChatInvites', {
const result = await callApi('deleteRevokedExportedChatInvites', { peer,
peer, admin,
admin, });
}); if (!result) {
if (!result) { return undefined;
return; }
}
global = getGlobal(); global = getGlobal();
setGlobal(updateManagement(global, chatId, { return updateManagement(global, chatId, {
revokedInvites: [], revokedInvites: [],
})); });
})();
}); });
addActionHandler('loadChatInviteImporters', (global, actions, payload) => { addActionHandler('loadChatInviteImporters', async (global, actions, payload) => {
const { const {
chatId, link, offsetDate, offsetUserId, limit, chatId, link, offsetDate, offsetUserId, limit,
} = payload!; } = payload!;
const peer = selectChat(global, chatId); const peer = selectChat(global, chatId);
const offsetUser = selectUser(global, offsetUserId); const offsetUser = selectUser(global, offsetUserId);
if (!peer || (offsetUserId && !offsetUser)) return; if (!peer || (offsetUserId && !offsetUser)) return undefined;
(async () => { const result = await callApi('fetchChatInviteImporters', {
const result = await callApi('fetchChatInviteImporters', { peer,
peer, link,
link, offsetDate,
offsetDate, offsetUser,
offsetUser, limit,
limit, });
}); if (!result) {
if (!result) { return undefined;
return; }
}
global = getGlobal(); global = getGlobal();
const currentInviteInfo = global.management.byChatId[chatId]?.inviteInfo; const currentInviteInfo = global.management.byChatId[chatId]?.inviteInfo;
if (!currentInviteInfo?.invite || currentInviteInfo.invite.link !== link) return; if (!currentInviteInfo?.invite || currentInviteInfo.invite.link !== link) {
setGlobal(updateManagement(global, chatId, { return undefined;
inviteInfo: { }
...currentInviteInfo,
importers: result, return updateManagement(global, chatId, {
}, inviteInfo: {
})); ...currentInviteInfo,
})(); importers: result,
},
});
}); });
addActionHandler('loadChatInviteRequesters', (global, actions, payload) => { addActionHandler('loadChatInviteRequesters', async (global, actions, payload) => {
const { const {
chatId, link, offsetDate, offsetUserId, limit, chatId, link, offsetDate, offsetUserId, limit,
} = payload!; } = payload!;
const peer = selectChat(global, chatId); const peer = selectChat(global, chatId);
const offsetUser = selectUser(global, offsetUserId); const offsetUser = selectUser(global, offsetUserId);
if (!peer || (offsetUserId && !offsetUser)) return; if (!peer || (offsetUserId && !offsetUser)) return undefined;
(async () => { const result = await callApi('fetchChatInviteImporters', {
const result = await callApi('fetchChatInviteImporters', { peer,
peer, link,
link, offsetDate,
offsetDate, offsetUser,
offsetUser, limit,
limit, isRequested: true,
isRequested: true, });
}); if (!result) {
if (!result) { return undefined;
return; }
}
global = getGlobal(); global = getGlobal();
const currentInviteInfo = global.management.byChatId[chatId]?.inviteInfo; const currentInviteInfo = global.management.byChatId[chatId]?.inviteInfo;
if (!currentInviteInfo?.invite || currentInviteInfo.invite.link !== link) return; if (!currentInviteInfo?.invite || currentInviteInfo.invite.link !== link) {
setGlobal(updateManagement(global, chatId, { return undefined;
inviteInfo: { }
...currentInviteInfo,
requesters: result, return updateManagement(global, chatId, {
}, inviteInfo: {
})); ...currentInviteInfo,
})(); requesters: result,
},
});
}); });
addActionHandler('loadChatJoinRequests', (global, actions, payload) => { addActionHandler('loadChatJoinRequests', async (global, actions, payload) => {
const { const {
chatId, offsetDate, offsetUserId, limit, chatId, offsetDate, offsetUserId, limit,
} = payload!; } = payload!;
const peer = selectChat(global, chatId); const peer = selectChat(global, chatId);
const offsetUser = selectUser(global, offsetUserId); const offsetUser = selectUser(global, offsetUserId);
if (!peer || (offsetUserId && !offsetUser)) return; if (!peer || (offsetUserId && !offsetUser)) return undefined;
(async () => { const result = await callApi('fetchChatInviteImporters', {
const result = await callApi('fetchChatInviteImporters', { peer,
peer, offsetDate,
offsetDate, offsetUser,
offsetUser, limit,
limit, isRequested: true,
isRequested: true, });
}); if (!result) {
if (!result) { return undefined;
return; }
}
global = getGlobal(); global = getGlobal();
setGlobal(updateChat(global, chatId, { joinRequests: result })); return updateChat(global, chatId, { joinRequests: result });
})();
}); });
addActionHandler('hideChatJoinRequest', (global, actions, payload) => { addActionHandler('hideChatJoinRequest', async (global, actions, payload) => {
const { const {
chatId, userId, isApproved, chatId, userId, isApproved,
} = payload!; } = payload!;
const peer = selectChat(global, chatId); const peer = selectChat(global, chatId);
const user = selectUser(global, userId); const user = selectUser(global, userId);
if (!peer || !user) return; if (!peer || !user) return undefined;
(async () => { const result = await callApi('hideChatJoinRequest', {
const result = await callApi('hideChatJoinRequest', { peer,
peer, user,
user, isApproved,
isApproved, });
}); if (!result) return undefined;
if (!result) return; global = getGlobal();
global = getGlobal(); const targetChat = selectChat(global, chatId);
const targetChat = selectChat(global, chatId); if (!targetChat) return undefined;
if (!targetChat) return;
setGlobal(updateChat(global, chatId, { return updateChat(global, chatId, {
joinRequests: targetChat.joinRequests?.filter((importer) => importer.userId !== userId), joinRequests: targetChat.joinRequests?.filter((importer) => importer.userId !== userId),
})); });
})();
}); });
addActionHandler('hideAllChatJoinRequests', (global, actions, payload) => { addActionHandler('hideAllChatJoinRequests', async (global, actions, payload) => {
const { const {
chatId, isApproved, link, chatId, isApproved, link,
} = payload!; } = payload!;
const peer = selectChat(global, chatId); const peer = selectChat(global, chatId);
if (!peer) return; if (!peer) return undefined;
(async () => { const result = await callApi('hideAllChatJoinRequests', {
const result = await callApi('hideAllChatJoinRequests', { peer,
peer, isApproved,
isApproved, link,
link, });
}); if (!result) return undefined;
if (!result) return; global = getGlobal();
global = getGlobal(); const targetChat = selectChat(global, chatId);
const targetChat = selectChat(global, chatId); if (!targetChat) return undefined;
if (!targetChat) return;
setGlobal(updateChat(global, chatId, { return updateChat(global, chatId, {
joinRequests: [], joinRequests: [],
fullInfo: { fullInfo: {
...targetChat.fullInfo, ...targetChat.fullInfo,
recentRequesterIds: [], recentRequesterIds: [],
requestsPending: 0, requestsPending: 0,
}, },
})); });
})();
}); });

View File

@ -157,30 +157,30 @@ async function loadWithBudget(
} }
} }
addActionHandler('loadMessage', (global, actions, payload) => { addActionHandler('loadMessage', async (global, actions, payload) => {
const { const {
chatId, messageId, replyOriginForId, threadUpdate, chatId, messageId, replyOriginForId, threadUpdate,
} = payload!; } = payload!;
const chat = selectChat(global, chatId);
const chat = selectChat(global, chatId);
if (!chat) { if (!chat) {
return; return undefined;
} }
(async () => { const message = await loadMessage(chat, messageId, replyOriginForId);
const message = await loadMessage(chat, messageId, replyOriginForId); if (message && threadUpdate) {
if (message && threadUpdate) { const { lastMessageId, isDeleting } = threadUpdate;
const { lastMessageId, isDeleting } = threadUpdate;
setGlobal(updateThreadUnreadFromForwardedMessage( return updateThreadUnreadFromForwardedMessage(
getGlobal(), getGlobal(),
message, message,
chatId, chatId,
lastMessageId, lastMessageId,
isDeleting, isDeleting,
)); );
} }
})();
return undefined;
}); });
addActionHandler('sendMessage', (global, actions, payload) => { addActionHandler('sendMessage', (global, actions, payload) => {
@ -425,62 +425,56 @@ addActionHandler('deleteScheduledMessages', (global, actions, payload) => {
} }
}); });
addActionHandler('deleteHistory', (global, actions, payload) => { addActionHandler('deleteHistory', async (global, actions, payload) => {
(async () => { const { chatId, shouldDeleteForAll } = payload!;
const { chatId, shouldDeleteForAll } = payload!; const chat = selectChat(global, chatId);
const chat = selectChat(global, chatId); if (!chat) {
if (!chat) { return;
return; }
}
const maxId = chat.lastMessage?.id; const maxId = chat.lastMessage?.id;
await callApi('deleteHistory', { chat, shouldDeleteForAll, maxId }); await callApi('deleteHistory', { chat, shouldDeleteForAll, maxId });
const activeChat = selectCurrentMessageList(global); const activeChat = selectCurrentMessageList(global);
if (activeChat && activeChat.chatId === chatId) { if (activeChat && activeChat.chatId === chatId) {
actions.openChat({ id: undefined }); actions.openChat({ id: undefined });
} }
})();
}); });
addActionHandler('reportMessages', (global, actions, payload) => { addActionHandler('reportMessages', async (global, actions, payload) => {
(async () => { const {
const { messageIds, reason, description,
messageIds, reason, description, } = payload!;
} = payload!; const currentMessageList = selectCurrentMessageList(global);
const currentMessageList = selectCurrentMessageList(global); if (!currentMessageList) {
if (!currentMessageList) { return;
return; }
}
const { chatId } = currentMessageList; const { chatId } = currentMessageList;
const chat = selectChat(global, chatId)!; const chat = selectChat(global, chatId)!;
const result = await callApi('reportMessages', { const result = await callApi('reportMessages', {
peer: chat, messageIds, reason, description, peer: chat, messageIds, reason, description,
}); });
actions.showNotification({ actions.showNotification({
message: result message: result
? 'Thank you! Your report will be reviewed by our team.' ? 'Thank you! Your report will be reviewed by our team.'
: 'Error occured while submiting report. Please, try again later.', : 'Error occured while submiting report. Please, try again later.',
}); });
})();
}); });
addActionHandler('sendMessageAction', (global, actions, payload) => { addActionHandler('sendMessageAction', async (global, actions, payload) => {
(async () => { const { action, chatId, threadId } = payload!;
const { action, chatId, threadId } = payload!; 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)!;
if (!chat) return; if (!chat) return;
await callApi('sendMessageAction', { await callApi('sendMessageAction', {
peer: chat, threadId, action, peer: chat, threadId, action,
}); });
})();
}); });
addActionHandler('markMessageListRead', (global, actions, payload) => { addActionHandler('markMessageListRead', (global, actions, payload) => {
@ -960,23 +954,21 @@ addActionHandler('loadPinnedMessages', (global, actions, payload) => {
void loadPinnedMessages(chat); void loadPinnedMessages(chat);
}); });
addActionHandler('loadSeenBy', (global, actions, payload) => { addActionHandler('loadSeenBy', async (global, actions, payload) => {
const { chatId, messageId } = payload; const { chatId, messageId } = payload;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat) { if (!chat) {
return; return undefined;
} }
(async () => { const result = await callApi('fetchSeenBy', { chat, messageId });
const result = await callApi('fetchSeenBy', { chat, messageId }); if (!result) {
if (!result) { return undefined;
return; }
}
setGlobal(updateChatMessage(getGlobal(), chatId, messageId, { return updateChatMessage(getGlobal(), chatId, messageId, {
seenByUserIds: result, seenByUserIds: result,
})); });
})();
}); });
addActionHandler('saveDefaultSendAs', (global, actions, payload) => { addActionHandler('saveDefaultSendAs', (global, actions, payload) => {
@ -997,31 +989,25 @@ addActionHandler('saveDefaultSendAs', (global, actions, payload) => {
}); });
}); });
addActionHandler('loadSendAs', (global, actions, payload) => { addActionHandler('loadSendAs', async (global, actions, payload) => {
const { chatId } = payload; const { chatId } = payload;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat) { if (!chat) {
return; return undefined;
} }
(async () => { const result = await callApi('fetchSendAs', { chat });
const result = await callApi('fetchSendAs', { chat }); if (!result) {
if (!result) { return updateChat(getGlobal(), chatId, {
global = updateChat(global, chatId, { sendAsIds: [],
sendAsIds: [],
});
setGlobal(global);
return;
}
global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = updateChat(global, chatId, {
sendAsIds: result.ids,
}); });
setGlobal(global); }
})();
global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
global = updateChat(global, chatId, { sendAsIds: result.ids });
return global;
}); });
async function loadPinnedMessages(chat: ApiChat) { async function loadPinnedMessages(chat: ApiChat) {
@ -1060,25 +1046,23 @@ async function loadScheduledHistory(chat: ApiChat) {
setGlobal(global); setGlobal(global);
} }
addActionHandler('loadSponsoredMessages', (global, actions, payload) => { addActionHandler('loadSponsoredMessages', async (global, actions, payload) => {
const { chatId } = payload; const { chatId } = payload;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat) { if (!chat) {
return; return undefined;
} }
(async () => { const result = await callApi('fetchSponsoredMessages', { chat });
const result = await callApi('fetchSponsoredMessages', { chat }); if (!result) {
if (!result) { return undefined;
return; }
}
let newGlobal = updateSponsoredMessage(getGlobal(), chatId, result.messages[0]); global = getGlobal();
newGlobal = addUsers(newGlobal, buildCollectionByKey(result.users, 'id')); global = updateSponsoredMessage(global, chatId, result.messages[0]);
newGlobal = addChats(newGlobal, buildCollectionByKey(result.chats, 'id')); global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
setGlobal(newGlobal); return global;
})();
}); });
addActionHandler('viewSponsoredMessage', (global, actions, payload) => { addActionHandler('viewSponsoredMessage', (global, actions, payload) => {

View File

@ -1,4 +1,4 @@
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal } from '../../index';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import * as mediaLoader from '../../../util/mediaLoader'; import * as mediaLoader from '../../../util/mediaLoader';
import { ApiAppConfig, ApiMediaFormat } from '../../../api/types'; import { ApiAppConfig, ApiMediaFormat } from '../../../api/types';
@ -19,29 +19,26 @@ const INTERACTION_RANDOM_OFFSET = 40;
let interactionLocalId = 0; let interactionLocalId = 0;
addActionHandler('loadAvailableReactions', () => { addActionHandler('loadAvailableReactions', async () => {
(async () => { const result = await callApi('getAvailableReactions');
const result = await callApi('getAvailableReactions'); if (!result) {
return undefined;
}
if (!result) { // Preload animations
return; result.forEach((availableReaction) => {
if (availableReaction.aroundAnimation) {
mediaLoader.fetch(`sticker${availableReaction.aroundAnimation.id}`, ApiMediaFormat.Lottie);
} }
if (availableReaction.centerIcon) {
mediaLoader.fetch(`sticker${availableReaction.centerIcon.id}`, ApiMediaFormat.Lottie);
}
});
// Preload animations return {
result.forEach((availableReaction) => { ...getGlobal(),
if (availableReaction.aroundAnimation) { availableReactions: result,
mediaLoader.fetch(`sticker${availableReaction.aroundAnimation.id}`, ApiMediaFormat.Lottie); };
}
if (availableReaction.centerIcon) {
mediaLoader.fetch(`sticker${availableReaction.centerIcon.id}`, ApiMediaFormat.Lottie);
}
});
setGlobal({
...getGlobal(),
availableReactions: result,
});
})();
}); });
addActionHandler('interactWithAnimatedEmoji', (global, actions, payload) => { addActionHandler('interactWithAnimatedEmoji', (global, actions, payload) => {
@ -196,25 +193,21 @@ addActionHandler('stopActiveReaction', (global, actions, payload) => {
}; };
}); });
addActionHandler('setDefaultReaction', (global, actions, payload) => { addActionHandler('setDefaultReaction', async (global, actions, payload) => {
const { reaction } = payload; const { reaction } = payload;
(async () => { const result = await callApi('setDefaultReaction', { reaction });
const result = await callApi('setDefaultReaction', { reaction }); if (!result) {
return undefined;
}
if (!result) { return {
return; ...getGlobal(),
} appConfig: {
...global.appConfig,
global = getGlobal(); defaultReaction: reaction,
setGlobal({ } as ApiAppConfig,
...global, };
appConfig: {
...global.appConfig,
defaultReaction: reaction,
} as ApiAppConfig,
});
})();
}); });
addActionHandler('stopActiveEmojiInteraction', (global, actions, payload) => { addActionHandler('stopActiveEmojiInteraction', (global, actions, payload) => {
@ -226,46 +219,44 @@ addActionHandler('stopActiveEmojiInteraction', (global, actions, payload) => {
}; };
}); });
addActionHandler('loadReactors', (global, actions, payload) => { addActionHandler('loadReactors', async (global, actions, payload) => {
const { chatId, messageId, reaction } = payload; const { chatId, messageId, reaction } = payload;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
const message = selectChatMessage(global, chatId, messageId); const message = selectChatMessage(global, chatId, messageId);
if (!chat || !message) { if (!chat || !message) {
return; return undefined;
} }
const offset = message.reactors?.nextOffset; const offset = message.reactors?.nextOffset;
const result = await callApi('fetchMessageReactionsList', {
reaction,
chat,
messageId,
offset,
});
(async () => { if (!result) {
const result = await callApi('fetchMessageReactionsList', { return undefined;
reaction, }
chat,
messageId,
offset,
});
if (!result) { global = getGlobal();
return;
}
global = getGlobal(); if (result.users?.length) {
if (result.users?.length) { global = addUsers(global, buildCollectionByKey(result.users, 'id'));
global = addUsers(global, buildCollectionByKey(result.users, 'id')); }
}
const { nextOffset, count, reactions } = result; const { nextOffset, count, reactions } = result;
setGlobal(updateChatMessage(global, chatId, messageId, { return updateChatMessage(global, chatId, messageId, {
reactors: { reactors: {
nextOffset, nextOffset,
count, count,
reactions: [ reactions: [
...(message.reactors?.reactions || []), ...(message.reactors?.reactions || []),
...reactions, ...reactions,
], ],
}, },
})); });
})();
}); });
addActionHandler('loadMessageReactions', (global, actions, payload) => { addActionHandler('loadMessageReactions', (global, actions, payload) => {

View File

@ -18,116 +18,110 @@ import {
} from '../../reducers'; } from '../../reducers';
import { isUserId } from '../../helpers'; import { isUserId } from '../../helpers';
addActionHandler('updateProfile', (global, actions, payload) => { addActionHandler('updateProfile', async (global, actions, payload) => {
const { const {
photo, firstName, lastName, bio: about, username, photo, firstName, lastName, bio: about, username,
} = payload!; } = payload!;
(async () => { const { currentUserId } = global;
const { currentUserId } = global; if (!currentUserId) {
if (!currentUserId) { return undefined;
return; }
}
setGlobal({ setGlobal({
...getGlobal(), ...getGlobal(),
profileEdit: { profileEdit: {
progress: ProfileEditProgress.InProgress, progress: ProfileEditProgress.InProgress,
}, },
}); });
if (photo) { if (photo) {
await callApi('updateProfilePhoto', photo); await callApi('updateProfilePhoto', photo);
} }
if (firstName || lastName || about) { if (firstName || lastName || about) {
const result = await callApi('updateProfile', { firstName, lastName, about }); const result = await callApi('updateProfile', { firstName, lastName, about });
if (result) { if (result) {
global = getGlobal(); global = getGlobal();
const currentUser = currentUserId && selectUser(global, currentUserId); const currentUser = currentUserId && selectUser(global, currentUserId);
if (currentUser) { if (currentUser) {
setGlobal(updateUser( setGlobal(updateUser(
global, global,
currentUser.id, currentUser.id,
{ {
firstName, firstName,
lastName, lastName,
fullInfo: { fullInfo: {
...currentUser.fullInfo, ...currentUser.fullInfo,
bio: about, bio: about,
},
}, },
)); },
} ));
} }
} }
}
if (username) { if (username) {
const result = await callApi('updateUsername', username); const result = await callApi('updateUsername', username);
if (result && currentUserId) { if (result && currentUserId) {
setGlobal(updateUser(getGlobal(), currentUserId, { username })); setGlobal(updateUser(getGlobal(), currentUserId, { username }));
}
} }
}
setGlobal({ return {
...getGlobal(), ...getGlobal(),
profileEdit: { profileEdit: {
progress: ProfileEditProgress.Complete, progress: ProfileEditProgress.Complete,
}, },
}); };
})();
}); });
addActionHandler('checkUsername', (global, actions, payload) => { addActionHandler('checkUsername', async (global, actions, payload) => {
const { username } = payload!; const { username } = payload!;
(async () => { // No need to check the username if profile update is already in progress
// No need to check the username if profile update is already in progress if (global.profileEdit && global.profileEdit.progress === ProfileEditProgress.InProgress) {
if (global.profileEdit && global.profileEdit.progress === ProfileEditProgress.InProgress) { return undefined;
return; }
}
setGlobal({ setGlobal({
...global, ...global,
profileEdit: { profileEdit: {
progress: global.profileEdit ? global.profileEdit.progress : ProfileEditProgress.Idle, progress: global.profileEdit ? global.profileEdit.progress : ProfileEditProgress.Idle,
isUsernameAvailable: undefined, isUsernameAvailable: undefined,
}, },
}); });
const isUsernameAvailable = await callApi('checkUsername', username); const isUsernameAvailable = await callApi('checkUsername', username);
global = getGlobal(); global = getGlobal();
setGlobal({ return {
...global, ...global,
profileEdit: { profileEdit: {
...global.profileEdit!, ...global.profileEdit!,
isUsernameAvailable, isUsernameAvailable,
}, },
}); };
})();
}); });
addActionHandler('loadWallpapers', () => { addActionHandler('loadWallpapers', async () => {
(async () => { const result = await callApi('fetchWallpapers');
const result = await callApi('fetchWallpapers'); if (!result) {
if (!result) { return undefined;
return; }
}
const global = getGlobal(); const global = getGlobal();
setGlobal({ return {
...global, ...global,
settings: { settings: {
...global.settings, ...global.settings,
loadedWallpapers: result.wallpapers, loadedWallpapers: result.wallpapers,
}, },
}); };
})();
}); });
addActionHandler('uploadWallpaper', (global, actions, payload) => { addActionHandler('uploadWallpaper', async (global, actions, payload) => {
const file = payload; const file = payload;
const previewBlobUrl = URL.createObjectURL(file); const previewBlobUrl = URL.createObjectURL(file);
@ -150,91 +144,82 @@ addActionHandler('uploadWallpaper', (global, actions, payload) => {
}, },
}); });
(async () => { const result = await callApi('uploadWallpaper', file);
const result = await callApi('uploadWallpaper', file); if (!result) {
if (!result) { return undefined;
return; }
}
const { wallpaper } = result; const { wallpaper } = result;
global = getGlobal(); global = getGlobal();
if (!global.settings.loadedWallpapers) { if (!global.settings.loadedWallpapers) {
return; return undefined;
} }
const firstWallpaper = global.settings.loadedWallpapers[0]; const firstWallpaper = global.settings.loadedWallpapers[0];
if (!firstWallpaper || firstWallpaper.slug !== UPLOADING_WALLPAPER_SLUG) { if (!firstWallpaper || firstWallpaper.slug !== UPLOADING_WALLPAPER_SLUG) {
return; return undefined;
} }
const withLocalMedia = { const withLocalMedia = {
...wallpaper, ...wallpaper,
document: { document: {
...wallpaper.document, ...wallpaper.document,
previewBlobUrl, previewBlobUrl,
}, },
}; };
setGlobal({ return {
...global, ...global,
settings: { settings: {
...global.settings, ...global.settings,
loadedWallpapers: [ loadedWallpapers: [
withLocalMedia, withLocalMedia,
...global.settings.loadedWallpapers.slice(1), ...global.settings.loadedWallpapers.slice(1),
], ],
}, },
}); };
})();
}); });
addActionHandler('loadBlockedContacts', () => { addActionHandler('loadBlockedContacts', async (global) => {
(async () => { const result = await callApi('fetchBlockedContacts');
const result = await callApi('fetchBlockedContacts'); if (!result) {
return undefined;
}
if (!result) { global = getGlobal();
return;
}
let newGlobal = getGlobal(); if (result.users?.length) {
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
}
if (result.chats?.length) {
global = updateChats(global, buildCollectionByKey(result.chats, 'id'));
}
if (result.users?.length) { global = {
newGlobal = addUsers(newGlobal, buildCollectionByKey(result.users, 'id')); ...global,
} blocked: {
if (result.chats?.length) { ...global.blocked,
newGlobal = updateChats(newGlobal, buildCollectionByKey(result.chats, 'id')); ids: [...(global.blocked.ids || []), ...result.blockedIds],
} totalCount: result.totalCount,
},
};
newGlobal = { return global;
...newGlobal,
blocked: {
...newGlobal.blocked,
ids: [...(newGlobal.blocked.ids || []), ...result.blockedIds],
totalCount: result.totalCount,
},
};
setGlobal(newGlobal);
})();
}); });
addActionHandler('blockContact', (global, actions, payload) => { addActionHandler('blockContact', async (global, actions, payload) => {
const { contactId, accessHash } = payload!; const { contactId, accessHash } = payload!;
(async () => { const result = await callApi('blockContact', contactId, accessHash);
const result = await callApi('blockContact', contactId, accessHash); if (!result) {
if (!result) { return undefined;
return; }
}
const newGlobal = getGlobal(); return addBlockedContact(getGlobal(), contactId);
setGlobal(addBlockedContact(newGlobal, contactId));
})();
}); });
addActionHandler('unblockContact', (global, actions, payload) => { addActionHandler('unblockContact', async (global, actions, payload) => {
const { contactId } = payload!; const { contactId } = payload!;
let accessHash: string | undefined; let accessHash: string | undefined;
const isPrivate = isUserId(contactId); const isPrivate = isUserId(contactId);
@ -242,181 +227,156 @@ addActionHandler('unblockContact', (global, actions, payload) => {
if (isPrivate) { if (isPrivate) {
const user = selectUser(global, contactId); const user = selectUser(global, contactId);
if (!user) { if (!user) {
return; return undefined;
} }
accessHash = user.accessHash; accessHash = user.accessHash;
} }
(async () => { const result = await callApi('unblockContact', contactId, accessHash);
const result = await callApi('unblockContact', contactId, accessHash); if (!result) {
if (!result) { return undefined;
return; }
}
const newGlobal = getGlobal(); return removeBlockedContact(getGlobal(), contactId);
setGlobal(removeBlockedContact(newGlobal, contactId));
})();
}); });
addActionHandler('loadAuthorizations', () => { addActionHandler('loadAuthorizations', async () => {
(async () => { const result = await callApi('fetchAuthorizations');
const result = await callApi('fetchAuthorizations'); if (!result) {
if (!result) { return undefined;
return; }
}
setGlobal({ return {
...getGlobal(), ...getGlobal(),
activeSessions: result, activeSessions: result,
}); };
})();
}); });
addActionHandler('terminateAuthorization', (global, actions, payload) => { addActionHandler('terminateAuthorization', async (global, actions, payload) => {
const { hash } = payload!; const { hash } = payload!;
(async () => { const result = await callApi('terminateAuthorization', hash);
const result = await callApi('terminateAuthorization', hash); if (!result) {
if (!result) { return undefined;
return; }
}
const newGlobal = getGlobal(); global = getGlobal();
setGlobal({ return {
...newGlobal, ...global,
activeSessions: newGlobal.activeSessions.filter((session) => session.hash !== hash), activeSessions: global.activeSessions.filter((session) => session.hash !== hash),
}); };
})();
}); });
addActionHandler('terminateAllAuthorizations', () => { addActionHandler('terminateAllAuthorizations', async (global) => {
(async () => { const result = await callApi('terminateAllAuthorizations');
const result = await callApi('terminateAllAuthorizations'); if (!result) {
if (!result) { return undefined;
return; }
}
const global = getGlobal(); global = getGlobal();
setGlobal({ return {
...global, ...global,
activeSessions: global.activeSessions.filter((session) => session.isCurrent), activeSessions: global.activeSessions.filter((session) => session.isCurrent),
}); };
})();
}); });
addActionHandler('loadNotificationExceptions', (global) => { addActionHandler('loadNotificationExceptions', async (global) => {
const { serverTimeOffset } = global; const { serverTimeOffset } = global;
(async () => { const result = await callApi('fetchNotificationExceptions', { serverTimeOffset });
const result = await callApi('fetchNotificationExceptions', { serverTimeOffset }); if (!result) {
if (!result) { return undefined;
return; }
}
setGlobal(addNotifyExceptions(getGlobal(), result)); return addNotifyExceptions(getGlobal(), result);
})();
}); });
addActionHandler('loadNotificationSettings', (global) => { addActionHandler('loadNotificationSettings', async (global) => {
const { serverTimeOffset } = global; const { serverTimeOffset } = global;
(async () => { const result = await callApi('fetchNotificationSettings', {
const result = await callApi('fetchNotificationSettings', { serverTimeOffset,
serverTimeOffset, });
}); if (!result) {
if (!result) { return undefined;
return; }
}
setGlobal(replaceSettings(getGlobal(), result)); return replaceSettings(getGlobal(), result);
})();
}); });
addActionHandler('updateNotificationSettings', (global, actions, payload) => { addActionHandler('updateNotificationSettings', async (global, actions, payload) => {
const { peerType, isSilent, shouldShowPreviews } = payload!; const { peerType, isSilent, shouldShowPreviews } = payload!;
(async () => { const result = await callApi('updateNotificationSettings', peerType, { isSilent, shouldShowPreviews });
const result = await callApi('updateNotificationSettings', peerType, { isSilent, shouldShowPreviews }); if (!result) {
return undefined;
}
if (!result) { return updateNotifySettings(getGlobal(), peerType, isSilent, shouldShowPreviews);
return;
}
setGlobal(updateNotifySettings(getGlobal(), peerType, isSilent, shouldShowPreviews));
})();
}); });
addActionHandler('updateWebNotificationSettings', (global, actions, payload) => { addActionHandler('updateWebNotificationSettings', (global, actions, payload) => {
(async () => { setGlobal(replaceSettings(global, payload));
setGlobal(replaceSettings(getGlobal(), payload));
const newGlobal = getGlobal(); const { hasPushNotifications, hasWebNotifications } = global.settings.byKey;
const { hasPushNotifications, hasWebNotifications } = newGlobal.settings.byKey; if (hasWebNotifications && hasPushNotifications) {
if (hasWebNotifications && hasPushNotifications) { void subscribe();
await subscribe(); } else {
} else { void unsubscribe();
await unsubscribe(); }
}
})();
}); });
addActionHandler('updateContactSignUpNotification', (global, actions, payload) => { addActionHandler('updateContactSignUpNotification', async (global, actions, payload) => {
const { isSilent } = payload!; const { isSilent } = payload!;
(async () => { const result = await callApi('updateContactSignUpNotification', isSilent);
const result = await callApi('updateContactSignUpNotification', isSilent); if (!result) {
if (!result) { return undefined;
return; }
}
setGlobal(replaceSettings(getGlobal(), { hasContactJoinedNotifications: !isSilent })); return replaceSettings(getGlobal(), { hasContactJoinedNotifications: !isSilent });
})();
}); });
addActionHandler('loadLanguages', () => { addActionHandler('loadLanguages', async () => {
(async () => { const result = await callApi('fetchLanguages');
const result = await callApi('fetchLanguages'); if (!result) {
if (!result) { return undefined;
return; }
}
setGlobal(replaceSettings(getGlobal(), { languages: result })); return replaceSettings(getGlobal(), { languages: result });
})();
}); });
addActionHandler('loadPrivacySettings', () => { addActionHandler('loadPrivacySettings', async (global) => {
(async () => { const [
const [ phoneNumberSettings, lastSeenSettings, profilePhotoSettings, forwardsSettings, chatInviteSettings,
phoneNumberSettings, lastSeenSettings, profilePhotoSettings, forwardsSettings, chatInviteSettings, ] = await Promise.all([
] = await Promise.all([ callApi('fetchPrivacySettings', 'phoneNumber'),
callApi('fetchPrivacySettings', 'phoneNumber'), callApi('fetchPrivacySettings', 'lastSeen'),
callApi('fetchPrivacySettings', 'lastSeen'), callApi('fetchPrivacySettings', 'profilePhoto'),
callApi('fetchPrivacySettings', 'profilePhoto'), callApi('fetchPrivacySettings', 'forwards'),
callApi('fetchPrivacySettings', 'forwards'), callApi('fetchPrivacySettings', 'chatInvite'),
callApi('fetchPrivacySettings', 'chatInvite'), ]);
]);
if ( if (
!phoneNumberSettings || !lastSeenSettings || !profilePhotoSettings || !forwardsSettings || !chatInviteSettings !phoneNumberSettings || !lastSeenSettings || !profilePhotoSettings || !forwardsSettings || !chatInviteSettings
) { ) {
return; return undefined;
} }
const global = getGlobal(); global = getGlobal();
global.settings.privacy.phoneNumber = phoneNumberSettings; global.settings.privacy.phoneNumber = phoneNumberSettings;
global.settings.privacy.lastSeen = lastSeenSettings; global.settings.privacy.lastSeen = lastSeenSettings;
global.settings.privacy.profilePhoto = profilePhotoSettings; global.settings.privacy.profilePhoto = profilePhotoSettings;
global.settings.privacy.forwards = forwardsSettings; global.settings.privacy.forwards = forwardsSettings;
global.settings.privacy.chatInvite = chatInviteSettings; global.settings.privacy.chatInvite = chatInviteSettings;
setGlobal(global); return global;
})();
}); });
addActionHandler('setPrivacyVisibility', (global, actions, payload) => { addActionHandler('setPrivacyVisibility', async (global, actions, payload) => {
const { privacyKey, visibility } = payload!; const { privacyKey, visibility } = payload!;
const { const {
@ -424,7 +384,7 @@ addActionHandler('setPrivacyVisibility', (global, actions, payload) => {
} = global.settings; } = global.settings;
if (!settings) { if (!settings) {
return; return undefined;
} }
const rules = buildInputPrivacyRules(global, { const rules = buildInputPrivacyRules(global, {
@ -433,27 +393,33 @@ addActionHandler('setPrivacyVisibility', (global, actions, payload) => {
deniedIds: [...settings.blockUserIds, ...settings.blockChatIds], deniedIds: [...settings.blockUserIds, ...settings.blockChatIds],
}); });
(async () => { const result = await callApi('setPrivacySettings', privacyKey, rules);
const result = await callApi('setPrivacySettings', privacyKey, rules); if (!result) {
return undefined;
}
if (result) { global = getGlobal();
const newGlobal = getGlobal();
newGlobal.settings.privacy[privacyKey as ApiPrivacyKey] = result; return {
...global,
setGlobal(newGlobal); settings: {
} ...global.settings,
})(); privacy: {
...global.settings.privacy,
[privacyKey]: result,
},
},
};
}); });
addActionHandler('setPrivacySettings', (global, actions, payload) => { addActionHandler('setPrivacySettings', async (global, actions, payload) => {
const { privacyKey, isAllowList, contactsIds } = payload!; const { privacyKey, isAllowList, contactsIds } = payload!;
const { const {
privacy: { [privacyKey as ApiPrivacyKey]: settings }, privacy: { [privacyKey as ApiPrivacyKey]: settings },
} = global.settings; } = global.settings;
if (!settings) { if (!settings) {
return; return undefined;
} }
const rules = buildInputPrivacyRules(global, { const rules = buildInputPrivacyRules(global, {
@ -462,17 +428,23 @@ addActionHandler('setPrivacySettings', (global, actions, payload) => {
deniedIds: !isAllowList ? contactsIds : [...settings.blockUserIds, ...settings.blockChatIds], deniedIds: !isAllowList ? contactsIds : [...settings.blockUserIds, ...settings.blockChatIds],
}); });
(async () => { const result = await callApi('setPrivacySettings', privacyKey, rules);
const result = await callApi('setPrivacySettings', privacyKey, rules); if (!result) {
return undefined;
}
if (result) { global = getGlobal();
const newGlobal = getGlobal();
newGlobal.settings.privacy[privacyKey as ApiPrivacyKey] = result; return {
...global,
setGlobal(newGlobal); settings: {
} ...global.settings,
})(); privacy: {
...global.settings.privacy,
[privacyKey]: result,
},
},
};
}); });
function buildInputPrivacyRules(global: GlobalState, { function buildInputPrivacyRules(global: GlobalState, {
@ -547,71 +519,62 @@ addActionHandler('updateIsOnline', (global, actions, payload) => {
callApi('updateIsOnline', payload); callApi('updateIsOnline', payload);
}); });
addActionHandler('loadContentSettings', () => { addActionHandler('loadContentSettings', async () => {
(async () => { const result = await callApi('fetchContentSettings');
const result = await callApi('fetchContentSettings'); if (!result) return undefined;
if (!result) return;
setGlobal(replaceSettings(getGlobal(), result)); return replaceSettings(getGlobal(), result);
})();
}); });
addActionHandler('updateContentSettings', (global, actions, payload) => { addActionHandler('updateContentSettings', async (global, actions, payload) => {
(async () => { setGlobal(replaceSettings(getGlobal(), { isSensitiveEnabled: payload }));
setGlobal(replaceSettings(getGlobal(), { isSensitiveEnabled: payload }));
const result = await callApi('updateContentSettings', payload); const result = await callApi('updateContentSettings', payload);
if (!result) { if (!result) {
setGlobal(replaceSettings(getGlobal(), { isSensitiveEnabled: !payload })); return replaceSettings(getGlobal(), { isSensitiveEnabled: !payload });
} }
})();
return undefined;
}); });
addActionHandler('loadCountryList', (global, actions, payload = {}) => { addActionHandler('loadCountryList', async (global, actions, payload = {}) => {
let { langCode } = payload; let { langCode } = payload;
if (!langCode) langCode = global.settings.byKey.language; if (!langCode) langCode = global.settings.byKey.language;
(async () => { const countryList = await callApi('fetchCountryList', { langCode });
const countryList = await callApi('fetchCountryList', { langCode }); if (!countryList) return undefined;
if (!countryList) return;
setGlobal({ return {
...getGlobal(), ...getGlobal(),
countryList, countryList,
}); };
})();
}); });
addActionHandler('ensureTimeFormat', (global, actions) => { addActionHandler('ensureTimeFormat', async (global, actions) => {
if (global.authNearestCountry) { if (global.authNearestCountry) {
const timeFormat = COUNTRIES_WITH_12H_TIME_FORMAT.has(global.authNearestCountry.toUpperCase()) ? '12h' : '24h'; const timeFormat = COUNTRIES_WITH_12H_TIME_FORMAT.has(global.authNearestCountry.toUpperCase()) ? '12h' : '24h';
actions.setSettingOption({ timeFormat }); actions.setSettingOption({ timeFormat });
setTimeFormat(timeFormat); setTimeFormat(timeFormat);
} }
(async () => { if (global.settings.byKey.wasTimeFormatSetManually) {
if (getGlobal().settings.byKey.wasTimeFormatSetManually) { return;
return; }
}
const nearestCountryCode = await callApi('fetchNearestCountry'); const nearestCountryCode = await callApi('fetchNearestCountry');
if (nearestCountryCode) { if (nearestCountryCode) {
const timeFormat = COUNTRIES_WITH_12H_TIME_FORMAT.has(nearestCountryCode.toUpperCase()) ? '12h' : '24h'; const timeFormat = COUNTRIES_WITH_12H_TIME_FORMAT.has(nearestCountryCode.toUpperCase()) ? '12h' : '24h';
actions.setSettingOption({ timeFormat }); actions.setSettingOption({ timeFormat });
setTimeFormat(timeFormat); setTimeFormat(timeFormat);
} }
})();
}); });
addActionHandler('loadAppConfig', () => { addActionHandler('loadAppConfig', async () => {
(async () => { const appConfig = await callApi('fetchAppConfig');
const appConfig = await callApi('fetchAppConfig'); if (!appConfig) return undefined;
if (!appConfig) return; return {
...getGlobal(),
setGlobal({ appConfig,
...getGlobal(), };
appConfig,
});
})();
}); });

View File

@ -1,55 +1,50 @@
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal } from '../../index';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { updateStatistics, updateStatisticsGraph } from '../../reducers'; import { updateStatistics, updateStatisticsGraph } from '../../reducers';
import { selectChatMessages, selectChat } from '../../selectors'; import { selectChatMessages, selectChat } from '../../selectors';
addActionHandler('loadStatistics', (global, actions, payload) => { addActionHandler('loadStatistics', async (global, actions, payload) => {
const { chatId } = payload; const { chatId } = payload;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat?.fullInfo) { if (!chat?.fullInfo) {
return; return undefined;
} }
(async () => { const result = await callApi('fetchStatistics', { chat });
const result = await callApi('fetchStatistics', { chat }); if (!result) {
return undefined;
}
if (!result) { global = getGlobal();
return;
}
global = getGlobal(); if (result?.recentTopMessages.length) {
const messages = selectChatMessages(global, chatId);
if (result?.recentTopMessages.length) { result.recentTopMessages = result.recentTopMessages
const messages = selectChatMessages(global, chatId); .map((message) => ({ ...message, ...messages[message.msgId] }));
}
result.recentTopMessages = result.recentTopMessages global = updateStatistics(global, chatId, result);
.map((message) => ({ ...message, ...messages[message.msgId] }));
}
global = updateStatistics(global, chatId, result); return global;
setGlobal(global);
})();
}); });
addActionHandler('loadStatisticsAsyncGraph', (global, actions, payload) => { addActionHandler('loadStatisticsAsyncGraph', async (global, actions, payload) => {
const { const {
chatId, token, name, isPercentage, chatId, token, name, isPercentage,
} = payload; } = payload;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat?.fullInfo) { if (!chat?.fullInfo) {
return; return undefined;
} }
(async () => { const dcId = chat.fullInfo!.statisticsDcId;
const dcId = chat.fullInfo!.statisticsDcId; const result = await callApi('fetchStatisticsAsyncGraph', { token, dcId, isPercentage });
const result = await callApi('fetchStatisticsAsyncGraph', { token, dcId, isPercentage });
if (!result) { if (!result) {
return; return undefined;
} }
setGlobal(updateStatisticsGraph(getGlobal(), chatId, name, result)); return updateStatisticsGraph(getGlobal(), chatId, name, result);
})();
}); });

View File

@ -25,26 +25,24 @@ addActionHandler('loadStickerSets', (global) => {
void loadStickerSets(hash); void loadStickerSets(hash);
}); });
addActionHandler('loadAddedStickers', (global, actions) => { addActionHandler('loadAddedStickers', async (global, actions) => {
const { setIds: addedSetIds } = global.stickers.added; const { setIds: addedSetIds } = global.stickers.added;
const cached = global.stickers.setsById; const cached = global.stickers.setsById;
if (!addedSetIds || !addedSetIds.length) { if (!addedSetIds || !addedSetIds.length) {
return; return;
} }
(async () => { for (let i = 0; i < addedSetIds.length; i++) {
for (let i = 0; i < addedSetIds.length; i++) { const id = addedSetIds[i];
const id = addedSetIds[i]; if (cached[id].stickers) {
if (cached[id].stickers) { continue; // Already loaded
continue; // Already loaded
}
actions.loadStickers({ stickerSetId: id });
if (i % ADDED_SETS_THROTTLE_CHUNK === 0 && i > 0) {
await pause(ADDED_SETS_THROTTLE);
}
} }
})(); actions.loadStickers({ stickerSetId: id });
if (i % ADDED_SETS_THROTTLE_CHUNK === 0 && i > 0) {
await pause(ADDED_SETS_THROTTLE);
}
}
}); });
addActionHandler('loadRecentStickers', (global) => { addActionHandler('loadRecentStickers', (global) => {
@ -57,29 +55,26 @@ addActionHandler('loadFavoriteStickers', (global) => {
void loadFavoriteStickers(hash); void loadFavoriteStickers(hash);
}); });
addActionHandler('loadGreetingStickers', (global) => { addActionHandler('loadGreetingStickers', async (global) => {
const { hash } = global.stickers.greeting || {}; const { hash } = global.stickers.greeting || {};
(async () => { const greeting = await callApi('fetchStickersForEmoji', { emoji: '👋⭐️', hash });
const greeting = await callApi('fetchStickersForEmoji', { emoji: '👋⭐️', hash }); if (!greeting) {
return undefined;
}
if (!greeting) { global = getGlobal();
return;
}
const newGlobal = getGlobal(); return {
...global,
setGlobal({ stickers: {
...newGlobal, ...global.stickers,
stickers: { greeting: {
...newGlobal.stickers, hash: greeting.hash,
greeting: { stickers: greeting.stickers.filter((sticker) => sticker.emoji === '👋'),
hash: greeting.hash,
stickers: greeting.stickers.filter((sticker) => sticker.emoji === '👋'),
},
}, },
}); },
})(); };
}); });
addActionHandler('loadFeaturedStickers', (global) => { addActionHandler('loadFeaturedStickers', (global) => {
@ -141,12 +136,12 @@ addActionHandler('toggleStickerSet', (global, actions, payload) => {
void callApi(!installedDate ? 'installStickerSet' : 'uninstallStickerSet', { stickerSetId, accessHash }); void callApi(!installedDate ? 'installStickerSet' : 'uninstallStickerSet', { stickerSetId, accessHash });
}); });
addActionHandler('loadEmojiKeywords', (global, actions, payload: { language: LangCode }) => { addActionHandler('loadEmojiKeywords', async (global, actions, payload: { language: LangCode }) => {
const { language } = payload; const { language } = payload;
let currentEmojiKeywords = global.emojiKeywords[language]; let currentEmojiKeywords = global.emojiKeywords[language];
if (currentEmojiKeywords?.isLoading) { if (currentEmojiKeywords?.isLoading) {
return; return undefined;
} }
setGlobal({ setGlobal({
@ -160,45 +155,41 @@ addActionHandler('loadEmojiKeywords', (global, actions, payload: { language: Lan
}, },
}); });
(async () => { const emojiKeywords = await callApi('fetchEmojiKeywords', {
const emojiKeywords = await callApi('fetchEmojiKeywords', { language,
language, fromVersion: currentEmojiKeywords ? currentEmojiKeywords.version : 0,
fromVersion: currentEmojiKeywords ? currentEmojiKeywords.version : 0, });
});
global = getGlobal(); global = getGlobal();
currentEmojiKeywords = global.emojiKeywords[language]; currentEmojiKeywords = global.emojiKeywords[language];
if (!emojiKeywords) { if (!emojiKeywords) {
setGlobal({ return {
...global,
emojiKeywords: {
...global.emojiKeywords,
[language]: {
...currentEmojiKeywords,
isLoading: false,
},
},
});
return;
}
setGlobal({
...global, ...global,
emojiKeywords: { emojiKeywords: {
...global.emojiKeywords, ...global.emojiKeywords,
[language]: { [language]: {
...currentEmojiKeywords,
isLoading: false, isLoading: false,
version: emojiKeywords.version,
keywords: {
...(currentEmojiKeywords?.keywords),
...emojiKeywords.keywords,
},
}, },
}, },
}); };
})(); }
return {
...global,
emojiKeywords: {
...global.emojiKeywords,
[language]: {
isLoading: false,
version: emojiKeywords.version,
keywords: {
...(currentEmojiKeywords?.keywords),
...emojiKeywords.keywords,
},
},
},
};
}); });
async function loadStickerSets(hash?: string) { async function loadStickerSets(hash?: string) {

View File

@ -3,86 +3,76 @@ import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { replaceSettings, updateTwoFaSettings } from '../../reducers'; import { replaceSettings, updateTwoFaSettings } from '../../reducers';
addActionHandler('loadPasswordInfo', () => { addActionHandler('loadPasswordInfo', async (global) => {
(async () => { const result = await callApi('getPasswordInfo');
const result = await callApi('getPasswordInfo'); if (!result) {
if (!result) { return undefined;
return; }
}
let global = getGlobal(); global = getGlobal();
global = replaceSettings(global, { hasPassword: result.hasPassword }); global = replaceSettings(global, { hasPassword: result.hasPassword });
global = updateTwoFaSettings(global, { hint: result.hint }); global = updateTwoFaSettings(global, { hint: result.hint });
setGlobal(global); return global;
})();
}); });
addActionHandler('checkPassword', (global, actions, payload) => { addActionHandler('checkPassword', async (global, actions, payload) => {
const { currentPassword, onSuccess } = payload; const { currentPassword, onSuccess } = payload;
setGlobal(updateTwoFaSettings(global, { isLoading: true, error: undefined })); setGlobal(updateTwoFaSettings(global, { isLoading: true, error: undefined }));
(async () => { const isSuccess = await callApi('checkPassword', currentPassword);
const isSuccess = await callApi('checkPassword', currentPassword);
setGlobal(updateTwoFaSettings(getGlobal(), { isLoading: false })); setGlobal(updateTwoFaSettings(getGlobal(), { isLoading: false }));
if (isSuccess) { if (isSuccess) {
onSuccess(); onSuccess();
} }
})();
}); });
addActionHandler('clearPassword', (global, actions, payload) => { addActionHandler('clearPassword', async (global, actions, payload) => {
const { currentPassword, onSuccess } = payload; const { currentPassword, onSuccess } = payload;
setGlobal(updateTwoFaSettings(global, { isLoading: true, error: undefined })); setGlobal(updateTwoFaSettings(global, { isLoading: true, error: undefined }));
(async () => { const isSuccess = await callApi('clearPassword', currentPassword);
const isSuccess = await callApi('clearPassword', currentPassword);
setGlobal(updateTwoFaSettings(getGlobal(), { isLoading: false })); setGlobal(updateTwoFaSettings(getGlobal(), { isLoading: false }));
if (isSuccess) { if (isSuccess) {
onSuccess(); onSuccess();
} }
})();
}); });
addActionHandler('updatePassword', (global, actions, payload) => { addActionHandler('updatePassword', async (global, actions, payload) => {
const { const {
currentPassword, password, hint, email, onSuccess, currentPassword, password, hint, email, onSuccess,
} = payload; } = payload;
setGlobal(updateTwoFaSettings(global, { isLoading: true, error: undefined })); setGlobal(updateTwoFaSettings(global, { isLoading: true, error: undefined }));
(async () => { const isSuccess = await callApi('updatePassword', currentPassword, password, hint, email);
const isSuccess = await callApi('updatePassword', currentPassword, password, hint, email);
setGlobal(updateTwoFaSettings(getGlobal(), { isLoading: false })); setGlobal(updateTwoFaSettings(getGlobal(), { isLoading: false }));
if (isSuccess) { if (isSuccess) {
onSuccess(); onSuccess();
} }
})();
}); });
addActionHandler('updateRecoveryEmail', (global, actions, payload) => { addActionHandler('updateRecoveryEmail', async (global, actions, payload) => {
const { const {
currentPassword, email, onSuccess, currentPassword, email, onSuccess,
} = payload; } = payload;
setGlobal(updateTwoFaSettings(global, { isLoading: true, error: undefined })); setGlobal(updateTwoFaSettings(global, { isLoading: true, error: undefined }));
(async () => { const isSuccess = await callApi('updateRecoveryEmail', currentPassword, email);
const isSuccess = await callApi('updateRecoveryEmail', currentPassword, email);
setGlobal(updateTwoFaSettings(getGlobal(), { isLoading: false, waitingEmailCodeLength: undefined })); setGlobal(updateTwoFaSettings(getGlobal(), { isLoading: false, waitingEmailCodeLength: undefined }));
if (isSuccess) { if (isSuccess) {
onSuccess(); onSuccess();
} }
})();
}); });
addActionHandler('provideTwoFaEmailCode', (global, actions, payload) => { addActionHandler('provideTwoFaEmailCode', (global, actions, payload) => {

View File

@ -32,31 +32,29 @@ addActionHandler('loadFullUser', (global, actions, payload) => {
runDebouncedForFetchFullUser(() => callApi('fetchFullUser', { id, accessHash })); runDebouncedForFetchFullUser(() => callApi('fetchFullUser', { id, accessHash }));
}); });
addActionHandler('loadUser', (global, actions, payload) => { addActionHandler('loadUser', async (global, actions, payload) => {
const { userId } = payload!; const { userId } = payload!;
const user = selectUser(global, userId); const user = selectUser(global, userId);
if (!user) { if (!user) {
return; return undefined;
} }
(async () => { const result = await callApi('fetchUsers', { users: [user] });
const result = await callApi('fetchUsers', { users: [user] }); if (!result) {
if (!result) { return undefined;
return; }
}
const { users, userStatusesById } = result; const { users, userStatusesById } = result;
global = getGlobal(); global = getGlobal();
global = updateUsers(global, buildCollectionByKey(users, 'id')); global = updateUsers(global, buildCollectionByKey(users, 'id'));
setGlobal(replaceUserStatuses(global, { global = replaceUserStatuses(global, {
...global.users.statusesById, ...global.users.statusesById,
...userStatusesById, ...userStatusesById,
})); });
setGlobal(global); return global;
})();
}); });
addActionHandler('loadTopUsers', (global) => { addActionHandler('loadTopUsers', (global) => {
@ -75,35 +73,34 @@ addActionHandler('loadCurrentUser', () => {
void callApi('fetchCurrentUser'); void callApi('fetchCurrentUser');
}); });
addActionHandler('loadCommonChats', (global) => { addActionHandler('loadCommonChats', async (global) => {
const { chatId } = selectCurrentMessageList(global) || {}; const { chatId } = selectCurrentMessageList(global) || {};
const user = chatId ? selectUser(global, chatId) : undefined; const user = chatId ? selectUser(global, chatId) : undefined;
if (!user || isUserBot(user) || user.commonChats?.isFullyLoaded) { if (!user || isUserBot(user) || user.commonChats?.isFullyLoaded) {
return; return undefined;
} }
(async () => { const maxId = user.commonChats?.maxId;
const maxId = user.commonChats?.maxId; const result = await callApi('fetchCommonChats', user.id, user.accessHash!, maxId);
const result = await callApi('fetchCommonChats', user.id, user.accessHash!, maxId); if (!result) {
if (!result) { return undefined;
return; }
}
const { chats, chatIds, isFullyLoaded } = result; const { chats, chatIds, isFullyLoaded } = result;
global = getGlobal(); global = getGlobal();
if (chats.length) { if (chats.length) {
global = addChats(global, buildCollectionByKey(chats, 'id')); global = addChats(global, buildCollectionByKey(chats, 'id'));
} }
global = updateUser(global, user.id, { global = updateUser(global, user.id, {
commonChats: { commonChats: {
maxId: chatIds.length ? chatIds[chatIds.length - 1] : '0', maxId: chatIds.length ? chatIds[chatIds.length - 1] : '0',
ids: unique((user.commonChats?.ids || []).concat(chatIds)), ids: unique((user.commonChats?.ids || []).concat(chatIds)),
isFullyLoaded, isFullyLoaded,
}, },
}); });
setGlobal(global);
})(); return global;
}); });
addActionHandler('updateContact', (global, actions, payload) => { addActionHandler('updateContact', (global, actions, payload) => {
@ -223,32 +220,31 @@ async function deleteContact(userId: string) {
await callApi('deleteContact', { id, accessHash }); await callApi('deleteContact', { id, accessHash });
} }
addActionHandler('loadProfilePhotos', (global, actions, payload) => { addActionHandler('loadProfilePhotos', async (global, actions, payload) => {
const { profileId } = payload!; const { profileId } = payload!;
const isPrivate = isUserId(profileId); const isPrivate = isUserId(profileId);
const user = isPrivate ? selectUser(global, profileId) : undefined; const user = isPrivate ? selectUser(global, profileId) : undefined;
const chat = !isPrivate ? selectChat(global, profileId) : undefined; const chat = !isPrivate ? selectChat(global, profileId) : undefined;
if (!user && !chat) { if (!user && !chat) {
return; return undefined;
} }
(async () => { const result = await callApi('fetchProfilePhotos', user, chat);
const result = await callApi('fetchProfilePhotos', user, chat); if (!result || !result.photos) {
if (!result || !result.photos) { return undefined;
return; }
}
let newGlobal = getGlobal(); global = getGlobal();
if (isPrivate) {
newGlobal = updateUser(newGlobal, profileId, { photos: result.photos });
} else {
newGlobal = addUsers(newGlobal, buildCollectionByKey(result.users!, 'id'));
newGlobal = updateChat(newGlobal, profileId, { photos: result.photos });
}
setGlobal(newGlobal); if (isPrivate) {
})(); global = updateUser(global, profileId, { photos: result.photos });
} else {
global = addUsers(global, buildCollectionByKey(result.users!, 'id'));
global = updateChat(global, profileId, { photos: result.photos });
}
return global;
}); });
addActionHandler('setUserSearchQuery', (global, actions, payload) => { addActionHandler('setUserSearchQuery', (global, actions, payload) => {

View File

@ -1,6 +1,6 @@
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { ApiUpdate, MAIN_THREAD_ID } from '../../../api/types'; import { MAIN_THREAD_ID } from '../../../api/types';
import { ARCHIVED_FOLDER_ID, MAX_ACTIVE_PINNED_CHATS } from '../../../config'; import { ARCHIVED_FOLDER_ID, MAX_ACTIVE_PINNED_CHATS } from '../../../config';
import { pick } from '../../../util/iteratees'; import { pick } from '../../../util/iteratees';
@ -25,7 +25,7 @@ const TYPING_STATUS_CLEAR_DELAY = 6000; // 6 seconds
// Enough to animate and mark as read in Message List // Enough to animate and mark as read in Message List
const CURRENT_CHAT_UNREAD_DELAY = 1500; const CURRENT_CHAT_UNREAD_DELAY = 1500;
addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => { addActionHandler('apiUpdate', (global, actions, update) => {
switch (update['@type']) { switch (update['@type']) {
case 'updateChat': { case 'updateChat': {
if (!update.noTopChatsRequest && !selectIsChatListed(global, update.id)) { if (!update.noTopChatsRequest && !selectIsChatListed(global, update.id)) {
@ -33,8 +33,7 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
actions.loadTopChats(); actions.loadTopChats();
} }
const newGlobal = updateChat(global, update.id, update.chat, update.newProfilePhoto); setGlobal(updateChat(global, update.id, update.chat, update.newProfilePhoto));
setGlobal(newGlobal);
if (update.chat.id) { if (update.chat.id) {
closeMessageNotifications({ closeMessageNotifications({
@ -42,13 +41,14 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
lastReadInboxMessageId: update.chat.lastReadInboxMessageId, lastReadInboxMessageId: update.chat.lastReadInboxMessageId,
}); });
} }
break;
return undefined;
} }
case 'updateChatJoin': { case 'updateChatJoin': {
const listType = selectChatListType(global, update.id); const listType = selectChatListType(global, update.id);
if (!listType) { if (!listType) {
break; return undefined;
} }
global = updateChatListIds(global, listType, [update.id]); global = updateChatListIds(global, listType, [update.id]);
@ -59,19 +59,16 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
if (chat) { if (chat) {
actions.requestChatUpdate({ chatId: chat.id }); actions.requestChatUpdate({ chatId: chat.id });
} }
break;
return undefined;
} }
case 'updateChatLeave': { case 'updateChatLeave': {
setGlobal(leaveChat(global, update.id)); return leaveChat(global, update.id);
break;
} }
case 'updateChatInbox': { case 'updateChatInbox': {
setGlobal(updateChat(global, update.id, update.chat)); return updateChat(global, update.id, update.chat);
break;
} }
case 'updateChatTypingStatus': { case 'updateChatTypingStatus': {
@ -79,14 +76,14 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
setGlobal(updateChat(global, id, { typingStatus })); setGlobal(updateChat(global, id, { typingStatus }));
setTimeout(() => { setTimeout(() => {
const newGlobal = getGlobal(); global = getGlobal();
const chat = selectChat(newGlobal, id); const chat = selectChat(global, id);
if (chat && typingStatus && chat.typingStatus && chat.typingStatus.timestamp === typingStatus.timestamp) { if (chat && typingStatus && chat.typingStatus && chat.typingStatus.timestamp === typingStatus.timestamp) {
setGlobal(updateChat(newGlobal, id, { typingStatus: undefined })); setGlobal(updateChat(global, id, { typingStatus: undefined }));
} }
}, TYPING_STATUS_CLEAR_DELAY); }, TYPING_STATUS_CLEAR_DELAY);
break; return undefined;
} }
case 'newMessage': { case 'newMessage': {
@ -94,12 +91,12 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
const { chatId: currentChatId, threadId, type: messageListType } = selectCurrentMessageList(global) || {}; const { chatId: currentChatId, threadId, type: messageListType } = selectCurrentMessageList(global) || {};
if (message.senderId === global.currentUserId && !message.isFromScheduled) { if (message.senderId === global.currentUserId && !message.isFromScheduled) {
return; return undefined;
} }
const chat = selectChat(global, update.chatId); const chat = selectChat(global, update.chatId);
if (!chat) { if (!chat) {
return; return undefined;
} }
const isActiveChat = ( const isActiveChat = (
@ -126,14 +123,14 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
message, message,
}); });
break; return undefined;
} }
case 'updateMessage': { case 'updateMessage': {
const { message } = update; const { message } = update;
const chat = selectChat(global, update.chatId); const chat = selectChat(global, update.chatId);
if (!chat) { if (!chat) {
return; return undefined;
} }
if (getMessageRecentReaction(message)) { if (getMessageRecentReaction(message)) {
@ -142,14 +139,15 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
message, message,
}); });
} }
break;
return undefined;
} }
case 'updateCommonBoxMessages': case 'updateCommonBoxMessages':
case 'updateChannelMessages': { case 'updateChannelMessages': {
const { ids, messageUpdate } = update; const { ids, messageUpdate } = update;
if (messageUpdate.hasUnreadMention !== false) { if (messageUpdate.hasUnreadMention !== false) {
return; return undefined;
} }
ids.forEach((id) => { ids.forEach((id) => {
@ -162,34 +160,29 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
} }
}); });
setGlobal(global); return global;
break;
} }
case 'updateChatFullInfo': { case 'updateChatFullInfo': {
const { fullInfo } = update; const { fullInfo } = update;
const targetChat = global.chats.byId[update.id]; const targetChat = global.chats.byId[update.id];
if (!targetChat) { if (!targetChat) {
return; return undefined;
} }
setGlobal(updateChat(global, update.id, { return updateChat(global, update.id, {
fullInfo: { fullInfo: {
...targetChat.fullInfo, ...targetChat.fullInfo,
...fullInfo, ...fullInfo,
}, },
})); });
break;
} }
case 'updatePinnedChatIds': { case 'updatePinnedChatIds': {
const { ids, folderId } = update; const { ids, folderId } = update;
const listType = folderId === ARCHIVED_FOLDER_ID ? 'archived' : 'active'; const listType = folderId === ARCHIVED_FOLDER_ID ? 'archived' : 'active';
global = { return {
...global, ...global,
chats: { chats: {
...global.chats, ...global.chats,
@ -199,57 +192,49 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
}, },
}, },
}; };
setGlobal(global);
break;
} }
case 'updateChatPinned': { case 'updateChatPinned': {
const { id, isPinned } = update; const { id, isPinned } = update;
const listType = selectChatListType(global, id); const listType = selectChatListType(global, id);
if (listType) { if (!listType) {
const { [listType]: orderedPinnedIds } = global.chats.orderedPinnedIds; return undefined;
let newOrderedPinnedIds = orderedPinnedIds || [];
if (!isPinned) {
newOrderedPinnedIds = newOrderedPinnedIds.filter((pinnedId) => pinnedId !== id);
} else if (!newOrderedPinnedIds.includes(id)) {
// When moving pinned chats to archive, active ordered pinned ids don't get updated
// (to preserve chat pinned state when it returns from archive)
// If user already has max pinned chats, we should check for orderedIds
// that don't point to listed chats
if (listType === 'active' && newOrderedPinnedIds.length >= MAX_ACTIVE_PINNED_CHATS) {
const listIds = global.chats.listIds.active;
newOrderedPinnedIds = newOrderedPinnedIds.filter((pinnedId) => listIds && listIds.includes(pinnedId));
}
newOrderedPinnedIds = [id, ...newOrderedPinnedIds];
}
global = {
...global,
chats: {
...global.chats,
orderedPinnedIds: {
...global.chats.orderedPinnedIds,
[listType]: newOrderedPinnedIds.length ? newOrderedPinnedIds : undefined,
},
},
};
} }
setGlobal(global); const { [listType]: orderedPinnedIds } = global.chats.orderedPinnedIds;
break; let newOrderedPinnedIds = orderedPinnedIds || [];
if (!isPinned) {
newOrderedPinnedIds = newOrderedPinnedIds.filter((pinnedId) => pinnedId !== id);
} else if (!newOrderedPinnedIds.includes(id)) {
// When moving pinned chats to archive, active ordered pinned ids don't get updated
// (to preserve chat pinned state when it returns from archive)
// If user already has max pinned chats, we should check for orderedIds
// that don't point to listed chats
if (listType === 'active' && newOrderedPinnedIds.length >= MAX_ACTIVE_PINNED_CHATS) {
const listIds = global.chats.listIds.active;
newOrderedPinnedIds = newOrderedPinnedIds.filter((pinnedId) => listIds && listIds.includes(pinnedId));
}
newOrderedPinnedIds = [id, ...newOrderedPinnedIds];
}
return {
...global,
chats: {
...global.chats,
orderedPinnedIds: {
...global.chats.orderedPinnedIds,
[listType]: newOrderedPinnedIds.length ? newOrderedPinnedIds : undefined,
},
},
};
} }
case 'updateChatListType': { case 'updateChatListType': {
const { id, folderId } = update; const { id, folderId } = update;
setGlobal(updateChatListType(global, id, folderId)); return updateChatListType(global, id, folderId);
break;
} }
case 'updateChatFolder': { case 'updateChatFolder': {
@ -267,51 +252,45 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
? orderedIds && orderedIds.includes(id) ? orderedIds : [...(orderedIds || []), id] ? orderedIds && orderedIds.includes(id) ? orderedIds : [...(orderedIds || []), id]
: orderedIds ? orderedIds.filter((orderedId) => orderedId !== id) : undefined; : orderedIds ? orderedIds.filter((orderedId) => orderedId !== id) : undefined;
setGlobal({ return {
...global, ...global,
chatFolders: { chatFolders: {
...global.chatFolders, ...global.chatFolders,
byId: newChatFoldersById, byId: newChatFoldersById,
orderedIds: newOrderedIds, orderedIds: newOrderedIds,
}, },
}); };
break;
} }
case 'updateChatFoldersOrder': { case 'updateChatFoldersOrder': {
const { orderedIds } = update; const { orderedIds } = update;
setGlobal({ return {
...global, ...global,
chatFolders: { chatFolders: {
...global.chatFolders, ...global.chatFolders,
orderedIds, orderedIds,
}, },
}); };
break;
} }
case 'updateRecommendedChatFolders': { case 'updateRecommendedChatFolders': {
const { folders } = update; const { folders } = update;
setGlobal({ return {
...global, ...global,
chatFolders: { chatFolders: {
...global.chatFolders, ...global.chatFolders,
recommended: folders, recommended: folders,
}, },
}); };
break;
} }
case 'updateChatMembers': { case 'updateChatMembers': {
const targetChat = global.chats.byId[update.id]; const targetChat = global.chats.byId[update.id];
const { replacedMembers, addedMember, deletedMemberId } = update; const { replacedMembers, addedMember, deletedMemberId } = update;
if (!targetChat) { if (!targetChat) {
return; return undefined;
} }
let shouldUpdate = false; let shouldUpdate = false;
@ -342,17 +321,17 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
const adminMembers = members.filter(({ isOwner, isAdmin }) => isOwner || isAdmin); const adminMembers = members.filter(({ isOwner, isAdmin }) => isOwner || isAdmin);
// TODO Kicked members? // TODO Kicked members?
setGlobal(updateChat(global, update.id, { return updateChat(global, update.id, {
membersCount: members.length, membersCount: members.length,
fullInfo: { fullInfo: {
...targetChat.fullInfo, ...targetChat.fullInfo,
members, members,
adminMembers, adminMembers,
}, },
})); });
} }
break; return undefined;
} }
case 'deleteProfilePhotos': { case 'deleteProfilePhotos': {
@ -360,11 +339,12 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
const chat = global.chats.byId[chatId]; const chat = global.chats.byId[chatId];
if (chat?.photos) { if (chat?.photos) {
setGlobal(updateChat(global, chatId, { return updateChat(global, chatId, {
photos: chat.photos.filter((photo) => !ids.includes(photo.id)), photos: chat.photos.filter((photo) => !ids.includes(photo.id)),
})); });
} }
break;
return undefined;
} }
case 'draftMessage': { case 'draftMessage': {
@ -372,38 +352,43 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
chatId, formattedText, date, replyingToId, chatId, formattedText, date, replyingToId,
} = update; } = update;
const chat = global.chats.byId[chatId]; const chat = global.chats.byId[chatId];
if (!chat) {
if (chat) { return undefined;
global = replaceThreadParam(global, chatId, MAIN_THREAD_ID, 'draft', formattedText);
global = replaceThreadParam(global, chatId, MAIN_THREAD_ID, 'replyingToId', replyingToId);
global = updateChat(global, chatId, { draftDate: date });
setGlobal(global);
} }
break;
global = replaceThreadParam(global, chatId, MAIN_THREAD_ID, 'draft', formattedText);
global = replaceThreadParam(global, chatId, MAIN_THREAD_ID, 'replyingToId', replyingToId);
global = updateChat(global, chatId, { draftDate: date });
return global;
} }
case 'showInvite': { case 'showInvite': {
const { data } = update; const { data } = update;
actions.showDialog({ data }); actions.showDialog({ data });
break;
return undefined;
} }
case 'updatePendingJoinRequests': { case 'updatePendingJoinRequests': {
const { chatId, requestsPending, recentRequesterIds } = update; const { chatId, requestsPending, recentRequesterIds } = update;
const chat = global.chats.byId[chatId]; const chat = global.chats.byId[chatId];
if (chat) { if (!chat) {
global = updateChat(global, chatId, { return undefined;
fullInfo: {
...chat.fullInfo,
requestsPending,
recentRequesterIds,
},
});
setGlobal(global);
actions.loadChatJoinRequests({ chatId });
} }
global = updateChat(global, chatId, {
fullInfo: {
...chat.fullInfo,
requestsPending,
recentRequesterIds,
},
});
setGlobal(global);
actions.loadChatJoinRequests({ chatId });
} }
} }
return undefined;
}); });

View File

@ -5,7 +5,6 @@ import {
import { GlobalState } from '../../types'; import { GlobalState } from '../../types';
import { import {
ApiUpdate,
ApiUpdateAuthorizationState, ApiUpdateAuthorizationState,
ApiUpdateAuthorizationError, ApiUpdateAuthorizationError,
ApiUpdateConnectionState, ApiUpdateConnectionState,
@ -20,7 +19,7 @@ import { selectNotifySettings } from '../../selectors';
import { forceWebsync } from '../../../util/websync'; import { forceWebsync } from '../../../util/websync';
import { getShippingError } from '../../../util/getReadableErrorText'; import { getShippingError } from '../../../util/getReadableErrorText';
addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => { addActionHandler('apiUpdate', (global, actions, update) => {
if (DEBUG) { if (DEBUG) {
if (update['@type'] !== 'updateUserStatus' && update['@type'] !== 'updateServerTimeOffset') { if (update['@type'] !== 'updateUserStatus' && update['@type'] !== 'updateServerTimeOffset') {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console

View File

@ -1,13 +1,12 @@
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { import {
ApiUpdate, ApiMessage, ApiPollResult, ApiThreadInfo, MAIN_THREAD_ID, ApiMessage, ApiPollResult, ApiThreadInfo, MAIN_THREAD_ID,
} from '../../../api/types'; } from '../../../api/types';
import { unique } from '../../../util/iteratees'; import { unique } from '../../../util/iteratees';
import { areDeepEqual } from '../../../util/areDeepEqual'; import { areDeepEqual } from '../../../util/areDeepEqual';
import { notifyAboutMessage } from '../../../util/notifications'; import { notifyAboutMessage } from '../../../util/notifications';
import { checkIfReactionAdded } from '../../helpers/reactions';
import { import {
updateChat, updateChat,
deleteChatMessages, deleteChatMessages,
@ -34,7 +33,7 @@ import {
selectPinnedIds, selectPinnedIds,
selectScheduledMessage, selectScheduledMessage,
selectScheduledMessages, selectScheduledMessages,
isMessageInCurrentMessageList, selectIsMessageInCurrentMessageList,
selectScheduledIds, selectScheduledIds,
selectCurrentMessageList, selectCurrentMessageList,
selectViewportIds, selectViewportIds,
@ -46,12 +45,12 @@ import {
selectLocalAnimatedEmoji, selectLocalAnimatedEmoji,
} from '../../selectors'; } from '../../selectors';
import { import {
getMessageContent, isUserId, isMessageLocal, getMessageText, getMessageContent, isUserId, isMessageLocal, getMessageText, checkIfReactionAdded,
} from '../../helpers'; } from '../../helpers';
const ANIMATION_DELAY = 350; const ANIMATION_DELAY = 350;
addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => { addActionHandler('apiUpdate', (global, actions, update) => {
switch (update['@type']) { switch (update['@type']) {
case 'newMessage': { case 'newMessage': {
const { const {
@ -73,7 +72,7 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
const newMessage = selectChatMessage(global, chatId, id)!; const newMessage = selectChatMessage(global, chatId, id)!;
if (isMessageInCurrentMessageList(global, chatId, message as ApiMessage)) { if (selectIsMessageInCurrentMessageList(global, chatId, message as ApiMessage)) {
if (message.isOutgoing && !(message.content?.action)) { if (message.isOutgoing && !(message.content?.action)) {
const currentMessageList = selectCurrentMessageList(global); const currentMessageList = selectCurrentMessageList(global);
if (currentMessageList) { if (currentMessageList) {
@ -186,7 +185,7 @@ addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => {
&& !message.isOutgoing && !message.isOutgoing
&& chat.lastMessage?.id === message.id && chat.lastMessage?.id === message.id
&& selectIsChatWithBot(global, chat) && selectIsChatWithBot(global, chat)
&& isMessageInCurrentMessageList(global, chatId, message as ApiMessage) && selectIsMessageInCurrentMessageList(global, chatId, message as ApiMessage)
&& selectIsViewportNewest(global, chatId, message.threadInfo?.threadId || MAIN_THREAD_ID) && selectIsViewportNewest(global, chatId, message.threadInfo?.threadId || MAIN_THREAD_ID)
) { ) {
actions.focusLastMessage(); actions.focusLastMessage();
@ -602,11 +601,11 @@ function updateListedAndViewportIds(global: GlobalState, actions: GlobalActions,
if (selectIsViewportNewest(global, chatId, MAIN_THREAD_ID)) { if (selectIsViewportNewest(global, chatId, MAIN_THREAD_ID)) {
// Always keep the first unread message in the viewport list // Always keep the first unread message in the viewport list
const firstUnreadId = selectFirstUnreadId(global, chatId, MAIN_THREAD_ID); const firstUnreadId = selectFirstUnreadId(global, chatId, MAIN_THREAD_ID);
const newGlobal = addViewportId(global, chatId, MAIN_THREAD_ID, id); const candidateGlobal = addViewportId(global, chatId, MAIN_THREAD_ID, id);
const newViewportIds = selectViewportIds(newGlobal, chatId, MAIN_THREAD_ID); const newViewportIds = selectViewportIds(candidateGlobal, chatId, MAIN_THREAD_ID);
if (!firstUnreadId || newViewportIds!.includes(firstUnreadId)) { if (!firstUnreadId || newViewportIds!.includes(firstUnreadId)) {
global = newGlobal; global = candidateGlobal;
} }
} }

View File

@ -1,13 +1,12 @@
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { ApiUpdate } from '../../../api/types';
import { ApiPrivacyKey, PaymentStep } from '../../../types'; import { ApiPrivacyKey, PaymentStep } from '../../../types';
import { import {
addBlockedContact, removeBlockedContact, setConfirmPaymentUrl, setPaymentStep, addBlockedContact, removeBlockedContact, setConfirmPaymentUrl, setPaymentStep,
} from '../../reducers'; } from '../../reducers';
addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => { addActionHandler('apiUpdate', (global, actions, update) => {
switch (update['@type']) { switch (update['@type']) {
case 'updatePeerBlocked': case 'updatePeerBlocked':
if (update.isBlocked) { if (update.isBlocked) {

View File

@ -1,10 +1,8 @@
import { addActionHandler } from '../../index'; import { addActionHandler } from '../../index';
import { ApiUpdate } from '../../../api/types';
import { clearPayment } from '../../reducers'; import { clearPayment } from '../../reducers';
addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => { addActionHandler('apiUpdate', (global, actions, update) => {
switch (update['@type']) { switch (update['@type']) {
case 'updatePaymentStateCompleted': { case 'updatePaymentStateCompleted': {
return clearPayment(global); return clearPayment(global);

View File

@ -1,10 +1,8 @@
import { addActionHandler, setGlobal } from '../../index'; import { addActionHandler, setGlobal } from '../../index';
import { ApiUpdate } from '../../../api/types';
import { GlobalState } from '../../types';
import { addNotifyException, updateChat, updateNotifySettings } from '../../reducers'; import { addNotifyException, updateChat, updateNotifySettings } from '../../reducers';
addActionHandler('apiUpdate', (global, actions, update: ApiUpdate): GlobalState | undefined => { addActionHandler('apiUpdate', (global, actions, update) => {
switch (update['@type']) { switch (update['@type']) {
case 'updateNotifySettings': { case 'updateNotifySettings': {
return updateNotifySettings(global, update.peerType, update.isSilent, update.shouldShowPreviews); return updateNotifySettings(global, update.peerType, update.isSilent, update.shouldShowPreviews);

View File

@ -1,10 +1,8 @@
import { addActionHandler } from '../../index'; import { addActionHandler } from '../../index';
import { ApiUpdate } from '../../../api/types';
import { updateStickerSet } from '../../reducers'; import { updateStickerSet } from '../../reducers';
addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => { addActionHandler('apiUpdate', (global, actions, update) => {
switch (update['@type']) { switch (update['@type']) {
case 'updateStickerSet': { case 'updateStickerSet': {
return updateStickerSet(global, update.id, update.stickerSet); return updateStickerSet(global, update.id, update.stickerSet);

View File

@ -1,9 +1,6 @@
import { addActionHandler } from '../../index'; import { addActionHandler } from '../../index';
import { ApiUpdate } from '../../../api/types'; addActionHandler('apiUpdate', (global, actions, update) => {
import { GlobalState } from '../../types';
addActionHandler('apiUpdate', (global, actions, update: ApiUpdate): GlobalState | undefined => {
switch (update['@type']) { switch (update['@type']) {
case 'updateTwoFaStateWaitCode': { case 'updateTwoFaStateWaitCode': {
return { return {

View File

@ -1,6 +1,6 @@
import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { ApiUpdate, ApiUserStatus } from '../../../api/types'; import { ApiUserStatus } from '../../../api/types';
import { deleteContact, replaceUserStatuses, updateUser } from '../../reducers'; import { deleteContact, replaceUserStatuses, updateUser } from '../../reducers';
import { throttle } from '../../../util/schedulers'; import { throttle } from '../../../util/schedulers';
@ -27,7 +27,7 @@ function flushStatusUpdates() {
pendingStatusUpdates = {}; pendingStatusUpdates = {};
} }
addActionHandler('apiUpdate', (global, actions, update: ApiUpdate) => { addActionHandler('apiUpdate', (global, actions, update) => {
switch (update['@type']) { switch (update['@type']) {
case 'deleteContact': { case 'deleteContact': {
return deleteContact(global, update.id); return deleteContact(global, update.id);

View File

@ -102,26 +102,24 @@ addActionHandler('toggleGroupCallPanel', (global) => {
}; };
}); });
addActionHandler('subscribeToGroupCallUpdates', (global, actions, payload) => { addActionHandler('subscribeToGroupCallUpdates', async (global, actions, payload) => {
const { subscribed, id } = payload!; const { subscribed, id } = payload!;
const groupCall = selectGroupCall(global, id); const groupCall = selectGroupCall(global, id);
if (!groupCall) return; if (!groupCall) return;
(async () => { if (subscribed) {
if (subscribed) { await fetchGroupCall(groupCall);
await fetchGroupCall(groupCall); await fetchGroupCallParticipants(groupCall);
await fetchGroupCallParticipants(groupCall); }
}
await callApi('toggleGroupCallStartSubscription', { await callApi('toggleGroupCallStartSubscription', {
subscribed, subscribed,
call: groupCall, call: groupCall,
}); });
})();
}); });
addActionHandler('createGroupCall', (global, actions, payload) => { addActionHandler('createGroupCall', async (global, actions, payload) => {
const { chatId } = payload; const { chatId } = payload;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
@ -129,24 +127,22 @@ addActionHandler('createGroupCall', (global, actions, payload) => {
return; return;
} }
(async () => { const result = await callApi('createGroupCall', {
const result = await callApi('createGroupCall', { peer: chat,
peer: chat, });
});
if (!result) return; if (!result) return;
global = getGlobal(); global = getGlobal();
setGlobal(updateGroupCall(global, result.id, { setGlobal(updateGroupCall(global, result.id, {
...result, ...result,
chatId, chatId,
})); }));
actions.joinGroupCall({ id: result.id, accessHash: result.accessHash }); actions.joinGroupCall({ id: result.id, accessHash: result.accessHash });
})();
}); });
addActionHandler('createGroupCallInviteLink', (global, actions) => { addActionHandler('createGroupCallInviteLink', async (global, actions) => {
const groupCall = selectActiveGroupCall(global); const groupCall = selectActiveGroupCall(global);
if (!groupCall || !groupCall.chatId) { if (!groupCall || !groupCall.chatId) {
@ -160,47 +156,43 @@ addActionHandler('createGroupCallInviteLink', (global, actions) => {
const canInvite = Boolean(chat.username); const canInvite = Boolean(chat.username);
(async () => { let { inviteLink } = chat.fullInfo!;
let { inviteLink } = chat.fullInfo!; if (canInvite) {
if (canInvite) { inviteLink = await callApi('exportGroupCallInvite', {
inviteLink = await callApi('exportGroupCallInvite', { call: groupCall,
call: groupCall, canSelfUnmute: false,
canSelfUnmute: false,
});
}
if (!inviteLink) {
return;
}
copyTextToClipboard(inviteLink);
actions.showNotification({
message: 'Link copied to clipboard',
}); });
})(); }
if (!inviteLink) {
return;
}
copyTextToClipboard(inviteLink);
actions.showNotification({
message: 'Link copied to clipboard',
});
}); });
addActionHandler('joinVoiceChatByLink', (global, actions, payload) => { addActionHandler('joinVoiceChatByLink', async (global, actions, payload) => {
const { username, inviteHash } = payload!; const { username, inviteHash } = payload!;
(async () => { const chat = await fetchChatByUsername(username);
const chat = await fetchChatByUsername(username);
if (!chat) { if (!chat) {
actions.showNotification({ message: langProvider.getTranslation('NoUsernameFound') }); actions.showNotification({ message: langProvider.getTranslation('NoUsernameFound') });
return; return;
} }
const full = await loadFullChat(chat); const full = await loadFullChat(chat);
if (full?.groupCall) { if (full?.groupCall) {
actions.joinGroupCall({ id: full.groupCall.id, accessHash: full.groupCall.accessHash, inviteHash }); actions.joinGroupCall({ id: full.groupCall.id, accessHash: full.groupCall.accessHash, inviteHash });
} }
})();
}); });
addActionHandler('joinGroupCall', (global, actions, payload) => { addActionHandler('joinGroupCall', async (global, actions, payload) => {
if (!ARE_CALLS_SUPPORTED) return; if (!ARE_CALLS_SUPPORTED) return undefined;
const { const {
chatId, id, accessHash, inviteHash, chatId, id, accessHash, inviteHash,
@ -208,59 +200,56 @@ addActionHandler('joinGroupCall', (global, actions, payload) => {
createAudioElement(); createAudioElement();
(async () => { await initializeSoundsForSafari();
await initializeSoundsForSafari(); const { groupCalls: { activeGroupCallId } } = global;
const { groupCalls: { activeGroupCallId } } = global; let groupCall = id ? selectGroupCall(global, id) : selectChatGroupCall(global, chatId);
let groupCall = id ? selectGroupCall(global, id) : selectChatGroupCall(global, chatId);
if (groupCall?.id === activeGroupCallId) { if (groupCall?.id === activeGroupCallId) {
actions.toggleGroupCallPanel(); actions.toggleGroupCallPanel();
return; return undefined;
} }
if (activeGroupCallId) { if (activeGroupCallId) {
actions.leaveGroupCall({ actions.leaveGroupCall({
rejoin: payload, rejoin: payload,
});
return;
}
if (groupCall && activeGroupCallId === groupCall.id) {
actions.toggleGroupCallPanel();
return;
}
if (!groupCall && (!id || !accessHash)) {
groupCall = await fetchGroupCall({
id,
accessHash,
});
}
if (!groupCall) return;
global = getGlobal();
global = updateGroupCall(
global,
groupCall.id,
{
...groupCall,
inviteHash,
},
undefined,
groupCall.participantsCount + 1,
);
setGlobal({
...global,
groupCalls: {
...global.groupCalls,
activeGroupCallId: groupCall.id,
isGroupCallPanelHidden: false,
},
}); });
})(); return undefined;
}
if (groupCall && activeGroupCallId === groupCall.id) {
actions.toggleGroupCallPanel();
return undefined;
}
if (!groupCall && (!id || !accessHash)) {
groupCall = await fetchGroupCall({
id,
accessHash,
});
}
if (!groupCall) return undefined;
global = getGlobal();
global = updateGroupCall(
global,
groupCall.id,
{
...groupCall,
inviteHash,
},
undefined,
groupCall.participantsCount + 1,
);
global = {
...global,
groupCalls: {
...global.groupCalls,
activeGroupCallId: groupCall.id,
isGroupCallPanelHidden: false,
},
};
return global;
}); });
addActionHandler('playGroupCallSound', (global, actions, payload) => { addActionHandler('playGroupCallSound', (global, actions, payload) => {

View File

@ -265,10 +265,10 @@ addActionHandler('openPollResults', (global, actions, payload) => {
if (!shouldOpenInstantly) { if (!shouldOpenInstantly) {
window.setTimeout(() => { window.setTimeout(() => {
const newGlobal = getGlobal(); global = getGlobal();
setGlobal({ setGlobal({
...newGlobal, ...global,
pollResults: { pollResults: {
chatId, chatId,
messageId, messageId,
@ -277,22 +277,24 @@ addActionHandler('openPollResults', (global, actions, payload) => {
}); });
}, POLL_RESULT_OPEN_DELAY_MS); }, POLL_RESULT_OPEN_DELAY_MS);
} else if (chatId !== global.pollResults.chatId || messageId !== global.pollResults.messageId) { } else if (chatId !== global.pollResults.chatId || messageId !== global.pollResults.messageId) {
setGlobal({ return {
...global, ...global,
pollResults: { pollResults: {
chatId, chatId,
messageId, messageId,
voters: {}, voters: {},
}, },
}); };
} }
return undefined;
}); });
addActionHandler('closePollResults', (global) => { addActionHandler('closePollResults', (global) => {
setGlobal({ return {
...global, ...global,
pollResults: {}, pollResults: {},
}); };
}); });
addActionHandler('focusLastMessage', (global, actions) => { addActionHandler('focusLastMessage', (global, actions) => {

View File

@ -1,6 +1,5 @@
import { addActionHandler } from '../../index'; import { addActionHandler } from '../../index';
import { GlobalState } from '../../types';
import { ApiError } from '../../../api/types'; import { ApiError } from '../../../api/types';
import { IS_SINGLE_COLUMN_LAYOUT, IS_TABLET_COLUMN_LAYOUT } from '../../../util/environment'; import { IS_SINGLE_COLUMN_LAYOUT, IS_TABLET_COLUMN_LAYOUT } from '../../../util/environment';
@ -33,7 +32,7 @@ addActionHandler('resetLeftColumnWidth', (global) => {
}; };
}); });
addActionHandler('toggleManagement', (global): GlobalState | undefined => { addActionHandler('toggleManagement', (global) => {
const { chatId } = selectCurrentMessageList(global) || {}; const { chatId } = selectCurrentMessageList(global) || {};
if (!chatId) { if (!chatId) {
@ -54,7 +53,7 @@ addActionHandler('toggleManagement', (global): GlobalState | undefined => {
}; };
}); });
addActionHandler('requestNextManagementScreen', (global, actions, payload): GlobalState | undefined => { addActionHandler('requestNextManagementScreen', (global, actions, payload) => {
const { screen } = payload || {}; const { screen } = payload || {};
const { chatId } = selectCurrentMessageList(global) || {}; const { chatId } = selectCurrentMessageList(global) || {};
@ -77,7 +76,7 @@ addActionHandler('requestNextManagementScreen', (global, actions, payload): Glob
}; };
}); });
addActionHandler('closeManagement', (global): GlobalState | undefined => { addActionHandler('closeManagement', (global) => {
const { chatId } = selectCurrentMessageList(global) || {}; const { chatId } = selectCurrentMessageList(global) || {};
if (!chatId) { if (!chatId) {

View File

@ -16,8 +16,9 @@ addActionHandler('openPaymentModal', (global, actions, payload) => {
}); });
addActionHandler('closePaymentModal', (global) => { addActionHandler('closePaymentModal', (global) => {
const newGlobal = clearPayment(global); global = clearPayment(global);
return closeInvoice(newGlobal); global = closeInvoice(global);
return global;
}); });
addActionHandler('addPaymentError', (global, actions, payload) => { addActionHandler('addPaymentError', (global, actions, payload) => {

View File

@ -202,7 +202,7 @@ export function selectThreadByMessage(global: GlobalState, chatId: string, messa
}); });
} }
export function isMessageInCurrentMessageList(global: GlobalState, chatId: string, message: ApiMessage) { export function selectIsMessageInCurrentMessageList(global: GlobalState, chatId: string, message: ApiMessage) {
const currentMessageList = selectCurrentMessageList(global); const currentMessageList = selectCurrentMessageList(global);
if (!currentMessageList) { if (!currentMessageList) {
return false; return false;

View File

@ -29,7 +29,7 @@ type ActionHandler = (
global: GlobalState, global: GlobalState,
actions: Actions, actions: Actions,
payload: any, payload: any,
) => GlobalState | void; ) => GlobalState | void | Promise<GlobalState | void>;
type MapStateToProps<OwnProps = undefined> = ((global: GlobalState, ownProps: OwnProps) => AnyLiteral); type MapStateToProps<OwnProps = undefined> = ((global: GlobalState, ownProps: OwnProps) => AnyLiteral);
@ -80,9 +80,19 @@ export function getActions() {
function handleAction(name: string, payload?: ActionPayload, options?: ActionOptions) { function handleAction(name: string, payload?: ActionPayload, options?: ActionOptions) {
actionHandlers[name]?.forEach((handler) => { actionHandlers[name]?.forEach((handler) => {
const newGlobal = handler(currentGlobal, actions, payload); const response = handler(currentGlobal, actions, payload);
if (newGlobal) { if (!response) {
setGlobal(newGlobal, options); return;
}
if (typeof response.then === 'function') {
response.then((newGlobal: GlobalState | void) => {
if (newGlobal) {
setGlobal(newGlobal, options);
}
});
} else {
setGlobal(response, options);
} }
}); });
} }
@ -244,7 +254,7 @@ export function typify<ProjectGlobalState, ActionPayloads, NonTypedActionNames e
global: ProjectGlobalState, global: ProjectGlobalState,
actions: ProjectActions, actions: ProjectActions,
payload: ProjectActionTypes[ActionName], payload: ProjectActionTypes[ActionName],
) => ProjectGlobalState | void; ) => ProjectGlobalState | void | Promise<ProjectGlobalState | void>;
}; };
return { return {