2022-03-19 21:20:02 +01:00

323 lines
8.5 KiB
TypeScript

import {
addActionHandler, getActions, getGlobal, setGlobal,
} from '../../index';
import { ApiUser } from '../../../api/types';
import { ManagementProgress } from '../../../types';
import { debounce, throttle } from '../../../util/schedulers';
import { buildCollectionByKey, unique } from '../../../util/iteratees';
import { isUserBot, isUserId } from '../../helpers';
import { callApi } from '../../../api/gramjs';
import { selectChat, selectCurrentMessageList, selectUser } from '../../selectors';
import {
addChats, addUsers, closeNewContactDialog, replaceUserStatuses, updateChat, updateManagementProgress, updateUser,
updateUsers, updateUserSearch, updateUserSearchFetchingStatus,
} from '../../reducers';
import { getServerTime } from '../../../util/serverTime';
import * as langProvider from '../../../util/langProvider';
const runDebouncedForFetchFullUser = debounce((cb) => cb(), 500, false, true);
const TOP_PEERS_REQUEST_COOLDOWN = 60; // 1 min
const runThrottledForSearch = throttle((cb) => cb(), 500, false);
addActionHandler('loadFullUser', (global, actions, payload) => {
const { userId } = payload!;
const user = selectUser(global, userId);
if (!user) {
return;
}
const { id, accessHash } = user;
runDebouncedForFetchFullUser(() => callApi('fetchFullUser', { id, accessHash }));
});
addActionHandler('loadUser', async (global, actions, payload) => {
const { userId } = payload!;
const user = selectUser(global, userId);
if (!user) {
return undefined;
}
const result = await callApi('fetchUsers', { users: [user] });
if (!result) {
return undefined;
}
const { users, userStatusesById } = result;
global = getGlobal();
global = updateUsers(global, buildCollectionByKey(users, 'id'));
global = replaceUserStatuses(global, {
...global.users.statusesById,
...userStatusesById,
});
return global;
});
addActionHandler('loadTopUsers', (global) => {
const { topPeers: { lastRequestedAt } } = global;
if (!lastRequestedAt || getServerTime(global.serverTimeOffset) - lastRequestedAt > TOP_PEERS_REQUEST_COOLDOWN) {
void loadTopUsers();
}
});
addActionHandler('loadContactList', () => {
void loadContactList();
});
addActionHandler('loadCurrentUser', () => {
void callApi('fetchCurrentUser');
});
addActionHandler('loadCommonChats', async (global) => {
const { chatId } = selectCurrentMessageList(global) || {};
const user = chatId ? selectUser(global, chatId) : undefined;
if (!user || isUserBot(user) || user.commonChats?.isFullyLoaded) {
return undefined;
}
const maxId = user.commonChats?.maxId;
const result = await callApi('fetchCommonChats', user.id, user.accessHash!, maxId);
if (!result) {
return undefined;
}
const { chats, chatIds, isFullyLoaded } = result;
global = getGlobal();
if (chats.length) {
global = addChats(global, buildCollectionByKey(chats, 'id'));
}
global = updateUser(global, user.id, {
commonChats: {
maxId: chatIds.length ? chatIds[chatIds.length - 1] : '0',
ids: unique((user.commonChats?.ids || []).concat(chatIds)),
isFullyLoaded,
},
});
return global;
});
addActionHandler('updateContact', (global, actions, payload) => {
const {
userId, isMuted = false, firstName, lastName, shouldSharePhoneNumber,
} = payload;
void updateContact(userId, isMuted, firstName, lastName, shouldSharePhoneNumber);
});
addActionHandler('deleteContact', (global, actions, payload) => {
const { userId } = payload!;
void deleteContact(userId);
});
async function loadTopUsers() {
const result = await callApi('fetchTopUsers');
if (!result) {
return;
}
const { ids, users } = result;
let global = getGlobal();
global = addUsers(global, buildCollectionByKey(users, 'id'));
global = {
...global,
topPeers: {
...global.topPeers,
userIds: ids,
lastRequestedAt: getServerTime(global.serverTimeOffset),
},
};
setGlobal(global);
}
async function loadContactList() {
const contactList = await callApi('fetchContactList');
if (!contactList) {
return;
}
let global = addUsers(getGlobal(), buildCollectionByKey(contactList.users, 'id'));
global = addChats(global, buildCollectionByKey(contactList.chats, 'id'));
// Sort contact list by Last Name (or First Name), with latin names being placed first
const getCompareString = (user: ApiUser) => (user.lastName || user.firstName || '');
const collator = new Intl.Collator('en-US');
const sortedUsers = contactList.users.sort((a, b) => (
collator.compare(getCompareString(a), getCompareString(b))
)).filter((user) => !user.isSelf);
setGlobal({
...global,
contactList: {
userIds: sortedUsers.map((user) => user.id),
},
});
}
async function updateContact(
userId: string,
isMuted: boolean,
firstName: string,
lastName?: string,
shouldSharePhoneNumber?: boolean,
) {
let global = getGlobal();
const user = selectUser(global, userId);
if (!user) {
return;
}
getActions().updateChatMutedState({ chatId: userId, isMuted });
setGlobal(updateManagementProgress(getGlobal(), ManagementProgress.InProgress));
let result;
if (!user.isContact && user.phoneNumber) {
result = await callApi('importContact', { phone: user.phoneNumber, firstName, lastName });
} else {
const { id, accessHash } = user;
result = await callApi('updateContact', {
id,
accessHash,
phoneNumber: '',
firstName,
lastName,
shouldSharePhoneNumber,
});
}
global = getGlobal();
if (result) {
setGlobal(updateUser(
global,
user.id,
{
firstName,
lastName,
},
));
}
global = updateManagementProgress(global, ManagementProgress.Complete);
global = closeNewContactDialog(global);
setGlobal(global);
}
async function deleteContact(userId: string) {
const global = getGlobal();
const user = selectUser(global, userId);
if (!user) {
return;
}
const { id, accessHash } = user;
await callApi('deleteContact', { id, accessHash });
}
addActionHandler('loadProfilePhotos', async (global, actions, payload) => {
const { profileId } = payload!;
const isPrivate = isUserId(profileId);
const user = isPrivate ? selectUser(global, profileId) : undefined;
const chat = !isPrivate ? selectChat(global, profileId) : undefined;
if (!user && !chat) {
return undefined;
}
const result = await callApi('fetchProfilePhotos', user, chat);
if (!result || !result.photos) {
return undefined;
}
global = getGlobal();
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) => {
const { query } = payload!;
if (!query) return;
void runThrottledForSearch(() => {
searchUsers(query);
});
});
addActionHandler('importContact', async (global, actions, payload) => {
const { phoneNumber: phone, firstName, lastName } = payload!;
const result = await callApi('importContact', { phone, firstName, lastName });
if (result) {
actions.openChat({ id: result });
return closeNewContactDialog(getGlobal());
}
actions.showNotification({
message: langProvider.getTranslation('Contacts.PhoneNumber.NotRegistred'),
});
return undefined;
});
addActionHandler('reportSpam', (global, actions, payload) => {
const { chatId } = payload!;
const userOrChat = isUserId(chatId) ? selectUser(global, chatId) : selectChat(global, chatId);
if (!userOrChat) {
return;
}
void callApi('reportSpam', userOrChat);
});
async function searchUsers(query: string) {
const result = await callApi('searchChats', { query });
let global = getGlobal();
const currentSearchQuery = global.userSearch.query;
if (!result || !currentSearchQuery || (query !== currentSearchQuery)) {
setGlobal(updateUserSearchFetchingStatus(global, false));
return;
}
const { localUsers, globalUsers } = result;
let localUserIds;
let globalUserIds;
if (localUsers.length) {
global = addUsers(global, buildCollectionByKey(localUsers, 'id'));
localUserIds = localUsers.map(({ id }) => id);
}
if (globalUsers.length) {
global = addUsers(global, buildCollectionByKey(globalUsers, 'id'));
globalUserIds = globalUsers.map(({ id }) => id);
}
global = updateUserSearchFetchingStatus(global, false);
global = updateUserSearch(global, { localUserIds, globalUserIds });
setGlobal(global);
}