960 lines
24 KiB
TypeScript
960 lines
24 KiB
TypeScript
import {
|
|
addReducer, getDispatch, getGlobal, setGlobal,
|
|
} from '../../../lib/teact/teactn';
|
|
|
|
import {
|
|
ApiChat, ApiUser, ApiChatFolder, MAIN_THREAD_ID,
|
|
} from '../../../api/types';
|
|
import { ChatCreationProgress, ManagementProgress } from '../../../types';
|
|
import { GlobalActions } from '../../../global/types';
|
|
|
|
import {
|
|
ARCHIVED_FOLDER_ID,
|
|
TOP_CHAT_MESSAGES_PRELOAD_LIMIT,
|
|
CHAT_LIST_LOAD_SLICE,
|
|
RE_TME_INVITE_LINK,
|
|
RE_TME_LINK,
|
|
TIPS_USERNAME,
|
|
} from '../../../config';
|
|
import { callApi } from '../../../api/gramjs';
|
|
import {
|
|
addChats,
|
|
addUsers,
|
|
replaceThreadParam,
|
|
updateChatListIds,
|
|
updateChats,
|
|
updateChat,
|
|
updateChatListSecondaryInfo,
|
|
updateManagementProgress,
|
|
} from '../../reducers';
|
|
import {
|
|
selectChat,
|
|
selectCurrentChat,
|
|
selectUser,
|
|
selectChatListType,
|
|
selectIsChatPinned,
|
|
selectChatFolder,
|
|
selectSupportChat,
|
|
selectChatByUsername,
|
|
selectThreadTopMessageId,
|
|
selectCurrentMessageList,
|
|
} from '../../selectors';
|
|
import { buildCollectionByKey } from '../../../util/iteratees';
|
|
import { debounce, pause, throttle } from '../../../util/schedulers';
|
|
import {
|
|
isChatSummaryOnly, isChatArchived, prepareChatList, isChatBasicGroup,
|
|
} from '../../helpers';
|
|
|
|
const TOP_CHATS_PRELOAD_PAUSE = 100;
|
|
// We expect this ID does not exist
|
|
const TMP_CHAT_ID = -1;
|
|
|
|
const runThrottledForLoadChats = throttle((cb) => cb(), 1000, true);
|
|
const runThrottledForLoadTopChats = throttle((cb) => cb(), 3000, true);
|
|
const runDebouncedForLoadFullChat = debounce((cb) => cb(), 500, false, true);
|
|
|
|
addReducer('preloadTopChatMessages', (global, actions) => {
|
|
(async () => {
|
|
const preloadedChatIds: number[] = [];
|
|
|
|
for (let i = 0; i < TOP_CHAT_MESSAGES_PRELOAD_LIMIT; i++) {
|
|
await pause(TOP_CHATS_PRELOAD_PAUSE);
|
|
|
|
const {
|
|
byId,
|
|
listIds: { active: listIds },
|
|
orderedPinnedIds: { active: orderedPinnedIds },
|
|
} = getGlobal().chats;
|
|
if (!listIds) {
|
|
return;
|
|
}
|
|
|
|
const { chatId: currentChatId } = selectCurrentMessageList(global) || {};
|
|
const { pinnedChats, otherChats } = prepareChatList(byId, listIds, orderedPinnedIds);
|
|
const topChats = [...pinnedChats, ...otherChats];
|
|
const chatToPreload = topChats.find(({ id }) => id !== currentChatId && !preloadedChatIds.includes(id));
|
|
if (!chatToPreload) {
|
|
return;
|
|
}
|
|
|
|
preloadedChatIds.push(chatToPreload.id);
|
|
|
|
actions.loadViewportMessages({ chatId: chatToPreload.id, threadId: MAIN_THREAD_ID });
|
|
}
|
|
})();
|
|
});
|
|
|
|
addReducer('openChat', (global, actions, payload) => {
|
|
const { id, threadId } = payload!;
|
|
const { currentUserId } = global;
|
|
const chat = selectChat(global, id);
|
|
|
|
if (chat && chat.hasUnreadMark) {
|
|
actions.toggleChatUnread({ id });
|
|
}
|
|
|
|
if (!chat) {
|
|
if (id === currentUserId) {
|
|
void callApi('fetchChat', { type: 'self' });
|
|
} else {
|
|
const user = selectUser(global, id);
|
|
if (user) {
|
|
void callApi('fetchChat', { type: 'user', user });
|
|
}
|
|
}
|
|
} else if (isChatSummaryOnly(chat) && !chat.isMin) {
|
|
actions.requestChatUpdate({ chatId: id });
|
|
}
|
|
|
|
if (threadId !== MAIN_THREAD_ID) {
|
|
const topMessageId = selectThreadTopMessageId(global, id, threadId);
|
|
if (!topMessageId) {
|
|
actions.requestThreadInfoUpdate({ chatId: id, threadId });
|
|
}
|
|
}
|
|
});
|
|
|
|
addReducer('openSupportChat', (global, actions) => {
|
|
const chat = selectSupportChat(global);
|
|
|
|
actions.openChat({ id: chat ? chat.id : TMP_CHAT_ID });
|
|
|
|
if (chat) {
|
|
return;
|
|
}
|
|
|
|
(async () => {
|
|
const result = await callApi('fetchChat', { type: 'support' });
|
|
if (result) {
|
|
actions.openChat({ id: result.chatId });
|
|
}
|
|
})();
|
|
});
|
|
|
|
addReducer('openTipsChat', (global, actions) => {
|
|
actions.openChatByUsername({ username: TIPS_USERNAME });
|
|
});
|
|
|
|
addReducer('loadMoreChats', (global, actions, payload) => {
|
|
const { listType = 'active' } = payload!;
|
|
const listIds = global.chats.listIds[listType as ('active' | 'archived')];
|
|
const isFullyLoaded = global.chats.isFullyLoaded[listType as ('active' | 'archived')];
|
|
|
|
if (isFullyLoaded) {
|
|
return;
|
|
}
|
|
|
|
const oldestChat = listIds
|
|
? listIds
|
|
.map((id) => global.chats.byId[id])
|
|
.filter((chat) => Boolean(chat && chat.lastMessage) && !selectIsChatPinned(global, chat.id))
|
|
.sort((chat1, chat2) => (chat1.lastMessage!.date - chat2.lastMessage!.date))[0]
|
|
: undefined;
|
|
|
|
if (oldestChat) {
|
|
runThrottledForLoadChats(() => loadChats(listType, oldestChat.id, oldestChat.lastMessage!.date));
|
|
} else {
|
|
runThrottledForLoadChats(() => loadChats(listType));
|
|
}
|
|
});
|
|
|
|
addReducer('loadFullChat', (global, actions, payload) => {
|
|
const { chatId, force } = payload!;
|
|
const chat = selectChat(global, chatId);
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
if (force) {
|
|
loadFullChat(chat);
|
|
} else {
|
|
runDebouncedForLoadFullChat(() => loadFullChat(chat));
|
|
}
|
|
});
|
|
|
|
addReducer('loadTopChats', () => {
|
|
runThrottledForLoadTopChats(() => loadChats('active'));
|
|
});
|
|
|
|
addReducer('requestChatUpdate', (global, actions, payload) => {
|
|
const { chatId } = payload!;
|
|
const chat = selectChat(global, chatId);
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
void callApi('requestChatUpdate', chat);
|
|
});
|
|
|
|
addReducer('updateChatMutedState', (global, actions, payload) => {
|
|
const { chatId, isMuted } = payload!;
|
|
const chat = selectChat(global, chatId);
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
void callApi('updateChatMutedState', { chat, isMuted });
|
|
});
|
|
|
|
addReducer('createChannel', (global, actions, payload) => {
|
|
const {
|
|
title, about, photo, memberIds,
|
|
} = payload!;
|
|
|
|
const members = (memberIds as number[])
|
|
.map((id: number) => selectUser(global, id))
|
|
.filter<ApiUser>(Boolean as any);
|
|
|
|
void createChannel(title, members, about, photo);
|
|
});
|
|
|
|
addReducer('joinChannel', (global, actions, payload) => {
|
|
const { chatId } = payload!;
|
|
const chat = selectChat(global, chatId);
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
const { id: channelId, accessHash } = chat;
|
|
|
|
if (channelId && accessHash) {
|
|
void callApi('joinChannel', { channelId, accessHash });
|
|
}
|
|
});
|
|
|
|
addReducer('leaveChannel', (global, actions, payload) => {
|
|
(async () => {
|
|
const { chatId } = payload!;
|
|
const chat = selectChat(global, chatId);
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
const { id: channelId, accessHash } = chat;
|
|
|
|
if (channelId && accessHash) {
|
|
await callApi('leaveChannel', { channelId, accessHash });
|
|
}
|
|
|
|
actions.openChat({ id: undefined });
|
|
})();
|
|
});
|
|
|
|
addReducer('deleteChannel', (global, actions, payload) => {
|
|
(async () => {
|
|
const { chatId } = payload!;
|
|
const chat = selectChat(global, chatId);
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
const { id: channelId, accessHash } = chat;
|
|
|
|
if (channelId && accessHash) {
|
|
await callApi('deleteChannel', { channelId, accessHash });
|
|
}
|
|
|
|
actions.openChat({ id: undefined });
|
|
})();
|
|
});
|
|
|
|
addReducer('createGroupChat', (global, actions, payload) => {
|
|
const { title, memberIds, photo } = payload!;
|
|
const members = (memberIds as number[])
|
|
.map((id: number) => selectUser(global, id))
|
|
.filter<ApiUser>(Boolean as any);
|
|
|
|
void createGroupChat(title, members, photo);
|
|
});
|
|
|
|
addReducer('toggleChatPinned', (global, actions, payload) => {
|
|
const { id, folderId } = payload!;
|
|
const chat = selectChat(global, id);
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
if (folderId) {
|
|
const folder = selectChatFolder(global, folderId);
|
|
if (folder) {
|
|
const shouldBePinned = !selectIsChatPinned(global, id, folderId);
|
|
|
|
const { pinnedChatIds, includedChatIds } = folder;
|
|
const newPinnedIds = shouldBePinned
|
|
? [id, ...(pinnedChatIds || [])]
|
|
: (pinnedChatIds || []).filter((pinnedId) => pinnedId !== id);
|
|
|
|
// With both Pin and Unpin we need to re-add a user to the included group
|
|
const newIncludedChatIds = [id, ...includedChatIds];
|
|
|
|
void callApi('editChatFolder', {
|
|
id: folderId,
|
|
folderUpdate: {
|
|
...folder,
|
|
pinnedChatIds: newPinnedIds,
|
|
includedChatIds: newIncludedChatIds,
|
|
},
|
|
});
|
|
}
|
|
} else {
|
|
const listType = selectChatListType(global, id);
|
|
const isPinned = selectIsChatPinned(global, id, listType === 'archived' ? ARCHIVED_FOLDER_ID : undefined);
|
|
void callApi('toggleChatPinned', { chat, shouldBePinned: !isPinned });
|
|
}
|
|
});
|
|
|
|
addReducer('toggleChatArchived', (global, actions, payload) => {
|
|
const { id } = payload!;
|
|
const chat = selectChat(global, id);
|
|
if (chat) {
|
|
void callApi('toggleChatArchived', {
|
|
chat,
|
|
folderId: isChatArchived(chat) ? 0 : ARCHIVED_FOLDER_ID,
|
|
});
|
|
}
|
|
});
|
|
|
|
addReducer('loadChatFolders', () => {
|
|
void loadChatFolders();
|
|
});
|
|
|
|
addReducer('loadRecommendedChatFolders', () => {
|
|
void loadRecommendedChatFolders();
|
|
});
|
|
|
|
addReducer('editChatFolder', (global, actions, payload) => {
|
|
const { id, folderUpdate } = payload!;
|
|
const folder = selectChatFolder(global, id);
|
|
|
|
if (folder) {
|
|
void callApi('editChatFolder', {
|
|
id,
|
|
folderUpdate: {
|
|
id,
|
|
emoticon: folder.emoticon,
|
|
pinnedChatIds: folder.pinnedChatIds,
|
|
...folderUpdate,
|
|
},
|
|
});
|
|
}
|
|
});
|
|
|
|
addReducer('addChatFolder', (global, actions, payload) => {
|
|
const { folder } = payload!;
|
|
const { orderedIds } = global.chatFolders;
|
|
const maxId = orderedIds && orderedIds.length ? Math.max.apply(Math.max, orderedIds) : ARCHIVED_FOLDER_ID;
|
|
|
|
void createChatFolder(folder, maxId);
|
|
});
|
|
|
|
addReducer('deleteChatFolder', (global, actions, payload) => {
|
|
const { id } = payload!;
|
|
const folder = selectChatFolder(global, id);
|
|
|
|
if (folder) {
|
|
void deleteChatFolder(id);
|
|
}
|
|
});
|
|
|
|
addReducer('toggleChatUnread', (global, actions, payload) => {
|
|
const { id } = payload!;
|
|
const chat = selectChat(global, id);
|
|
if (chat) {
|
|
if (chat.unreadCount) {
|
|
void callApi('markMessageListRead', { chat, threadId: MAIN_THREAD_ID });
|
|
} else {
|
|
void callApi('toggleDialogUnread', {
|
|
chat,
|
|
hasUnreadMark: !chat.hasUnreadMark,
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
addReducer('openTelegramLink', (global, actions, payload) => {
|
|
const { url } = payload!;
|
|
let match = RE_TME_INVITE_LINK.exec(url);
|
|
|
|
if (match) {
|
|
const hash = match[1];
|
|
|
|
(async () => {
|
|
const chat = await callApi('openChatByInvite', hash);
|
|
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
actions.openChat({ id: chat.id });
|
|
})();
|
|
} else {
|
|
match = RE_TME_LINK.exec(url)!;
|
|
|
|
const username = match[1];
|
|
const channelPostId = match[2] ? Number(match[2]) : undefined;
|
|
|
|
void openChatByUsername(actions, username, channelPostId);
|
|
}
|
|
});
|
|
|
|
addReducer('openChatByUsername', (global, actions, payload) => {
|
|
const { username } = payload!;
|
|
|
|
void openChatByUsername(actions, username);
|
|
});
|
|
|
|
addReducer('togglePreHistoryHidden', (global, actions, payload) => {
|
|
const { chatId, isEnabled } = payload!;
|
|
let chat = selectChat(global, chatId);
|
|
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
(async () => {
|
|
if (isChatBasicGroup(chat)) {
|
|
chat = await callApi('migrateChat', chat);
|
|
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
actions.openChat({ id: chat.id });
|
|
}
|
|
|
|
void callApi('togglePreHistoryHidden', { chat, isEnabled });
|
|
})();
|
|
});
|
|
|
|
addReducer('updateChatDefaultBannedRights', (global, actions, payload) => {
|
|
const { chatId, bannedRights } = payload!;
|
|
const chat = selectChat(global, chatId);
|
|
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
void callApi('updateChatDefaultBannedRights', { chat, bannedRights });
|
|
});
|
|
|
|
addReducer('updateChatMemberBannedRights', (global, actions, payload) => {
|
|
const { chatId, userId, bannedRights } = payload!;
|
|
let chat = selectChat(global, chatId);
|
|
const user = selectUser(global, userId);
|
|
|
|
if (!chat || !user) {
|
|
return;
|
|
}
|
|
|
|
(async () => {
|
|
if (isChatBasicGroup(chat)) {
|
|
chat = await callApi('migrateChat', chat);
|
|
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
actions.openChat({ id: chat.id });
|
|
}
|
|
|
|
await callApi('updateChatMemberBannedRights', { chat, user, bannedRights });
|
|
|
|
const newGlobal = getGlobal();
|
|
const chatAfterUpdate = selectChat(newGlobal, chatId);
|
|
|
|
if (!chatAfterUpdate || !chatAfterUpdate.fullInfo) {
|
|
return;
|
|
}
|
|
|
|
const { members, kickedMembers } = chatAfterUpdate.fullInfo;
|
|
|
|
const isBanned = !!bannedRights.viewMessages;
|
|
const isUnblocked = !Object.keys(bannedRights).length;
|
|
|
|
setGlobal(updateChat(newGlobal, chatId, {
|
|
fullInfo: {
|
|
...chatAfterUpdate.fullInfo,
|
|
...(members && isBanned && {
|
|
members: members.filter((m) => m.userId !== userId),
|
|
}),
|
|
...(members && !isBanned && {
|
|
members: members.map((m) => (
|
|
m.userId === userId
|
|
? { ...m, bannedRights }
|
|
: m
|
|
)),
|
|
}),
|
|
...(isUnblocked && kickedMembers && {
|
|
kickedMembers: kickedMembers.filter((m) => m.userId !== userId),
|
|
}),
|
|
},
|
|
}));
|
|
})();
|
|
});
|
|
|
|
addReducer('updateChatAdmin', (global, actions, payload) => {
|
|
const {
|
|
chatId, userId, adminRights, customTitle,
|
|
} = payload!;
|
|
let chat = selectChat(global, chatId);
|
|
const user = selectUser(global, userId);
|
|
|
|
if (!chat || !user) {
|
|
return;
|
|
}
|
|
|
|
(async () => {
|
|
if (isChatBasicGroup(chat)) {
|
|
chat = await callApi('migrateChat', chat);
|
|
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
actions.openChat({ id: chat.id });
|
|
}
|
|
|
|
await callApi('updateChatAdmin', {
|
|
chat, user, adminRights, customTitle,
|
|
});
|
|
|
|
const newGlobal = getGlobal();
|
|
const chatAfterUpdate = selectChat(newGlobal, chatId);
|
|
|
|
if (!chatAfterUpdate || !chatAfterUpdate.fullInfo) {
|
|
return;
|
|
}
|
|
|
|
const { adminMembers } = chatAfterUpdate.fullInfo;
|
|
|
|
const isDismissed = !Object.keys(adminRights).length;
|
|
|
|
setGlobal(updateChat(newGlobal, chatId, {
|
|
fullInfo: {
|
|
...chatAfterUpdate.fullInfo,
|
|
...(adminMembers && isDismissed && {
|
|
adminMembers: adminMembers.filter((m) => m.userId !== userId),
|
|
}),
|
|
...(adminMembers && !isDismissed && {
|
|
adminMembers: adminMembers.map((m) => (
|
|
m.userId === userId
|
|
? { ...m, adminRights, customTitle }
|
|
: m
|
|
)),
|
|
}),
|
|
},
|
|
}));
|
|
})();
|
|
});
|
|
|
|
addReducer('updateChat', (global, actions, payload) => {
|
|
const {
|
|
chatId, title, about, photo,
|
|
} = payload!;
|
|
|
|
const chat = selectChat(global, chatId);
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
(async () => {
|
|
setGlobal(updateManagementProgress(getGlobal(), ManagementProgress.InProgress));
|
|
|
|
await Promise.all([
|
|
chat.title !== title
|
|
? callApi('updateChatTitle', chat, title)
|
|
: undefined,
|
|
chat.fullInfo && chat.fullInfo.about !== about
|
|
? callApi('updateChatAbout', chat, about)
|
|
: undefined,
|
|
photo
|
|
? callApi('editChatPhoto', { chatId, accessHash: chat.accessHash, photo })
|
|
: undefined,
|
|
]);
|
|
|
|
setGlobal(updateManagementProgress(getGlobal(), ManagementProgress.Complete));
|
|
})();
|
|
});
|
|
|
|
addReducer('toggleSignatures', (global, actions, payload) => {
|
|
const { chatId, isEnabled } = payload!;
|
|
const chat = selectChat(global, chatId);
|
|
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
void callApi('toggleSignatures', { chat, isEnabled });
|
|
});
|
|
|
|
addReducer('loadGroupsForDiscussion', () => {
|
|
(async () => {
|
|
const groups = await callApi('fetchGroupsForDiscussion');
|
|
if (!groups) {
|
|
return;
|
|
}
|
|
|
|
const addedById = groups.reduce((result, group) => {
|
|
if (group) {
|
|
result[group.id] = group;
|
|
}
|
|
|
|
return result;
|
|
}, {} as Record<number, ApiChat>);
|
|
|
|
const global = addChats(getGlobal(), addedById);
|
|
setGlobal({
|
|
...global,
|
|
chats: {
|
|
...global.chats,
|
|
forDiscussionIds: Object.keys(addedById).map(Number),
|
|
},
|
|
});
|
|
})();
|
|
});
|
|
|
|
addReducer('linkDiscussionGroup', (global, actions, payload) => {
|
|
const { channelId, chatId } = payload!;
|
|
|
|
const channel = selectChat(global, channelId);
|
|
let chat = selectChat(global, chatId);
|
|
if (!channel || !chat) {
|
|
return;
|
|
}
|
|
|
|
(async () => {
|
|
if (isChatBasicGroup(chat)) {
|
|
chat = await callApi('migrateChat', chat);
|
|
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
|
|
actions.openChat({ id: chat.id });
|
|
}
|
|
|
|
let { fullInfo } = chat;
|
|
if (!fullInfo) {
|
|
const fullChat = await callApi('fetchFullChat', chat);
|
|
if (!fullChat) {
|
|
return;
|
|
}
|
|
|
|
fullInfo = fullChat.fullInfo;
|
|
}
|
|
|
|
if (fullInfo.isPreHistoryHidden) {
|
|
await callApi('togglePreHistoryHidden', { chat, isEnabled: false });
|
|
}
|
|
|
|
void callApi('setDiscussionGroup', { channel, chat });
|
|
})();
|
|
});
|
|
|
|
addReducer('unlinkDiscussionGroup', (global, actions, payload) => {
|
|
const { channelId } = payload!;
|
|
|
|
const channel = selectChat(global, channelId);
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
|
|
let chat: ApiChat | undefined;
|
|
if (channel.fullInfo && channel.fullInfo.linkedChatId) {
|
|
chat = selectChat(global, channel.fullInfo.linkedChatId);
|
|
}
|
|
|
|
(async () => {
|
|
await callApi('setDiscussionGroup', { channel });
|
|
if (chat) {
|
|
loadFullChat(chat);
|
|
}
|
|
})();
|
|
});
|
|
|
|
|
|
addReducer('setActiveChatFolder', (global, actions, payload) => {
|
|
return {
|
|
...global,
|
|
chatFolders: {
|
|
...global.chatFolders,
|
|
activeChatFolder: payload,
|
|
},
|
|
};
|
|
});
|
|
|
|
addReducer('loadMoreMembers', (global) => {
|
|
(async () => {
|
|
const { chatId } = selectCurrentMessageList(global) || {};
|
|
const chat = chatId ? selectChat(global, chatId) : undefined;
|
|
if (!chat || isChatBasicGroup(chat)) {
|
|
return;
|
|
}
|
|
|
|
const offset = (chat.fullInfo && chat.fullInfo.members && chat.fullInfo.members.length) || undefined;
|
|
const result = await callApi('fetchMembers', chat.id, chat.accessHash!, 'recent', offset);
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
const { members, users } = result;
|
|
if (!members || !members.length) {
|
|
return;
|
|
}
|
|
|
|
global = getGlobal();
|
|
global = addUsers(global, buildCollectionByKey(users, 'id'));
|
|
global = updateChat(global, chat.id, {
|
|
fullInfo: {
|
|
...chat.fullInfo,
|
|
members: [
|
|
...((chat.fullInfo || {}).members || []),
|
|
...(members || []),
|
|
],
|
|
},
|
|
});
|
|
setGlobal(global);
|
|
})();
|
|
});
|
|
|
|
async function loadChats(listType: 'active' | 'archived', offsetId?: number, offsetDate?: number) {
|
|
const result = await callApi('fetchChats', {
|
|
limit: CHAT_LIST_LOAD_SLICE,
|
|
offsetDate,
|
|
archived: listType === 'archived',
|
|
withPinned: getGlobal().chats.orderedPinnedIds[listType] === undefined,
|
|
});
|
|
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
const { chatIds } = result;
|
|
|
|
if (chatIds.length > 0 && chatIds[0] === offsetId) {
|
|
chatIds.shift();
|
|
}
|
|
|
|
let global = getGlobal();
|
|
|
|
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
|
|
global = updateChats(global, buildCollectionByKey(result.chats, 'id'));
|
|
global = updateChatListIds(global, listType, chatIds);
|
|
global = updateChatListSecondaryInfo(global, listType, result);
|
|
|
|
Object.keys(result.draftsById).map(Number).forEach((chatId) => {
|
|
global = replaceThreadParam(
|
|
global, chatId, MAIN_THREAD_ID, 'draft', result.draftsById[chatId],
|
|
);
|
|
});
|
|
|
|
Object.keys(result.replyingToById).map(Number).forEach((chatId) => {
|
|
global = replaceThreadParam(
|
|
global, chatId, MAIN_THREAD_ID, 'replyingToId', result.replyingToById[chatId],
|
|
);
|
|
});
|
|
|
|
|
|
if (chatIds.length === 0 && !global.chats.isFullyLoaded[listType]) {
|
|
global = {
|
|
...global,
|
|
chats: {
|
|
...global.chats,
|
|
isFullyLoaded: {
|
|
...global.chats.isFullyLoaded,
|
|
[listType]: true,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
setGlobal(global);
|
|
}
|
|
|
|
async function loadFullChat(chat: ApiChat) {
|
|
const result = await callApi('fetchFullChat', chat);
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
const { users, fullInfo } = result;
|
|
|
|
let global = getGlobal();
|
|
if (users) {
|
|
global = addUsers(global, buildCollectionByKey(users, 'id'));
|
|
}
|
|
global = updateChat(global, chat.id, { fullInfo });
|
|
|
|
setGlobal(global);
|
|
}
|
|
|
|
async function createChannel(title: string, users: ApiUser[], about?: string, photo?: File) {
|
|
setGlobal({
|
|
...getGlobal(),
|
|
chatCreation: {
|
|
progress: ChatCreationProgress.InProgress,
|
|
},
|
|
});
|
|
|
|
const createdChannel = await callApi('createChannel', { title, about, users });
|
|
if (!createdChannel) {
|
|
return;
|
|
}
|
|
|
|
const { id: channelId, accessHash } = createdChannel;
|
|
|
|
let global = getGlobal();
|
|
global = updateChat(global, channelId, createdChannel);
|
|
global = {
|
|
...global,
|
|
chatCreation: {
|
|
...global.chatCreation,
|
|
progress: createdChannel ? ChatCreationProgress.Complete : ChatCreationProgress.Error,
|
|
},
|
|
};
|
|
setGlobal(global);
|
|
getDispatch().openChat({ id: channelId });
|
|
|
|
if (channelId && accessHash && photo) {
|
|
await callApi('editChatPhoto', { chatId: channelId, accessHash, photo });
|
|
}
|
|
}
|
|
|
|
async function createGroupChat(title: string, users: ApiUser[], photo?: File) {
|
|
setGlobal({
|
|
...getGlobal(),
|
|
chatCreation: {
|
|
progress: ChatCreationProgress.InProgress,
|
|
},
|
|
});
|
|
|
|
const createdChat = await callApi('createGroupChat', { title, users });
|
|
if (!createdChat) {
|
|
return;
|
|
}
|
|
|
|
const { id: chatId } = createdChat;
|
|
|
|
let global = getGlobal();
|
|
global = updateChat(global, chatId, createdChat);
|
|
global = {
|
|
...global,
|
|
chatCreation: {
|
|
...global.chatCreation,
|
|
progress: createdChat ? ChatCreationProgress.Complete : ChatCreationProgress.Error,
|
|
},
|
|
};
|
|
setGlobal(global);
|
|
getDispatch().openChat({ id: chatId });
|
|
|
|
if (chatId && photo) {
|
|
await callApi('editChatPhoto', { chatId, photo });
|
|
}
|
|
}
|
|
|
|
async function loadChatFolders() {
|
|
const chatFolders = await callApi('fetchChatFolders');
|
|
|
|
if (chatFolders) {
|
|
const global = getGlobal();
|
|
|
|
setGlobal({
|
|
...global,
|
|
chatFolders: {
|
|
...global.chatFolders,
|
|
...chatFolders,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
async function loadRecommendedChatFolders() {
|
|
const recommendedChatFolders = await callApi('fetchRecommendedChatFolders');
|
|
|
|
if (recommendedChatFolders) {
|
|
const global = getGlobal();
|
|
|
|
setGlobal({
|
|
...global,
|
|
chatFolders: {
|
|
...global.chatFolders,
|
|
recommended: recommendedChatFolders,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
async function createChatFolder(folder: ApiChatFolder, maxId: number) {
|
|
// Clear fields from recommended folders
|
|
const { id: recommendedId, description, ...newFolder } = folder;
|
|
|
|
await callApi('editChatFolder', {
|
|
id: maxId + 1,
|
|
folderUpdate: {
|
|
id: maxId + 1,
|
|
...newFolder,
|
|
},
|
|
});
|
|
|
|
if (!description) {
|
|
return;
|
|
}
|
|
|
|
const global = getGlobal();
|
|
const { recommended } = global.chatFolders;
|
|
|
|
if (recommended) {
|
|
setGlobal({
|
|
...global,
|
|
chatFolders: {
|
|
...global.chatFolders,
|
|
recommended: recommended.filter(({ id }) => id !== recommendedId),
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
async function deleteChatFolder(id: number) {
|
|
await callApi('deleteChatFolder', id);
|
|
}
|
|
|
|
async function openChatByUsername(
|
|
actions: GlobalActions,
|
|
username: string,
|
|
channelPostId?: number,
|
|
) {
|
|
const global = getGlobal();
|
|
const localChat = selectChatByUsername(global, username);
|
|
if (localChat && !localChat.isMin) {
|
|
if (channelPostId) {
|
|
actions.focusMessage({ chatId: localChat.id, messageId: channelPostId });
|
|
} else {
|
|
actions.openChat({ id: localChat.id });
|
|
}
|
|
return;
|
|
}
|
|
|
|
const previousChat = selectCurrentChat(global);
|
|
// Open temporary empty chat to make the click response feel faster
|
|
actions.openChat({ id: TMP_CHAT_ID });
|
|
|
|
const chat = await callApi('getChatByUsername', username);
|
|
if (!chat) {
|
|
if (previousChat) {
|
|
actions.openChat({ id: previousChat.id });
|
|
}
|
|
|
|
actions.showNotification({ message: 'User does not exist' });
|
|
|
|
return;
|
|
}
|
|
|
|
setGlobal(updateChat(getGlobal(), chat.id, chat));
|
|
|
|
if (channelPostId) {
|
|
actions.focusMessage({ chatId: chat.id, messageId: channelPostId });
|
|
} else {
|
|
actions.openChat({ id: chat.id });
|
|
}
|
|
}
|