diff --git a/src/api/gramjs/methods/chats.ts b/src/api/gramjs/methods/chats.ts index 579adc383..29954a215 100644 --- a/src/api/gramjs/methods/chats.ts +++ b/src/api/gramjs/methods/chats.ts @@ -12,6 +12,7 @@ import { ApiChatBannedRights, ApiChatAdminRights, ApiGroupCall, + ApiUserStatus, } from '../../types'; import { @@ -338,6 +339,7 @@ export function clearDraft(chat: ApiChat) { async function getFullChatInfo(chatId: string): Promise<{ fullInfo: ApiChatFullInfo; users?: ApiUser[]; + userStatusesById?: { [userId: string]: ApiUserStatus }; groupCall?: Partial; membersCount?: number; } | undefined> { @@ -365,6 +367,7 @@ async function getFullChatInfo(chatId: string): Promise<{ const members = buildChatMembers(participants); const adminMembers = members ? members.filter(({ isAdmin, isOwner }) => isAdmin || isOwner) : undefined; const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined; + const { users, userStatusesById } = buildApiUsersAndStatuses(result.users); return { fullInfo: { @@ -381,7 +384,8 @@ async function getFullChatInfo(chatId: string): Promise<{ requestsPending, recentRequesterIds: recentRequesters?.map((userId) => buildApiPeerId(userId, 'user')), }, - users: result.users.map(buildApiUser).filter(Boolean as any), + users, + userStatusesById, groupCall: call ? { chatId, isLoaded: false, @@ -403,6 +407,7 @@ async function getFullChannelInfo( ): Promise<{ fullInfo: ApiChatFullInfo; users?: ApiUser[]; + userStatusesById: { [userId: string]: ApiUserStatus }; groupCall?: Partial; membersCount?: number; } | undefined> { @@ -440,11 +445,11 @@ async function getFullChannelInfo( ? exportedInvite.link : undefined; - const { members, users } = (canViewParticipants && await fetchMembers(id, accessHash)) || {}; - const { members: kickedMembers, users: bannedUsers } = ( + const { members, users, userStatusesById } = (canViewParticipants && await fetchMembers(id, accessHash)) || {}; + const { members: kickedMembers, users: bannedUsers, userStatusesById: bannedStatusesById } = ( canViewParticipants && adminRights && await fetchMembers(id, accessHash, 'kicked') ) || {}; - const { members: adminMembers, users: adminUsers } = ( + const { members: adminMembers, users: adminUsers, userStatusesById: adminStatusesById } = ( canViewParticipants && adminRights && await fetchMembers(id, accessHash, 'admin') ) || {}; const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined; @@ -463,6 +468,12 @@ async function getFullChannelInfo( } } + const statusesById = { + ...userStatusesById, + ...bannedStatusesById, + ...adminStatusesById, + }; + return { fullInfo: { about, @@ -492,6 +503,7 @@ async function getFullChannelInfo( statisticsDcId: statsDc, }, users: [...(users || []), ...(bannedUsers || []), ...(adminUsers || [])], + userStatusesById: statusesById, groupCall: call ? { chatId: id, isLoaded: false, diff --git a/src/api/gramjs/methods/users.ts b/src/api/gramjs/methods/users.ts index a1ba309a0..dafde5832 100644 --- a/src/api/gramjs/methods/users.ts +++ b/src/api/gramjs/methods/users.ts @@ -118,8 +118,11 @@ export async function fetchContactList() { } }); + const { users, userStatusesById } = buildApiUsersAndStatuses(result.users); + return { - users: result.users.map(buildApiUser).filter(Boolean as any), + users, + userStatusesById, chats: result.users.map((user) => buildApiChatFromPreview(user)).filter(Boolean as any), }; } diff --git a/src/components/left/main/ContactList.tsx b/src/components/left/main/ContactList.tsx index c0a43289f..2d92de563 100644 --- a/src/components/left/main/ContactList.tsx +++ b/src/components/left/main/ContactList.tsx @@ -1,12 +1,11 @@ import React, { - FC, useEffect, useCallback, useMemo, memo, + FC, useCallback, useMemo, memo, } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import { ApiUser, ApiUserStatus } from '../../../api/types'; import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment'; -import { throttle } from '../../../util/schedulers'; import { filterUsersByName, sortUserIds } from '../../../global/helpers'; import useInfiniteScroll from '../../../hooks/useInfiniteScroll'; import useHistoryBack from '../../../hooks/useHistoryBack'; @@ -31,8 +30,6 @@ type StateProps = { serverTimeOffset: number; }; -const runThrottled = throttle((cb) => cb(), 60000, true); - const ContactList: FC = ({ isActive, filter, @@ -43,21 +40,12 @@ const ContactList: FC = ({ onReset, }) => { const { - loadContactList, openChat, openNewContactDialog, } = getActions(); const lang = useLang(); - // Due to the parent Transition, this component never gets unmounted, - // that's why we use throttled API call on every update. - useEffect(() => { - runThrottled(() => { - loadContactList(); - }); - }); - useHistoryBack({ isActive, onBack: onReset, diff --git a/src/components/left/newChat/NewChatStep1.tsx b/src/components/left/newChat/NewChatStep1.tsx index 3a2324e84..ea20aad40 100644 --- a/src/components/left/newChat/NewChatStep1.tsx +++ b/src/components/left/newChat/NewChatStep1.tsx @@ -1,12 +1,11 @@ import React, { - FC, useCallback, useEffect, useMemo, memo, + FC, useCallback, useMemo, memo, } from '../../../lib/teact/teact'; import { getActions, getGlobal, withGlobal } from '../../../global'; import { ApiChat } from '../../../api/types'; import { unique } from '../../../util/iteratees'; -import { throttle } from '../../../util/schedulers'; import { filterUsersByName, isUserBot, sortChatIds } from '../../../global/helpers'; import useLang from '../../../hooks/useLang'; import useHistoryBack from '../../../hooks/useHistoryBack'; @@ -33,8 +32,6 @@ type StateProps = { globalUserIds?: string[]; }; -const runThrottled = throttle((cb) => cb(), 60000, true); - const NewChatStep1: FC = ({ isChannel, isActive, @@ -50,18 +47,9 @@ const NewChatStep1: FC = ({ globalUserIds, }) => { const { - loadContactList, setGlobalSearchQuery, } = getActions(); - // Due to the parent Transition, this component never gets unmounted, - // that's why we use throttled API call on every update. - useEffect(() => { - runThrottled(() => { - loadContactList(); - }); - }); - const lang = useLang(); useHistoryBack({ diff --git a/src/components/left/search/RecentContacts.tsx b/src/components/left/search/RecentContacts.tsx index 2fda1dff4..071b3740a 100644 --- a/src/components/left/search/RecentContacts.tsx +++ b/src/components/left/search/RecentContacts.tsx @@ -37,7 +37,7 @@ const RecentContacts: FC = ({ onReset, }) => { const { - loadTopUsers, loadContactList, openChat, + loadTopUsers, openChat, addRecentlyFoundChatId, clearRecentlyFoundChats, } = getActions(); @@ -49,10 +49,8 @@ const RecentContacts: FC = ({ useEffect(() => { runThrottled(() => { loadTopUsers(); - // Loading full contact list for quick local search before user enters the query - loadContactList(); }); - }, [loadTopUsers, loadContactList]); + }, [loadTopUsers]); useHorizontalScroll(topUsersRef.current, !topUserIds); diff --git a/src/components/left/settings/BlockUserModal.tsx b/src/components/left/settings/BlockUserModal.tsx index 1df84603c..1a8804bd7 100644 --- a/src/components/left/settings/BlockUserModal.tsx +++ b/src/components/left/settings/BlockUserModal.tsx @@ -34,7 +34,6 @@ const BlockUserModal: FC = ({ onClose, }) => { const { - loadContactList, setUserSearchQuery, blockContact, } = getActions(); @@ -82,7 +81,6 @@ const BlockUserModal: FC = ({ filterPlaceholder={lang('BlockedUsers.BlockUser')} filter={filter} onFilterChange={setFilter} - loadMore={loadContactList} onSelectChatOrUser={handleRemoveUser} onClose={onClose} /> diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index e7f8c73b4..dc64b4470 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -152,6 +152,7 @@ const Main: FC = ({ checkVersionNotification, loadAppConfig, loadAttachMenuBots, + loadContactList, } = getActions(); if (DEBUG && !DEBUG_isLogged) { @@ -178,10 +179,11 @@ const Main: FC = ({ loadTopInlineBots(); loadEmojiKeywords({ language: BASE_EMOJI_KEYWORD_LANG }); loadAttachMenuBots(); + loadContactList(); } }, [ lastSyncTime, loadAnimatedEmojis, loadEmojiKeywords, loadNotificationExceptions, loadNotificationSettings, - loadTopInlineBots, updateIsOnline, loadAvailableReactions, loadAppConfig, loadAttachMenuBots, + loadTopInlineBots, updateIsOnline, loadAvailableReactions, loadAppConfig, loadAttachMenuBots, loadContactList, ]); // Language-based API calls diff --git a/src/components/right/AddChatMembers.tsx b/src/components/right/AddChatMembers.tsx index ea8e2b7b6..333cdd1eb 100644 --- a/src/components/right/AddChatMembers.tsx +++ b/src/components/right/AddChatMembers.tsx @@ -1,10 +1,10 @@ import React, { - FC, useCallback, useMemo, memo, useState, useEffect, + FC, useCallback, useMemo, memo, useState, } from '../../lib/teact/teact'; import { getActions, getGlobal, withGlobal } from '../../global'; import { - ApiChat, ApiChatMember, ApiUpdateConnectionStateType, + ApiChat, ApiChatMember, } from '../../api/types'; import { NewChatMembersProgress } from '../../types'; @@ -31,7 +31,6 @@ export type OwnProps = { }; type StateProps = { - connectionState?: ApiUpdateConnectionStateType; isChannel?: boolean; members?: ApiChatMember[]; currentUserId?: string; @@ -46,7 +45,6 @@ type StateProps = { const AddChatMembers: FC = ({ isChannel, - connectionState, members, onNextStep, currentUserId, @@ -60,19 +58,13 @@ const AddChatMembers: FC = ({ onClose, isActive, }) => { - const { setUserSearchQuery, loadContactList } = getActions(); + const { setUserSearchQuery } = getActions(); const lang = useLang(); const [selectedMemberIds, setSelectedMemberIds] = useState([]); const prevSelectedMemberIds = usePrevious(selectedMemberIds); const noPickerScrollRestore = prevSelectedMemberIds === selectedMemberIds; - useEffect(() => { - if (isActive && connectionState === 'connectionStateReady') { - loadContactList(); - } - }, [connectionState, isActive, loadContactList]); - useHistoryBack({ isActive, onBack: onClose, @@ -160,7 +152,7 @@ export default memo(withGlobal( const chat = selectChat(global, chatId); const { userIds: localContactIds } = global.contactList || {}; const { byId: chatsById } = global.chats; - const { currentUserId, newChatMembersProgress, connectionState } = global; + const { currentUserId, newChatMembersProgress } = global; const isChannel = chat && isChatChannel(chat); const { @@ -181,7 +173,6 @@ export default memo(withGlobal( isLoading: newChatMembersProgress === NewChatMembersProgress.Loading, globalUserIds, localUserIds, - connectionState, }; }, )(AddChatMembers)); diff --git a/src/components/right/management/ManageGroupMembers.tsx b/src/components/right/management/ManageGroupMembers.tsx index b4cce6392..40322391f 100644 --- a/src/components/right/management/ManageGroupMembers.tsx +++ b/src/components/right/management/ManageGroupMembers.tsx @@ -62,7 +62,7 @@ const ManageGroupMembers: FC = ({ onScreenSelect, onChatMemberSelect, }) => { - const { openChat, setUserSearchQuery, loadContactList } = getActions(); + const { openChat, setUserSearchQuery, closeManagement } = getActions(); const lang = useLang(); // eslint-disable-next-line no-null/no-null const inputRef = useRef(null); @@ -119,16 +119,17 @@ const ManageGroupMembers: FC = ({ ); }, [memberIds, localContactIds, searchQuery, localUserIds, globalUserIds, isChannel, noAdmins, adminIds]); - const [viewportIds, getMore] = useInfiniteScroll(loadContactList, displayedIds, Boolean(searchQuery)); + const [viewportIds, getMore] = useInfiniteScroll(undefined, displayedIds, Boolean(searchQuery)); const handleMemberClick = useCallback((id: string) => { if (noAdmins) { onChatMemberSelect!(id, false); onScreenSelect!(ManagementScreens.ChatNewAdminRights); } else { + closeManagement(); openChat({ id }); } - }, [noAdmins, onChatMemberSelect, onScreenSelect, openChat]); + }, [closeManagement, noAdmins, onChatMemberSelect, onScreenSelect, openChat]); const handleFilterChange = useCallback((e: React.ChangeEvent) => { setUserSearchQuery({ query: e.target.value }); diff --git a/src/global/actions/api/chats.ts b/src/global/actions/api/chats.ts index a6488bc2f..15ca44a84 100644 --- a/src/global/actions/api/chats.ts +++ b/src/global/actions/api/chats.ts @@ -1105,7 +1105,7 @@ export async function loadFullChat(chat: ApiChat) { } const { - users, fullInfo, groupCall, membersCount, + users, userStatusesById, fullInfo, groupCall, membersCount, } = result; let global = getGlobal(); @@ -1113,6 +1113,10 @@ export async function loadFullChat(chat: ApiChat) { global = addUsers(global, buildCollectionByKey(users, 'id')); } + if (userStatusesById) { + global = addUserStatuses(global, userStatusesById); + } + if (groupCall) { const existingGroupCall = selectGroupCall(global, groupCall.id!); global = updateGroupCall( diff --git a/src/global/actions/api/users.ts b/src/global/actions/api/users.ts index e8548ac81..a6c2c64d6 100644 --- a/src/global/actions/api/users.ts +++ b/src/global/actions/api/users.ts @@ -11,8 +11,17 @@ 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, + addChats, + addUsers, + addUserStatuses, + closeNewContactDialog, + replaceUserStatuses, + updateChat, + updateManagementProgress, + updateUser, + updateUsers, + updateUserSearch, + updateUserSearchFetchingStatus, } from '../../reducers'; import { getServerTime } from '../../../util/serverTime'; import * as langProvider from '../../../util/langProvider'; @@ -145,6 +154,7 @@ async function loadContactList() { let global = addUsers(getGlobal(), buildCollectionByKey(contactList.users, 'id')); global = addChats(global, buildCollectionByKey(contactList.chats, 'id')); + global = addUserStatuses(global, contactList.userStatusesById); // Sort contact list by Last Name (or First Name), with latin names being placed first const getCompareString = (user: ApiUser) => (user.lastName || user.firstName || '');