Fix some user loading problems (#1868)

This commit is contained in:
Alexander Zinchuk 2022-05-16 13:34:09 +02:00
parent 69fbf47d7a
commit e4b2b0911f
11 changed files with 52 additions and 57 deletions

View File

@ -12,6 +12,7 @@ import {
ApiChatBannedRights, ApiChatBannedRights,
ApiChatAdminRights, ApiChatAdminRights,
ApiGroupCall, ApiGroupCall,
ApiUserStatus,
} from '../../types'; } from '../../types';
import { import {
@ -338,6 +339,7 @@ export function clearDraft(chat: ApiChat) {
async function getFullChatInfo(chatId: string): Promise<{ async function getFullChatInfo(chatId: string): Promise<{
fullInfo: ApiChatFullInfo; fullInfo: ApiChatFullInfo;
users?: ApiUser[]; users?: ApiUser[];
userStatusesById?: { [userId: string]: ApiUserStatus };
groupCall?: Partial<ApiGroupCall>; groupCall?: Partial<ApiGroupCall>;
membersCount?: number; membersCount?: number;
} | undefined> { } | undefined> {
@ -365,6 +367,7 @@ async function getFullChatInfo(chatId: string): Promise<{
const members = buildChatMembers(participants); const members = buildChatMembers(participants);
const adminMembers = members ? members.filter(({ isAdmin, isOwner }) => isAdmin || isOwner) : undefined; const adminMembers = members ? members.filter(({ isAdmin, isOwner }) => isAdmin || isOwner) : undefined;
const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined; const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined;
const { users, userStatusesById } = buildApiUsersAndStatuses(result.users);
return { return {
fullInfo: { fullInfo: {
@ -381,7 +384,8 @@ async function getFullChatInfo(chatId: string): Promise<{
requestsPending, requestsPending,
recentRequesterIds: recentRequesters?.map((userId) => buildApiPeerId(userId, 'user')), recentRequesterIds: recentRequesters?.map((userId) => buildApiPeerId(userId, 'user')),
}, },
users: result.users.map(buildApiUser).filter<ApiUser>(Boolean as any), users,
userStatusesById,
groupCall: call ? { groupCall: call ? {
chatId, chatId,
isLoaded: false, isLoaded: false,
@ -403,6 +407,7 @@ async function getFullChannelInfo(
): Promise<{ ): Promise<{
fullInfo: ApiChatFullInfo; fullInfo: ApiChatFullInfo;
users?: ApiUser[]; users?: ApiUser[];
userStatusesById: { [userId: string]: ApiUserStatus };
groupCall?: Partial<ApiGroupCall>; groupCall?: Partial<ApiGroupCall>;
membersCount?: number; membersCount?: number;
} | undefined> { } | undefined> {
@ -440,11 +445,11 @@ async function getFullChannelInfo(
? exportedInvite.link ? exportedInvite.link
: undefined; : undefined;
const { members, users } = (canViewParticipants && await fetchMembers(id, accessHash)) || {}; const { members, users, userStatusesById } = (canViewParticipants && await fetchMembers(id, accessHash)) || {};
const { members: kickedMembers, users: bannedUsers } = ( const { members: kickedMembers, users: bannedUsers, userStatusesById: bannedStatusesById } = (
canViewParticipants && adminRights && await fetchMembers(id, accessHash, 'kicked') 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') canViewParticipants && adminRights && await fetchMembers(id, accessHash, 'admin')
) || {}; ) || {};
const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined; const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined;
@ -463,6 +468,12 @@ async function getFullChannelInfo(
} }
} }
const statusesById = {
...userStatusesById,
...bannedStatusesById,
...adminStatusesById,
};
return { return {
fullInfo: { fullInfo: {
about, about,
@ -492,6 +503,7 @@ async function getFullChannelInfo(
statisticsDcId: statsDc, statisticsDcId: statsDc,
}, },
users: [...(users || []), ...(bannedUsers || []), ...(adminUsers || [])], users: [...(users || []), ...(bannedUsers || []), ...(adminUsers || [])],
userStatusesById: statusesById,
groupCall: call ? { groupCall: call ? {
chatId: id, chatId: id,
isLoaded: false, isLoaded: false,

View File

@ -118,8 +118,11 @@ export async function fetchContactList() {
} }
}); });
const { users, userStatusesById } = buildApiUsersAndStatuses(result.users);
return { return {
users: result.users.map(buildApiUser).filter<ApiUser>(Boolean as any), users,
userStatusesById,
chats: result.users.map((user) => buildApiChatFromPreview(user)).filter<ApiChat>(Boolean as any), chats: result.users.map((user) => buildApiChatFromPreview(user)).filter<ApiChat>(Boolean as any),
}; };
} }

View File

@ -1,12 +1,11 @@
import React, { import React, {
FC, useEffect, useCallback, useMemo, memo, FC, useCallback, useMemo, memo,
} from '../../../lib/teact/teact'; } from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global'; import { getActions, withGlobal } from '../../../global';
import { ApiUser, ApiUserStatus } from '../../../api/types'; import { ApiUser, ApiUserStatus } from '../../../api/types';
import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment'; import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment';
import { throttle } from '../../../util/schedulers';
import { filterUsersByName, sortUserIds } from '../../../global/helpers'; import { filterUsersByName, sortUserIds } from '../../../global/helpers';
import useInfiniteScroll from '../../../hooks/useInfiniteScroll'; import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
import useHistoryBack from '../../../hooks/useHistoryBack'; import useHistoryBack from '../../../hooks/useHistoryBack';
@ -31,8 +30,6 @@ type StateProps = {
serverTimeOffset: number; serverTimeOffset: number;
}; };
const runThrottled = throttle((cb) => cb(), 60000, true);
const ContactList: FC<OwnProps & StateProps> = ({ const ContactList: FC<OwnProps & StateProps> = ({
isActive, isActive,
filter, filter,
@ -43,21 +40,12 @@ const ContactList: FC<OwnProps & StateProps> = ({
onReset, onReset,
}) => { }) => {
const { const {
loadContactList,
openChat, openChat,
openNewContactDialog, openNewContactDialog,
} = getActions(); } = getActions();
const lang = useLang(); 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({ useHistoryBack({
isActive, isActive,
onBack: onReset, onBack: onReset,

View File

@ -1,12 +1,11 @@
import React, { import React, {
FC, useCallback, useEffect, useMemo, memo, FC, useCallback, useMemo, memo,
} from '../../../lib/teact/teact'; } from '../../../lib/teact/teact';
import { getActions, getGlobal, withGlobal } from '../../../global'; import { getActions, getGlobal, withGlobal } from '../../../global';
import { ApiChat } from '../../../api/types'; import { ApiChat } from '../../../api/types';
import { unique } from '../../../util/iteratees'; import { unique } from '../../../util/iteratees';
import { throttle } from '../../../util/schedulers';
import { filterUsersByName, isUserBot, sortChatIds } from '../../../global/helpers'; import { filterUsersByName, isUserBot, sortChatIds } from '../../../global/helpers';
import useLang from '../../../hooks/useLang'; import useLang from '../../../hooks/useLang';
import useHistoryBack from '../../../hooks/useHistoryBack'; import useHistoryBack from '../../../hooks/useHistoryBack';
@ -33,8 +32,6 @@ type StateProps = {
globalUserIds?: string[]; globalUserIds?: string[];
}; };
const runThrottled = throttle((cb) => cb(), 60000, true);
const NewChatStep1: FC<OwnProps & StateProps> = ({ const NewChatStep1: FC<OwnProps & StateProps> = ({
isChannel, isChannel,
isActive, isActive,
@ -50,18 +47,9 @@ const NewChatStep1: FC<OwnProps & StateProps> = ({
globalUserIds, globalUserIds,
}) => { }) => {
const { const {
loadContactList,
setGlobalSearchQuery, setGlobalSearchQuery,
} = getActions(); } = 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(); const lang = useLang();
useHistoryBack({ useHistoryBack({

View File

@ -37,7 +37,7 @@ const RecentContacts: FC<OwnProps & StateProps> = ({
onReset, onReset,
}) => { }) => {
const { const {
loadTopUsers, loadContactList, openChat, loadTopUsers, openChat,
addRecentlyFoundChatId, clearRecentlyFoundChats, addRecentlyFoundChatId, clearRecentlyFoundChats,
} = getActions(); } = getActions();
@ -49,10 +49,8 @@ const RecentContacts: FC<OwnProps & StateProps> = ({
useEffect(() => { useEffect(() => {
runThrottled(() => { runThrottled(() => {
loadTopUsers(); loadTopUsers();
// Loading full contact list for quick local search before user enters the query
loadContactList();
}); });
}, [loadTopUsers, loadContactList]); }, [loadTopUsers]);
useHorizontalScroll(topUsersRef.current, !topUserIds); useHorizontalScroll(topUsersRef.current, !topUserIds);

View File

@ -34,7 +34,6 @@ const BlockUserModal: FC<OwnProps & StateProps> = ({
onClose, onClose,
}) => { }) => {
const { const {
loadContactList,
setUserSearchQuery, setUserSearchQuery,
blockContact, blockContact,
} = getActions(); } = getActions();
@ -82,7 +81,6 @@ const BlockUserModal: FC<OwnProps & StateProps> = ({
filterPlaceholder={lang('BlockedUsers.BlockUser')} filterPlaceholder={lang('BlockedUsers.BlockUser')}
filter={filter} filter={filter}
onFilterChange={setFilter} onFilterChange={setFilter}
loadMore={loadContactList}
onSelectChatOrUser={handleRemoveUser} onSelectChatOrUser={handleRemoveUser}
onClose={onClose} onClose={onClose}
/> />

View File

@ -152,6 +152,7 @@ const Main: FC<StateProps> = ({
checkVersionNotification, checkVersionNotification,
loadAppConfig, loadAppConfig,
loadAttachMenuBots, loadAttachMenuBots,
loadContactList,
} = getActions(); } = getActions();
if (DEBUG && !DEBUG_isLogged) { if (DEBUG && !DEBUG_isLogged) {
@ -178,10 +179,11 @@ const Main: FC<StateProps> = ({
loadTopInlineBots(); loadTopInlineBots();
loadEmojiKeywords({ language: BASE_EMOJI_KEYWORD_LANG }); loadEmojiKeywords({ language: BASE_EMOJI_KEYWORD_LANG });
loadAttachMenuBots(); loadAttachMenuBots();
loadContactList();
} }
}, [ }, [
lastSyncTime, loadAnimatedEmojis, loadEmojiKeywords, loadNotificationExceptions, loadNotificationSettings, lastSyncTime, loadAnimatedEmojis, loadEmojiKeywords, loadNotificationExceptions, loadNotificationSettings,
loadTopInlineBots, updateIsOnline, loadAvailableReactions, loadAppConfig, loadAttachMenuBots, loadTopInlineBots, updateIsOnline, loadAvailableReactions, loadAppConfig, loadAttachMenuBots, loadContactList,
]); ]);
// Language-based API calls // Language-based API calls

View File

@ -1,10 +1,10 @@
import React, { import React, {
FC, useCallback, useMemo, memo, useState, useEffect, FC, useCallback, useMemo, memo, useState,
} from '../../lib/teact/teact'; } from '../../lib/teact/teact';
import { getActions, getGlobal, withGlobal } from '../../global'; import { getActions, getGlobal, withGlobal } from '../../global';
import { import {
ApiChat, ApiChatMember, ApiUpdateConnectionStateType, ApiChat, ApiChatMember,
} from '../../api/types'; } from '../../api/types';
import { NewChatMembersProgress } from '../../types'; import { NewChatMembersProgress } from '../../types';
@ -31,7 +31,6 @@ export type OwnProps = {
}; };
type StateProps = { type StateProps = {
connectionState?: ApiUpdateConnectionStateType;
isChannel?: boolean; isChannel?: boolean;
members?: ApiChatMember[]; members?: ApiChatMember[];
currentUserId?: string; currentUserId?: string;
@ -46,7 +45,6 @@ type StateProps = {
const AddChatMembers: FC<OwnProps & StateProps> = ({ const AddChatMembers: FC<OwnProps & StateProps> = ({
isChannel, isChannel,
connectionState,
members, members,
onNextStep, onNextStep,
currentUserId, currentUserId,
@ -60,19 +58,13 @@ const AddChatMembers: FC<OwnProps & StateProps> = ({
onClose, onClose,
isActive, isActive,
}) => { }) => {
const { setUserSearchQuery, loadContactList } = getActions(); const { setUserSearchQuery } = getActions();
const lang = useLang(); const lang = useLang();
const [selectedMemberIds, setSelectedMemberIds] = useState<string[]>([]); const [selectedMemberIds, setSelectedMemberIds] = useState<string[]>([]);
const prevSelectedMemberIds = usePrevious(selectedMemberIds); const prevSelectedMemberIds = usePrevious(selectedMemberIds);
const noPickerScrollRestore = prevSelectedMemberIds === selectedMemberIds; const noPickerScrollRestore = prevSelectedMemberIds === selectedMemberIds;
useEffect(() => {
if (isActive && connectionState === 'connectionStateReady') {
loadContactList();
}
}, [connectionState, isActive, loadContactList]);
useHistoryBack({ useHistoryBack({
isActive, isActive,
onBack: onClose, onBack: onClose,
@ -160,7 +152,7 @@ export default memo(withGlobal<OwnProps>(
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
const { userIds: localContactIds } = global.contactList || {}; const { userIds: localContactIds } = global.contactList || {};
const { byId: chatsById } = global.chats; const { byId: chatsById } = global.chats;
const { currentUserId, newChatMembersProgress, connectionState } = global; const { currentUserId, newChatMembersProgress } = global;
const isChannel = chat && isChatChannel(chat); const isChannel = chat && isChatChannel(chat);
const { const {
@ -181,7 +173,6 @@ export default memo(withGlobal<OwnProps>(
isLoading: newChatMembersProgress === NewChatMembersProgress.Loading, isLoading: newChatMembersProgress === NewChatMembersProgress.Loading,
globalUserIds, globalUserIds,
localUserIds, localUserIds,
connectionState,
}; };
}, },
)(AddChatMembers)); )(AddChatMembers));

View File

@ -62,7 +62,7 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
onScreenSelect, onScreenSelect,
onChatMemberSelect, onChatMemberSelect,
}) => { }) => {
const { openChat, setUserSearchQuery, loadContactList } = getActions(); const { openChat, setUserSearchQuery, closeManagement } = getActions();
const lang = useLang(); const lang = useLang();
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
@ -119,16 +119,17 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
); );
}, [memberIds, localContactIds, searchQuery, localUserIds, globalUserIds, isChannel, noAdmins, adminIds]); }, [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) => { const handleMemberClick = useCallback((id: string) => {
if (noAdmins) { if (noAdmins) {
onChatMemberSelect!(id, false); onChatMemberSelect!(id, false);
onScreenSelect!(ManagementScreens.ChatNewAdminRights); onScreenSelect!(ManagementScreens.ChatNewAdminRights);
} else { } else {
closeManagement();
openChat({ id }); openChat({ id });
} }
}, [noAdmins, onChatMemberSelect, onScreenSelect, openChat]); }, [closeManagement, noAdmins, onChatMemberSelect, onScreenSelect, openChat]);
const handleFilterChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { const handleFilterChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setUserSearchQuery({ query: e.target.value }); setUserSearchQuery({ query: e.target.value });

View File

@ -1105,7 +1105,7 @@ export async function loadFullChat(chat: ApiChat) {
} }
const { const {
users, fullInfo, groupCall, membersCount, users, userStatusesById, fullInfo, groupCall, membersCount,
} = result; } = result;
let global = getGlobal(); let global = getGlobal();
@ -1113,6 +1113,10 @@ export async function loadFullChat(chat: ApiChat) {
global = addUsers(global, buildCollectionByKey(users, 'id')); global = addUsers(global, buildCollectionByKey(users, 'id'));
} }
if (userStatusesById) {
global = addUserStatuses(global, userStatusesById);
}
if (groupCall) { if (groupCall) {
const existingGroupCall = selectGroupCall(global, groupCall.id!); const existingGroupCall = selectGroupCall(global, groupCall.id!);
global = updateGroupCall( global = updateGroupCall(

View File

@ -11,8 +11,17 @@ import { isUserBot, isUserId } from '../../helpers';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { selectChat, selectCurrentMessageList, selectUser } from '../../selectors'; import { selectChat, selectCurrentMessageList, selectUser } from '../../selectors';
import { import {
addChats, addUsers, closeNewContactDialog, replaceUserStatuses, updateChat, updateManagementProgress, updateUser, addChats,
updateUsers, updateUserSearch, updateUserSearchFetchingStatus, addUsers,
addUserStatuses,
closeNewContactDialog,
replaceUserStatuses,
updateChat,
updateManagementProgress,
updateUser,
updateUsers,
updateUserSearch,
updateUserSearchFetchingStatus,
} from '../../reducers'; } from '../../reducers';
import { getServerTime } from '../../../util/serverTime'; import { getServerTime } from '../../../util/serverTime';
import * as langProvider from '../../../util/langProvider'; import * as langProvider from '../../../util/langProvider';
@ -145,6 +154,7 @@ async function loadContactList() {
let global = addUsers(getGlobal(), buildCollectionByKey(contactList.users, 'id')); let global = addUsers(getGlobal(), buildCollectionByKey(contactList.users, 'id'));
global = addChats(global, buildCollectionByKey(contactList.chats, '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 // Sort contact list by Last Name (or First Name), with latin names being placed first
const getCompareString = (user: ApiUser) => (user.lastName || user.firstName || ''); const getCompareString = (user: ApiUser) => (user.lastName || user.firstName || '');