Profile: Common Chats tab (#1547)

This commit is contained in:
Alexander Zinchuk 2021-11-17 17:49:52 +03:00
parent aefd2f1cce
commit becfab6fe7
15 changed files with 167 additions and 31 deletions

View File

@ -416,7 +416,7 @@ export function buildInputReportReason(reason: ApiReportReason) {
return undefined; return undefined;
} }
function buildMtpPeerId(id: string, type: 'user' | 'chat' | 'channel') { export function buildMtpPeerId(id: string, type: 'user' | 'chat' | 'channel') {
// Workaround for old-fashioned IDs stored locally // Workaround for old-fashioned IDs stored locally
if (typeof id === 'number') { if (typeof id === 'number') {
return BigInt(Math.abs(id)); return BigInt(Math.abs(id));

View File

@ -1,6 +1,6 @@
import { Api as GramJs } from '../../lib/gramjs'; import { Api as GramJs } from '../../lib/gramjs';
import localDb from './localDb'; import localDb from './localDb';
import { getApiChatIdFromMtpPeer } from './apiBuilders/peers'; import { buildApiPeerId, getApiChatIdFromMtpPeer } from './apiBuilders/peers';
export function resolveMessageApiChatId(mtpMessage: GramJs.TypeMessage) { export function resolveMessageApiChatId(mtpMessage: GramJs.TypeMessage) {
if (!(mtpMessage instanceof GramJs.Message || mtpMessage instanceof GramJs.MessageService)) { if (!(mtpMessage instanceof GramJs.Message || mtpMessage instanceof GramJs.MessageService)) {
@ -41,3 +41,9 @@ export function addPhotoToLocalDb(photo: GramJs.TypePhoto) {
localDb.photos[String(photo.id)] = photo; localDb.photos[String(photo.id)] = photo;
} }
} }
export function addChatToLocalDb(chat: GramJs.TypeChat) {
if (chat instanceof GramJs.Chat || chat instanceof GramJs.Channel) {
localDb.chats[buildApiPeerId(chat.id, chat instanceof GramJs.Chat ? 'chat' : 'channel')] = chat;
}
}

View File

@ -39,7 +39,7 @@ import {
buildChatBannedRights, buildChatBannedRights,
buildChatAdminRights, buildChatAdminRights,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { addMessageToLocalDb } from '../helpers'; import { addChatToLocalDb, addMessageToLocalDb } from '../helpers';
import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers'; import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
const MAX_INT_32 = 2 ** 31 - 1; const MAX_INT_32 = 2 ** 31 - 1;
@ -1075,11 +1075,7 @@ function updateLocalDb(result: (
} }
if ('chats' in result) { if ('chats' in result) {
result.chats.forEach((chat) => { result.chats.forEach(addChatToLocalDb);
if (chat instanceof GramJs.Chat || chat instanceof GramJs.Channel) {
localDb.chats[buildApiPeerId(chat.id, chat instanceof GramJs.Chat ? 'chat' : 'channel')] = chat;
}
});
} }
if ('messages' in result) { if ('messages' in result) {

View File

@ -27,7 +27,7 @@ export {
export { export {
fetchFullUser, fetchNearestCountry, fetchTopUsers, fetchContactList, fetchUsers, fetchFullUser, fetchNearestCountry, fetchTopUsers, fetchContactList, fetchUsers,
addContact, updateContact, deleteUser, fetchProfilePhotos, addContact, updateContact, deleteUser, fetchProfilePhotos, fetchCommonChats,
} from './users'; } from './users';
export { export {

View File

@ -4,19 +4,21 @@ import {
OnApiUpdate, ApiUser, ApiChat, ApiPhoto, OnApiUpdate, ApiUser, ApiChat, ApiPhoto,
} from '../../types'; } from '../../types';
import { PROFILE_PHOTOS_LIMIT } from '../../../config'; import { COMMON_CHATS_LIMIT, PROFILE_PHOTOS_LIMIT } from '../../../config';
import { invokeRequest } from './client'; import { invokeRequest } from './client';
import { searchMessagesLocal } from './messages'; import { searchMessagesLocal } from './messages';
import { import {
buildInputEntity, buildInputEntity,
buildInputPeer, buildInputPeer,
buildInputContact, buildInputContact,
buildMtpPeerId,
getEntityTypeById,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { buildApiUser, buildApiUserFromFull } from '../apiBuilders/users'; import { buildApiUser, buildApiUserFromFull } from '../apiBuilders/users';
import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { buildApiPhoto } from '../apiBuilders/common'; import { buildApiPhoto } from '../apiBuilders/common';
import localDb from '../localDb'; import localDb from '../localDb';
import { addPhotoToLocalDb } from '../helpers'; import { addChatToLocalDb, addPhotoToLocalDb } from '../helpers';
import { buildApiPeerId } from '../apiBuilders/peers'; import { buildApiPeerId } from '../apiBuilders/peers';
let onUpdate: OnApiUpdate; let onUpdate: OnApiUpdate;
@ -54,6 +56,34 @@ export async function fetchFullUser({
}); });
} }
export async function fetchCommonChats(id: string, accessHash?: string, maxId?: string) {
const commonChats = await invokeRequest(new GramJs.messages.GetCommonChats({
userId: buildInputEntity(id, accessHash) as GramJs.InputUser,
maxId: maxId ? buildMtpPeerId(maxId, getEntityTypeById(maxId)) : undefined,
limit: COMMON_CHATS_LIMIT,
}));
if (!commonChats) {
return undefined;
}
updateLocalDb(commonChats);
const chatIds: string[] = [];
const chats: ApiChat[] = [];
commonChats.chats.forEach((mtpChat) => {
const chat = buildApiChatFromPreview(mtpChat);
if (chat) {
chats.push(chat);
chatIds.push(chat.id);
}
});
return { chats, chatIds, isFullyLoaded: chatIds.length < COMMON_CHATS_LIMIT };
}
export async function fetchNearestCountry() { export async function fetchNearestCountry() {
const dcInfo = await invokeRequest(new GramJs.help.GetNearestDc()); const dcInfo = await invokeRequest(new GramJs.help.GetNearestDc());
@ -217,6 +247,12 @@ export async function fetchProfilePhotos(user?: ApiUser, chat?: ApiChat) {
}; };
} }
function updateLocalDb(result: (GramJs.photos.Photos | GramJs.photos.PhotosSlice)) { function updateLocalDb(result: (GramJs.photos.Photos | GramJs.photos.PhotosSlice | GramJs.messages.Chats)) {
result.photos.forEach(addPhotoToLocalDb); if ('chats' in result) {
result.chats.forEach(addChatToLocalDb);
}
if ('photos' in result) {
result.photos.forEach(addPhotoToLocalDb);
}
} }

View File

@ -18,6 +18,11 @@ export interface ApiUser {
photos?: ApiPhoto[]; photos?: ApiPhoto[];
botPlaceholder?: string; botPlaceholder?: string;
canBeInvitedToGroup?: boolean; canBeInvitedToGroup?: boolean;
commonChats?: {
ids: string[];
maxId: string;
isFullyLoaded: boolean;
};
// Obtained from GetFullUser / UserFullInfo // Obtained from GetFullUser / UserFullInfo
fullInfo?: ApiUserFullInfo; fullInfo?: ApiUserFullInfo;

View File

@ -129,6 +129,7 @@
} }
} }
&.commonChats-list,
&.members-list { &.members-list {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;

View File

@ -7,7 +7,8 @@ import {
ApiMessage, ApiMessage,
ApiChatMember, ApiChatMember,
ApiUser, ApiUser,
MAIN_THREAD_ID, ApiChat, ApiChat,
MAIN_THREAD_ID,
} from '../../api/types'; } from '../../api/types';
import { GlobalActions } from '../../global/types'; import { GlobalActions } from '../../global/types';
import { import {
@ -31,6 +32,7 @@ import {
selectIsRightColumnShown, selectIsRightColumnShown,
selectTheme, selectTheme,
selectActiveDownloadIds, selectActiveDownloadIds,
selectUser,
} from '../../modules/selectors'; } from '../../modules/selectors';
import { pick } from '../../util/iteratees'; import { pick } from '../../util/iteratees';
import { captureEvents, SwipeDirection } from '../../util/captureEvents'; import { captureEvents, SwipeDirection } from '../../util/captureEvents';
@ -57,6 +59,7 @@ import WebLink from '../common/WebLink';
import NothingFound from '../common/NothingFound'; import NothingFound from '../common/NothingFound';
import FloatingActionButton from '../ui/FloatingActionButton'; import FloatingActionButton from '../ui/FloatingActionButton';
import DeleteMemberModal from './DeleteMemberModal'; import DeleteMemberModal from './DeleteMemberModal';
import GroupChatInfo from '../common/GroupChatInfo';
import './Profile.scss'; import './Profile.scss';
@ -75,11 +78,13 @@ type StateProps = {
chatMessages?: Record<number, ApiMessage>; chatMessages?: Record<number, ApiMessage>;
foundIds?: number[]; foundIds?: number[];
mediaSearchType?: SharedMediaType; mediaSearchType?: SharedMediaType;
hasCommonChatsTab?: boolean;
hasMembersTab?: boolean; hasMembersTab?: boolean;
areMembersHidden?: boolean; areMembersHidden?: boolean;
canAddMembers?: boolean; canAddMembers?: boolean;
canDeleteMembers?: boolean; canDeleteMembers?: boolean;
members?: ApiChatMember[]; members?: ApiChatMember[];
commonChatIds?: string[];
chatsById: Record<string, ApiChat>; chatsById: Record<string, ApiChat>;
usersById: Record<string, ApiUser>; usersById: Record<string, ApiUser>;
isRightColumnShown: boolean; isRightColumnShown: boolean;
@ -90,8 +95,9 @@ type StateProps = {
}; };
type DispatchProps = Pick<GlobalActions, ( type DispatchProps = Pick<GlobalActions, (
'setLocalMediaSearchType' | 'loadMoreMembers' | 'searchMediaMessagesLocal' | 'openMediaViewer' | 'setLocalMediaSearchType' | 'loadMoreMembers' | 'searchMediaMessagesLocal' | 'openMediaViewer' | 'loadCommonChats' |
'openAudioPlayer' | 'openUserInfo' | 'focusMessage' | 'loadProfilePhotos' | 'setNewChatMembersDialogState' 'openAudioPlayer' | 'openUserInfo' | 'focusMessage' | 'loadProfilePhotos' | 'setNewChatMembersDialogState' |
'openChat'
)>; )>;
const TABS = [ const TABS = [
@ -115,10 +121,12 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
chatMessages, chatMessages,
foundIds, foundIds,
mediaSearchType, mediaSearchType,
hasCommonChatsTab,
hasMembersTab, hasMembersTab,
areMembersHidden, areMembersHidden,
canAddMembers, canAddMembers,
canDeleteMembers, canDeleteMembers,
commonChatIds,
members, members,
usersById, usersById,
chatsById, chatsById,
@ -129,6 +137,8 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
serverTimeOffset, serverTimeOffset,
setLocalMediaSearchType, setLocalMediaSearchType,
loadMoreMembers, loadMoreMembers,
loadCommonChats,
openChat,
searchMediaMessagesLocal, searchMediaMessagesLocal,
openMediaViewer, openMediaViewer,
openAudioPlayer, openAudioPlayer,
@ -150,12 +160,15 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
type: 'members', title: isChannel ? 'ChannelSubscribers' : 'GroupMembers', type: 'members', title: isChannel ? 'ChannelSubscribers' : 'GroupMembers',
}] : []), }] : []),
...TABS, ...TABS,
]), [hasMembersTab, isChannel]); ...(hasCommonChatsTab ? [{
type: 'commonChats', title: 'SharedGroupsTab2',
}] : []),
]), [hasCommonChatsTab, hasMembersTab, isChannel]);
const tabType = tabs[activeTab].type as ProfileTabType; const tabType = tabs[activeTab].type as ProfileTabType;
const [resultType, viewportIds, getMore, noProfileInfo] = useProfileViewportIds( const [resultType, viewportIds, getMore, noProfileInfo] = useProfileViewportIds(
isRightColumnShown, loadMoreMembers, searchMediaMessagesLocal, tabType, mediaSearchType, members, isRightColumnShown, loadMoreMembers, loadCommonChats, searchMediaMessagesLocal, tabType, mediaSearchType, members,
usersById, chatMessages, foundIds, chatId, lastSyncTime, serverTimeOffset, commonChatIds, usersById, chatsById, chatMessages, foundIds, chatId, lastSyncTime, serverTimeOffset,
); );
const activeKey = tabs.findIndex(({ type }) => type === resultType); const activeKey = tabs.findIndex(({ type }) => type === resultType);
@ -273,6 +286,9 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
case 'members': case 'members':
text = areMembersHidden ? 'You have no access to group members list.' : 'No members found'; text = areMembersHidden ? 'You have no access to group members list.' : 'No members found';
break; break;
case 'commonChats':
text = lang('NoGroupsInCommon');
break;
case 'documents': case 'documents':
text = lang('lng_media_file_empty'); text = lang('lng_media_file_empty');
break; break;
@ -373,6 +389,17 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
<PrivateChatInfo userId={id} forceShowSelf /> <PrivateChatInfo userId={id} forceShowSelf />
</ListItem> </ListItem>
)) ))
) : resultType === 'commonChats' ? (
(viewportIds as string[])!.map((id, i) => (
<ListItem
key={id}
teactOrderKey={i}
className="chat-item-clickable scroll-item small-icon"
onClick={() => openChat({ id })}
>
<GroupChatInfo chatId={id} />
</ListItem>
))
) : undefined} ) : undefined}
</div> </div>
); );
@ -474,11 +501,16 @@ export default memo(withGlobal<OwnProps>(
const activeDownloadIds = selectActiveDownloadIds(global, chatId); const activeDownloadIds = selectActiveDownloadIds(global, chatId);
let resolvedUserId; let resolvedUserId;
let user;
if (userId) { if (userId) {
resolvedUserId = userId; resolvedUserId = userId;
} else if (isUserId(chatId)) { } else if (isUserId(chatId)) {
resolvedUserId = chatId; resolvedUserId = chatId;
} }
const hasCommonChatsTab = Boolean(resolvedUserId);
if (resolvedUserId) {
user = selectUser(global, resolvedUserId);
}
return { return {
theme: selectTheme(global), theme: selectTheme(global),
@ -487,6 +519,7 @@ export default memo(withGlobal<OwnProps>(
chatMessages, chatMessages,
foundIds, foundIds,
mediaSearchType, mediaSearchType,
hasCommonChatsTab,
hasMembersTab, hasMembersTab,
areMembersHidden, areMembersHidden,
canAddMembers, canAddMembers,
@ -500,6 +533,7 @@ export default memo(withGlobal<OwnProps>(
usersById, usersById,
chatsById, chatsById,
...(hasMembersTab && members && { members }), ...(hasMembersTab && members && { members }),
...(hasCommonChatsTab && user && { commonChatIds: user.commonChats?.ids }),
}; };
}, },
(setGlobal, actions): DispatchProps => pick(actions, [ (setGlobal, actions): DispatchProps => pick(actions, [
@ -512,5 +546,7 @@ export default memo(withGlobal<OwnProps>(
'focusMessage', 'focusMessage',
'loadProfilePhotos', 'loadProfilePhotos',
'setNewChatMembersDialogState', 'setNewChatMembersDialogState',
'loadCommonChats',
'openChat',
]), ]),
)(Profile)); )(Profile));

View File

@ -1,21 +1,26 @@
import { useMemo, useRef } from '../../../lib/teact/teact'; import { useMemo, useRef } from '../../../lib/teact/teact';
import { ApiChatMember, ApiMessage, ApiUser } from '../../../api/types'; import {
ApiChat, ApiChatMember, ApiMessage, ApiUser,
} from '../../../api/types';
import { ProfileTabType, SharedMediaType } from '../../../types'; import { ProfileTabType, SharedMediaType } from '../../../types';
import { MEMBERS_SLICE, MESSAGE_SEARCH_SLICE, SHARED_MEDIA_SLICE } from '../../../config'; import { MEMBERS_SLICE, MESSAGE_SEARCH_SLICE, SHARED_MEDIA_SLICE } from '../../../config';
import { getMessageContentIds, sortUserIds } from '../../../modules/helpers'; import { getMessageContentIds, sortChatIds, sortUserIds } from '../../../modules/helpers';
import useOnChange from '../../../hooks/useOnChange'; import useOnChange from '../../../hooks/useOnChange';
import useInfiniteScroll from '../../../hooks/useInfiniteScroll'; import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
export default function useProfileViewportIds( export default function useProfileViewportIds(
isRightColumnShown: boolean, isRightColumnShown: boolean,
loadMoreMembers: AnyToVoidFunction, loadMoreMembers: AnyToVoidFunction,
loadCommonChats: AnyToVoidFunction,
searchMessages: AnyToVoidFunction, searchMessages: AnyToVoidFunction,
tabType: ProfileTabType, tabType: ProfileTabType,
mediaSearchType?: SharedMediaType, mediaSearchType?: SharedMediaType,
groupChatMembers?: ApiChatMember[], groupChatMembers?: ApiChatMember[],
commonChatIds?: string[],
usersById?: Record<string, ApiUser>, usersById?: Record<string, ApiUser>,
chatsById?: Record<string, ApiChat>,
chatMessages?: Record<number, ApiMessage>, chatMessages?: Record<number, ApiMessage>,
foundIds?: number[], foundIds?: number[],
chatId?: string, chatId?: string,
@ -32,10 +37,22 @@ export default function useProfileViewportIds(
return sortUserIds(groupChatMembers.map(({ userId }) => userId), usersById, undefined, serverTimeOffset); return sortUserIds(groupChatMembers.map(({ userId }) => userId), usersById, undefined, serverTimeOffset);
}, [groupChatMembers, serverTimeOffset, usersById]); }, [groupChatMembers, serverTimeOffset, usersById]);
const [memberViewportIds, getMoreMembers, noProfileInfoForMembers] = useInfiniteScrollForMembers( const chatIds = useMemo(() => {
if (!commonChatIds || !chatsById) {
return undefined;
}
return sortChatIds(commonChatIds, chatsById, true);
}, [chatsById, commonChatIds]);
const [memberViewportIds, getMoreMembers, noProfileInfoForMembers] = useInfiniteScrollForLoadableItems(
resultType, loadMoreMembers, lastSyncTime, memberIds, resultType, loadMoreMembers, lastSyncTime, memberIds,
); );
const [commonChatViewportIds, getMoreCommonChats, noProfileInfoForCommonChats] = useInfiniteScrollForLoadableItems(
resultType, loadCommonChats, lastSyncTime, chatIds,
);
const [mediaViewportIds, getMoreMedia, noProfileInfoForMedia] = useInfiniteScrollForSharedMedia( const [mediaViewportIds, getMoreMedia, noProfileInfoForMedia] = useInfiniteScrollForSharedMedia(
'media', resultType, searchMessages, lastSyncTime, chatMessages, foundIds, 'media', resultType, searchMessages, lastSyncTime, chatMessages, foundIds,
); );
@ -66,6 +83,11 @@ export default function useProfileViewportIds(
getMore = getMoreMembers; getMore = getMoreMembers;
noProfileInfo = noProfileInfoForMembers; noProfileInfo = noProfileInfoForMembers;
break; break;
case 'commonChats':
viewportIds = commonChatViewportIds;
getMore = getMoreCommonChats;
noProfileInfo = noProfileInfoForCommonChats;
break;
case 'media': case 'media':
viewportIds = mediaViewportIds; viewportIds = mediaViewportIds;
getMore = getMoreMedia; getMore = getMoreMedia;
@ -96,20 +118,20 @@ export default function useProfileViewportIds(
return [resultType, viewportIds, getMore, noProfileInfo] as const; return [resultType, viewportIds, getMore, noProfileInfo] as const;
} }
function useInfiniteScrollForMembers( function useInfiniteScrollForLoadableItems(
currentResultType?: ProfileTabType, currentResultType?: ProfileTabType,
handleLoadMore?: AnyToVoidFunction, handleLoadMore?: AnyToVoidFunction,
lastSyncTime?: number, lastSyncTime?: number,
memberIds?: string[], itemIds?: string[],
) { ) {
const [viewportIds, getMore] = useInfiniteScroll( const [viewportIds, getMore] = useInfiniteScroll(
lastSyncTime ? handleLoadMore : undefined, lastSyncTime ? handleLoadMore : undefined,
memberIds, itemIds,
undefined, undefined,
MEMBERS_SLICE, MEMBERS_SLICE,
); );
const isOnTop = !viewportIds || !memberIds || viewportIds[0] === memberIds[0]; const isOnTop = !viewportIds || !itemIds || viewportIds[0] === itemIds[0];
return [viewportIds, getMore, !isOnTop] as const; return [viewportIds, getMore, !isOnTop] as const;
} }

View File

@ -57,6 +57,7 @@ export const PINNED_MESSAGES_LIMIT = 50;
export const BLOCKED_LIST_LIMIT = 100; export const BLOCKED_LIST_LIMIT = 100;
export const PROFILE_PHOTOS_LIMIT = 40; export const PROFILE_PHOTOS_LIMIT = 40;
export const PROFILE_SENSITIVE_AREA = 500; export const PROFILE_SENSITIVE_AREA = 500;
export const COMMON_CHATS_LIMIT = 100;
export const TOP_CHAT_MESSAGES_PRELOAD_LIMIT = 20; export const TOP_CHAT_MESSAGES_PRELOAD_LIMIT = 20;
export const ALL_CHATS_PRELOAD_DISABLED = false; export const ALL_CHATS_PRELOAD_DISABLED = false;

View File

@ -505,7 +505,7 @@ export type ActionTypes = (
// users // users
'loadFullUser' | 'openUserInfo' | 'loadNearestCountry' | 'loadTopUsers' | 'loadContactList' | 'loadFullUser' | 'openUserInfo' | 'loadNearestCountry' | 'loadTopUsers' | 'loadContactList' |
'loadCurrentUser' | 'updateProfile' | 'checkUsername' | 'addContact' | 'updateContact' | 'loadCurrentUser' | 'updateProfile' | 'checkUsername' | 'addContact' | 'updateContact' |
'deleteUser' | 'loadUser' | 'setUserSearchQuery' | 'deleteUser' | 'loadUser' | 'setUserSearchQuery' | 'loadCommonChats' |
// chat creation // chat creation
'createChannel' | 'createGroupChat' | 'resetChatCreation' | 'createChannel' | 'createGroupChat' | 'resetChatCreation' |
// settings // settings

View File

@ -1026,6 +1026,7 @@ messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flag
messages.getAllDrafts#6a3f8d65 = Updates; messages.getAllDrafts#6a3f8d65 = Updates;
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool; messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
messages.getRecentStickers#9da9403b flags:# attached:flags.0?true hash:long = messages.RecentStickers; messages.getRecentStickers#9da9403b flags:# attached:flags.0?true hash:long = messages.RecentStickers;
messages.getCommonChats#e40ca104 user_id:InputUser max_id:long limit:int = messages.Chats;
messages.getWebPage#32ca8f91 url:string hash:int = WebPage; messages.getWebPage#32ca8f91 url:string hash:int = WebPage;
messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool; messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs; messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs;

View File

@ -1027,6 +1027,7 @@ messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flag
messages.getAllDrafts#6a3f8d65 = Updates; messages.getAllDrafts#6a3f8d65 = Updates;
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool; messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
messages.getRecentStickers#9da9403b flags:# attached:flags.0?true hash:long = messages.RecentStickers; messages.getRecentStickers#9da9403b flags:# attached:flags.0?true hash:long = messages.RecentStickers;
messages.getCommonChats#e40ca104 user_id:InputUser max_id:long limit:int = messages.Chats;
messages.getWebPage#32ca8f91 url:string hash:int = WebPage; messages.getWebPage#32ca8f91 url:string hash:int = WebPage;
messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool; messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs; messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs;

View File

@ -6,10 +6,10 @@ import { ApiUser } from '../../../api/types';
import { ManagementProgress } from '../../../types'; import { ManagementProgress } from '../../../types';
import { debounce, throttle } from '../../../util/schedulers'; import { debounce, throttle } from '../../../util/schedulers';
import { buildCollectionByKey, pick } from '../../../util/iteratees'; import { buildCollectionByKey, pick, unique } from '../../../util/iteratees';
import { isUserId } from '../../helpers'; import { isUserBot, isUserId } from '../../helpers';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { selectChat, selectUser } from '../../selectors'; import { selectChat, selectCurrentMessageList, selectUser } from '../../selectors';
import { import {
addChats, addUsers, updateChat, updateManagementProgress, updateUser, updateUsers, addChats, addUsers, updateChat, updateManagementProgress, updateUser, updateUsers,
updateUserSearch, updateUserSearchFetchingStatus, updateUserSearch, updateUserSearchFetchingStatus,
@ -67,6 +67,37 @@ addReducer('loadCurrentUser', () => {
void callApi('fetchCurrentUser'); void callApi('fetchCurrentUser');
}); });
addReducer('loadCommonChats', (global) => {
const { chatId } = selectCurrentMessageList(global) || {};
const user = chatId ? selectUser(global, chatId) : undefined;
if (!user || isUserBot(user) || user.commonChats?.isFullyLoaded) {
return;
}
(async () => {
const maxId = user.commonChats?.maxId;
const result = await callApi('fetchCommonChats', user.id, user.accessHash!, maxId);
if (!result) {
return;
}
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,
},
});
setGlobal(global);
})();
});
addReducer('updateContact', (global, actions, payload) => { addReducer('updateContact', (global, actions, payload) => {
const { const {
userId, isMuted, firstName, lastName, userId, isMuted, firstName, lastName,

View File

@ -284,7 +284,7 @@ export enum NewChatMembersProgress {
Loading, Loading,
} }
export type ProfileTabType = 'members' | 'media' | 'documents' | 'links' | 'audio' | 'voice'; export type ProfileTabType = 'members' | 'commonChats' | 'media' | 'documents' | 'links' | 'audio' | 'voice';
export type SharedMediaType = 'media' | 'documents' | 'links' | 'audio' | 'voice'; export type SharedMediaType = 'media' | 'documents' | 'links' | 'audio' | 'voice';
export type ApiPrivacyKey = 'phoneNumber' | 'lastSeen' | 'profilePhoto' | 'forwards' | 'chatInvite'; export type ApiPrivacyKey = 'phoneNumber' | 'lastSeen' | 'profilePhoto' | 'forwards' | 'chatInvite';
export type PrivacyVisibility = 'everybody' | 'contacts' | 'nonContacts' | 'nobody'; export type PrivacyVisibility = 'everybody' | 'contacts' | 'nonContacts' | 'nobody';