import type { FC } from '@teact'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from '@teact'; import { getActions, getGlobal, withGlobal } from '../../global'; import type { ApiBotPreviewMedia, ApiChat, ApiChatMember, ApiMessage, ApiSavedStarGift, ApiStarGiftCollection, ApiStoryAlbum, ApiTypeStory, ApiUser, ApiUserStatus, } from '../../api/types'; import type { ProfileCollectionKey } from '../../global/selectors/payments'; import type { TabState } from '../../global/types'; import type { AnimationLevel, ProfileState, ProfileTabType, SharedMediaType, ThemeKey, ThreadId } from '../../types'; import type { RegularLangKey } from '../../types/language'; import { MAIN_THREAD_ID } from '../../api/types'; import { AudioOrigin, MediaViewerOrigin, NewChatMembersProgress } from '../../types'; import { MEMBERS_SLICE, PROFILE_SENSITIVE_AREA, SHARED_MEDIA_SLICE, SLIDE_TRANSITION_DURATION } from '../../config'; import { selectActiveGiftsCollectionId } from '../../global/selectors/payments'; const CONTENT_PANEL_SHOW_DELAY = 300; import { getHasAdminRight, getIsDownloading, getIsSavedDialog, getMessageDocument, isChatAdmin, isChatChannel, isChatGroup, isUserBot, isUserRightBanned, } from '../../global/helpers'; import { getSavedGiftKey } from '../../global/helpers/stars'; import { selectActiveDownloads, selectChat, selectChatFullInfo, selectChatMessages, selectCurrentSharedMediaSearch, selectIsChatRestricted, selectIsCurrentUserPremium, selectIsRightColumnShown, selectMonoforumChannel, selectPeerStories, selectSimilarBotsIds, selectSimilarChannelIds, selectTabState, selectTheme, selectUser, selectUserCommonChats, selectUserFullInfo, } from '../../global/selectors'; import { selectPremiumLimit } from '../../global/selectors/limits'; import { selectMessageDownloadableMedia } from '../../global/selectors/media'; import { selectSharedSettings } from '../../global/selectors/sharedState'; import { selectActiveStoriesCollectionId } from '../../global/selectors/stories'; import { VTT_PROFILE_GIFTS, VTT_RIGHT_PROFILE_COLLAPSE, VTT_RIGHT_PROFILE_EXPAND, } from '../../util/animations/viewTransitionTypes.ts'; import { areDeepEqual } from '../../util/areDeepEqual'; import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment'; import buildClassName from '../../util/buildClassName'; import { captureEvents, SwipeDirection } from '../../util/captureEvents'; import { isUserId } from '../../util/entities/ids'; import { resolveTransitionName } from '../../util/resolveTransitionName.ts'; import { LOCAL_TGS_URLS } from '../common/helpers/animatedAssets'; import renderText from '../common/helpers/renderText'; import { getSenderName } from '../left/search/helpers/getSenderName'; import { useViewTransition } from '../../hooks/animations/useViewTransition'; import { useVtn } from '../../hooks/animations/useVtn.ts'; import usePeerStoriesPolling from '../../hooks/polling/usePeerStoriesPolling'; import useTopOverscroll from '../../hooks/scroll/useTopOverscroll.tsx'; import useCacheBuster from '../../hooks/useCacheBuster'; import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps'; import useFlag from '../../hooks/useFlag'; import { useIntersectionObserver } from '../../hooks/useIntersectionObserver'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; import useOldLang from '../../hooks/useOldLang'; import useSyncEffect from '../../hooks/useSyncEffect'; import useAsyncRendering from './hooks/useAsyncRendering'; import useProfileState from './hooks/useProfileState'; import useProfileViewportIds from './hooks/useProfileViewportIds'; import useTransitionFixes from './hooks/useTransitionFixes'; import AnimatedIconWithPreview from '../common/AnimatedIconWithPreview'; import Audio from '../common/Audio'; import Document from '../common/Document'; import SavedGift from '../common/gift/SavedGift'; import GroupChatInfo from '../common/GroupChatInfo'; import Icon from '../common/icons/Icon'; import Media from '../common/Media'; import NothingFound from '../common/NothingFound'; import PreviewMedia from '../common/PreviewMedia'; import PrivateChatInfo from '../common/PrivateChatInfo'; import ChatExtra from '../common/profile/ChatExtra'; import ProfileInfo from '../common/profile/ProfileInfo.tsx'; import WebLink from '../common/WebLink'; import ChatList from '../left/main/ChatList'; import MediaStory from '../story/MediaStory'; import Button from '../ui/Button'; import FloatingActionButton from '../ui/FloatingActionButton'; import InfiniteScroll from '../ui/InfiniteScroll'; import Link from '../ui/Link'; import ListItem, { type MenuItemContextAction } from '../ui/ListItem'; import Spinner from '../ui/Spinner'; import TabList from '../ui/TabList'; import Transition from '../ui/Transition'; import DeleteMemberModal from './DeleteMemberModal'; import StarGiftCollectionList from './gifts/StarGiftCollectionList'; import StoryAlbumList from './stories/StoryAlbumList'; import './Profile.scss'; type OwnProps = { chatId: string; threadId?: ThreadId; profileState: ProfileState; isMobile?: boolean; isActive: boolean; onProfileStateChange: (state: ProfileState) => void; }; type StateProps = { monoforumChannel?: ApiChat; theme: ThemeKey; isChannel?: boolean; isBot?: boolean; currentUserId?: string; messagesById?: Record; foundIds?: number[]; mediaSearchType?: SharedMediaType; hasCommonChatsTab?: boolean; hasStoriesTab?: boolean; hasMembersTab?: boolean; hasPreviewMediaTab?: boolean; hasGiftsTab?: boolean; gifts?: ApiSavedStarGift[]; storyAlbums?: ApiStoryAlbum[]; giftCollections?: ApiStarGiftCollection[]; areMembersHidden?: boolean; canAddMembers?: boolean; canDeleteMembers?: boolean; members?: ApiChatMember[]; adminMembersById?: Record; commonChatIds?: string[]; storyIds?: number[]; pinnedStoryIds?: number[]; archiveStoryIds?: number[]; storyByIds?: Record; selectedStoryAlbumId: ProfileCollectionKey; activeCollectionId: ProfileCollectionKey; giftsFilter?: any; chatsById: Record; usersById: Record; userStatusesById: Record; isRightColumnShown: boolean; isRestricted?: boolean; activeDownloads: TabState['activeDownloads']; isChatProtected?: boolean; nextProfileTab?: ProfileTabType; animationLevel: AnimationLevel; shouldWarnAboutFiles?: boolean; similarChannels?: string[]; similarBots?: string[]; botPreviewMedia?: ApiBotPreviewMedia[]; isCurrentUserPremium?: boolean; limitSimilarPeers: number; isTopicInfo?: boolean; isSavedDialog?: boolean; forceScrollProfileTab?: boolean; isSynced?: boolean; hasAvatar?: boolean; }; type TabProps = { type: ProfileTabType; key: RegularLangKey; }; const TABS: TabProps[] = [ { type: 'media', key: 'ProfileTabMedia' }, { type: 'documents', key: 'ProfileTabFiles' }, { type: 'links', key: 'ProfileTabLinks' }, { type: 'audio', key: 'ProfileTabMusic' }, ]; const HIDDEN_RENDER_DELAY = 1000; const INTERSECTION_THROTTLE = 500; const Profile: FC = ({ chatId, isActive, threadId, profileState, theme, monoforumChannel, isChannel, isBot, currentUserId, messagesById, foundIds, storyIds, pinnedStoryIds, archiveStoryIds, storyByIds, selectedStoryAlbumId, activeCollectionId, giftsFilter, mediaSearchType, hasCommonChatsTab, hasStoriesTab, hasMembersTab, hasPreviewMediaTab, hasGiftsTab, gifts, storyAlbums, giftCollections, botPreviewMedia, areMembersHidden, canAddMembers, canDeleteMembers, commonChatIds, members, adminMembersById, usersById, userStatusesById, chatsById, isRightColumnShown, isRestricted, activeDownloads, isChatProtected, nextProfileTab, animationLevel, shouldWarnAboutFiles, similarChannels, similarBots, isCurrentUserPremium, limitSimilarPeers, isTopicInfo, isSavedDialog, forceScrollProfileTab, isSynced, hasAvatar, onProfileStateChange, }) => { const { setSharedMediaSearchType, loadMoreMembers, loadCommonChats, openChat, searchSharedMediaMessages, openMediaViewer, openAudioPlayer, focusMessage, setNewChatMembersDialogState, loadPeerProfileStories, loadStoriesArchive, openPremiumModal, loadChannelRecommendations, loadBotRecommendations, loadPreviewMedias, loadPeerSavedGifts, resetGiftProfileFilter, loadStarGiftCollections, loadStoryAlbums, resetSelectedStoryAlbum, } = getActions(); const containerRef = useRef(); const transitionRef = useRef(); const oldLang = useOldLang(); const lang = useLang(); const [deletingUserId, setDeletingUserId] = useState(); const [isGiftTransitionEnabled, enableGiftTransition, disableGiftTransition] = useFlag(); const profileId = isSavedDialog ? String(threadId) : chatId; const isSavedMessages = profileId === currentUserId && !isSavedDialog; const [isProfileExpanded, expandProfile, collapseProfile] = useFlag(); const [restoreContentHeightKey, setRestoreContentHeightKey] = useState(0); const tabs = useMemo(() => { const arr: TabProps[] = []; if (isSavedMessages && !isSavedDialog) { arr.push({ type: 'dialogs', key: 'ProfileTabSavedDialogs' }); } if (hasStoriesTab) { arr.push({ type: 'stories', key: 'ProfileTabStories' }); } if (hasStoriesTab && isSavedMessages) { arr.push({ type: 'storiesArchive', key: 'ProfileTabStoriesArchive' }); } if (hasGiftsTab) { arr.push({ type: 'gifts', key: 'ProfileTabGifts' }); } if (hasMembersTab) { arr.push({ type: 'members', key: isChannel ? 'ProfileTabSubscribers' : 'ProfileTabMembers' }); } if (hasPreviewMediaTab) { arr.push({ type: 'previewMedia', key: 'ProfileTabBotPreview' }); } arr.push(...TABS); // Voice messages filter currently does not work in forum topics. Return it when it's fixed on the server side. if (!isTopicInfo) { arr.push({ type: 'voice', key: 'ProfileTabVoice' }); } if (hasCommonChatsTab) { arr.push({ type: 'commonChats', key: 'ProfileTabSharedGroups' }); } if (isChannel && similarChannels?.length) { arr.push({ type: 'similarChannels', key: 'ProfileTabSimilarChannels' }); } if (isBot && similarBots?.length) { arr.push({ type: 'similarBots', key: 'ProfileTabSimilarBots' }); } return arr.map((tab) => ({ type: tab.type, title: lang(tab.key), })); }, [ isSavedMessages, isSavedDialog, hasStoriesTab, hasGiftsTab, hasMembersTab, hasPreviewMediaTab, isTopicInfo, hasCommonChatsTab, isChannel, isBot, similarChannels?.length, similarBots?.length, lang, ]); const initialTab = useMemo(() => { if (!nextProfileTab) { return 0; } const index = tabs.findIndex(({ type }) => type === nextProfileTab); return index === -1 ? 0 : index; }, [nextProfileTab, tabs]); const [allowAutoScrollToTabs, startAutoScrollToTabsIfNeeded, stopAutoScrollToTabs] = useFlag(false); const [activeTab, setActiveTab] = useState(initialTab); useEffect(() => { if (!nextProfileTab) return; const index = tabs.findIndex(({ type }) => type === nextProfileTab); if (index === -1) return; setActiveTab(index); }, [nextProfileTab, tabs]); const handleSwitchTab = useCallback((index: number) => { startAutoScrollToTabsIfNeeded(); setActiveTab(index); }, []); useEffect(() => { if (hasPreviewMediaTab && !botPreviewMedia) { loadPreviewMedias({ botId: chatId }); } }, [chatId, botPreviewMedia, hasPreviewMediaTab]); useEffect(() => { if (isChannel && !similarChannels && isSynced) { loadChannelRecommendations({ chatId }); } }, [chatId, isChannel, similarChannels, isSynced]); useEffect(() => { if (isBot && !similarBots && isSynced) { loadBotRecommendations({ userId: chatId }); } }, [chatId, isBot, similarBots, isSynced]); useEffect(() => { resetSelectedStoryAlbum(); }, [chatId]); useSyncEffect(() => { enableGiftTransition(); }, [giftsFilter]); useSyncEffect(() => { disableGiftTransition(); }, [gifts]); useEffect(() => { if (hasGiftsTab && isSynced) { loadStarGiftCollections({ peerId: chatId }); loadStoryAlbums({ peerId: chatId }); } }, [chatId, hasGiftsTab, isSynced]); const [renderingGifts, setRenderingGifts] = useState(gifts); const { startViewTransition } = useViewTransition(); const { createVtnStyle } = useVtn(); const giftIds = useMemo(() => renderingGifts?.map((gift) => getSavedGiftKey(gift)), [renderingGifts]); const renderingActiveTab = activeTab > tabs.length - 1 ? tabs.length - 1 : activeTab; const tabType = tabs[renderingActiveTab].type; const handleLoadCommonChats = useCallback(() => { loadCommonChats({ userId: chatId }); }, [chatId]); const handleLoadPeerStories = useCallback(({ offsetId }: { offsetId: number }) => { loadPeerProfileStories({ peerId: chatId, offsetId }); }, [chatId]); const handleLoadStoriesArchive = useCallback(({ offsetId }: { offsetId: number }) => { loadStoriesArchive({ peerId: chatId, offsetId }); }, [chatId]); const handleLoadGifts = useCallback(() => { loadPeerSavedGifts({ peerId: chatId }); }, [chatId]); const handleLoadMoreMembers = useCallback(() => { loadMoreMembers({ chatId }); }, [chatId, loadMoreMembers]); useEffectWithPrevDeps(([prevGifts]) => { if (areDeepEqual(gifts, prevGifts)) { return; } if (!gifts || !prevGifts || !isGiftTransitionEnabled) { setRenderingGifts(gifts); return; } const prevGiftIds = prevGifts.map((gift) => getSavedGiftKey(gift)); const newGiftIds = gifts.map((gift) => getSavedGiftKey(gift)); const hasOrderChanged = prevGiftIds.some((id, index) => id !== newGiftIds[index]); if (hasOrderChanged) { startViewTransition(VTT_PROFILE_GIFTS, () => { setRenderingGifts(gifts); }); } else { setRenderingGifts(gifts); } }, [gifts, startViewTransition, isGiftTransitionEnabled]); const [resultType, viewportIds, getMore, noProfileInfo] = useProfileViewportIds({ loadMoreMembers: handleLoadMoreMembers, searchMessages: searchSharedMediaMessages, loadStories: handleLoadPeerStories, loadStoriesArchive: handleLoadStoriesArchive, loadMoreGifts: handleLoadGifts, loadCommonChats: handleLoadCommonChats, tabType, mediaSearchType, groupChatMembers: members, commonChatIds, usersById, userStatusesById, chatsById, chatMessages: messagesById, foundIds, threadId, storyIds, giftIds, pinnedStoryIds, archiveStoryIds, similarChannels, similarBots, }); const isFirstTab = (isSavedMessages && resultType === 'dialogs') || (hasStoriesTab && resultType === 'stories') || resultType === 'members' || (!hasMembersTab && resultType === 'media'); const activeKey = tabs.findIndex(({ type }) => type === resultType); const [isGiftCollectionsShowed, markGiftCollectionsShowed, unmarkGiftCollectionsShowed] = useFlag(false); const [isStoryAlbumsShowed, markStoryAlbumsShowed, unmarkStoryAlbums] = useFlag(false); const hasGiftsCollections = giftCollections && giftCollections.length > 0; const hasStoryAlbums = storyAlbums && storyAlbums.length > 0; const isGiftsResult = resultType === 'gifts'; const isStoriesResult = resultType === 'stories'; const shouldShowContentPanel = (isGiftsResult && hasGiftsCollections) || (isStoriesResult && hasStoryAlbums); useEffect(() => { if (hasGiftsCollections) { setTimeout(() => { markGiftCollectionsShowed(); }, CONTENT_PANEL_SHOW_DELAY); } else { unmarkGiftCollectionsShowed(); } if (hasStoryAlbums) { setTimeout(() => { markStoryAlbumsShowed(); }, CONTENT_PANEL_SHOW_DELAY); } else { unmarkStoryAlbums(); } }, [hasGiftsCollections, hasStoryAlbums, markGiftCollectionsShowed, markStoryAlbumsShowed]); usePeerStoriesPolling(resultType === 'members' ? viewportIds as string[] : undefined); const handleStopAutoScrollToTabs = useLastCallback(() => { stopAutoScrollToTabs(); }); const handleExpandProfile = useLastCallback(() => { if (isProfileExpanded) return; startViewTransition(VTT_RIGHT_PROFILE_EXPAND, () => { expandProfile(); }); }); const handleCollapseProfile = useLastCallback(() => { if (!isProfileExpanded) return; startViewTransition(VTT_RIGHT_PROFILE_COLLAPSE, () => { collapseProfile(); }); }); const { handleScroll } = useProfileState({ containerRef, tabType: resultType, profileState, forceScrollProfileTab, allowAutoScrollToTabs, onProfileStateChange, handleStopAutoScrollToTabs, }); const { applyTransitionFix, releaseTransitionFix } = useTransitionFixes(containerRef); const [cacheBuster, resetCacheBuster] = useCacheBuster(); const { observe: observeIntersectionForMedia } = useIntersectionObserver({ rootRef: containerRef, throttleMs: INTERSECTION_THROTTLE, }); const handleTransitionStop = useLastCallback(() => { releaseTransitionFix(); resetCacheBuster(); }); const handleNewMemberDialogOpen = useLastCallback(() => { setNewChatMembersDialogState({ newChatMembersProgress: NewChatMembersProgress.InProgress }); }); // Update search type when switching tabs or forum topics useEffect(() => { setSharedMediaSearchType({ mediaType: tabType as SharedMediaType }); }, [setSharedMediaSearchType, tabType, threadId]); const handleSelectMedia = useLastCallback((messageId: number) => { openMediaViewer({ chatId: profileId, threadId: MAIN_THREAD_ID, messageId, origin: MediaViewerOrigin.SharedMedia, }); }); const handleSelectPreviewMedia = useLastCallback((index: number) => { openMediaViewer({ standaloneMedia: botPreviewMedia?.flatMap((item) => item?.content.photo || item?.content.video).filter(Boolean), origin: MediaViewerOrigin.PreviewMedia, mediaIndex: index, }); }); const handlePlayAudio = useLastCallback((messageId: number) => { openAudioPlayer({ chatId: profileId, messageId }); }); const handleMemberClick = useLastCallback((id: string) => { openChat({ id }); }); const handleMessageFocus = useLastCallback((message: ApiMessage) => { focusMessage({ chatId: message.chatId, messageId: message.id }); }); const handleDeleteMembersModalClose = useLastCallback(() => { setDeletingUserId(undefined); }); useEffectWithPrevDeps(([prevHasMemberTabs]) => { if (prevHasMemberTabs === undefined || activeTab === 0 || prevHasMemberTabs === hasMembersTab) { return; } const newActiveTab = activeTab + (hasMembersTab ? 1 : -1); setActiveTab(Math.min(newActiveTab, tabs.length - 1)); }, [hasMembersTab, activeTab, tabs]); const handleResetGiftsFilter = useLastCallback(() => { resetGiftProfileFilter({ peerId: chatId }); }); useTopOverscroll( containerRef, handleExpandProfile, handleCollapseProfile, !hasAvatar, ); useEffect(() => { if (!transitionRef.current || !IS_TOUCH_ENV) { return undefined; } return captureEvents(transitionRef.current, { selectorToPreventScroll: '.Profile', onSwipe: (e, direction) => { if (direction === SwipeDirection.Left) { setActiveTab(Math.min(renderingActiveTab + 1, tabs.length - 1)); return true; } else if (direction === SwipeDirection.Right) { setActiveTab(Math.max(0, renderingActiveTab - 1)); return true; } return false; }, }); }, [renderingActiveTab, tabs.length]); let renderingDelay; // @optimization Used to unparallelize rendering of message list and profile media if (isFirstTab) { renderingDelay = !isRightColumnShown ? HIDDEN_RENDER_DELAY : 0; // @optimization Used to delay first render of secondary tabs while animating } else if ((!viewportIds && !botPreviewMedia) || (!gifts?.length && resultType === 'gifts')) { renderingDelay = SLIDE_TRANSITION_DURATION; } const canRenderContent = useAsyncRendering([chatId, threadId, resultType, renderingActiveTab, activeCollectionId, selectedStoryAlbumId], renderingDelay); function getMemberContextAction(memberId: string): MenuItemContextAction[] | undefined { return memberId === currentUserId || !canDeleteMembers ? undefined : [{ title: oldLang('lng_context_remove_from_group'), icon: 'stop', handler: () => { setDeletingUserId(memberId); }, }]; } function renderNothingFoundGiftsWithFilter() { return (
{lang('GiftSearchEmpty')}
{lang('GiftSearchReset')}
); } function renderContent() { if (resultType === 'dialogs') { return ( ); } const noContent = (!viewportIds && !botPreviewMedia) || !canRenderContent || !messagesById; const noSpinner = isFirstTab && !canRenderContent; return (
{renderCategories()} {renderSpinnerOrContent(noContent, noSpinner)}
); } function renderCategories() { if (resultType === 'gifts') { return (
); } if (resultType === 'stories') { return (
); } return undefined; } function renderSpinnerOrContentBase(noContent: boolean, noSpinner: boolean) { if (noContent) { const forceRenderHiddenMembers = Boolean(resultType === 'members' && areMembersHidden); return (
{!noSpinner && !forceRenderHiddenMembers && } {forceRenderHiddenMembers && }
); } const isViewportIdsEmpty = viewportIds && !viewportIds?.length; if (isViewportIdsEmpty && resultType === 'gifts') { return renderNothingFoundGiftsWithFilter(); } if (isViewportIdsEmpty) { let text: string; switch (resultType) { case 'members': text = areMembersHidden ? lang('ChatMemberListNoAccess') : lang('NoMembersFound'); break; case 'commonChats': text = oldLang('NoGroupsInCommon'); break; case 'documents': text = oldLang('lng_media_file_empty'); break; case 'links': text = oldLang('lng_media_link_empty'); break; case 'audio': text = oldLang('lng_media_song_empty'); break; case 'voice': text = oldLang('lng_media_audio_empty'); break; case 'stories': text = oldLang('StoryList.SavedEmptyState.Title'); break; case 'storiesArchive': text = oldLang('StoryList.ArchivedEmptyState.Title'); break; default: text = oldLang('SharedMedia.EmptyTitle'); } return (
); } if (!messagesById) { // A TypeScript assertion, should never be really reached return; } const noTransition = resultType === 'gifts' ? isGiftCollectionsShowed : resultType === 'stories' ? isStoryAlbumsShowed : false; return (
{resultType === 'media' ? ( (viewportIds as number[]).map((id) => messagesById[id] && ( )) ) : (resultType === 'stories' || resultType === 'storiesArchive') ? ( (viewportIds as number[]).map((id, i) => storyByIds?.[id] && ( )) ) : resultType === 'documents' ? ( (viewportIds as number[]).map((id) => messagesById[id] && ( )) ) : resultType === 'links' ? ( (viewportIds as number[]).map((id) => messagesById[id] && ( )) ) : resultType === 'audio' ? ( (viewportIds as number[]).map((id) => messagesById[id] && (
); } const shouldUseTransitionForContent = resultType === 'stories' || resultType === 'gifts'; const contentTransitionKey = (() => { if (resultType === 'stories') { return selectedStoryAlbumId === 'all' ? 0 : selectedStoryAlbumId; } if (resultType === 'gifts') { return activeCollectionId === 'all' ? 0 : activeCollectionId; } return 0; })(); const handleOnStop = useLastCallback(() => { setRestoreContentHeightKey(restoreContentHeightKey + 1); }); function renderProfileInfo(peerId: string, isReady: boolean) { return (
); } function renderSpinnerOrContent(noContent: boolean, noSpinner: boolean) { const baseContent = renderSpinnerOrContentBase(noContent, noSpinner); const isSpinner = noContent && !noSpinner; if (shouldUseTransitionForContent) { return ( {baseContent} ); } return ( {baseContent} ); } const activeListSelector = `.shared-media-transition > .Transition_slide-active`; const itemSelector = !shouldUseTransitionForContent ? `${activeListSelector} .${resultType}-list > .scroll-item` /* eslint-disable @stylistic/max-len */ : `${activeListSelector} > .Transition > .Transition_slide-active > .Transition > .Transition_slide-active > .gifts-list > .scroll-item`; return ( {!noProfileInfo && !isSavedMessages && ( renderProfileInfo( monoforumChannel?.id || profileId, isRightColumnShown && canRenderContent, ) )} {!isRestricted && (
.Transition_slide-active > .Transition > .Transition_slide-active > .content' : undefined} > {renderContent()}
)} {canAddMembers && ( )} {canDeleteMembers && ( )}
); }; export default memo(withGlobal( (global, { chatId, threadId, isMobile, }): Complete => { const user = selectUser(global, chatId); const chat = selectChat(global, chatId); const chatFullInfo = selectChatFullInfo(global, chatId); const userFullInfo = selectUserFullInfo(global, chatId); const messagesById = selectChatMessages(global, chatId); const { animationLevel, shouldWarnAboutFiles } = selectSharedSettings(global); const { currentType: mediaSearchType, resultsByType } = selectCurrentSharedMediaSearch(global) || {}; const { foundIds } = (resultsByType && mediaSearchType && resultsByType[mediaSearchType]) || {}; const isTopicInfo = Boolean(chat?.isForum && threadId && threadId !== MAIN_THREAD_ID); const { byId: usersById, statusesById: userStatusesById } = global.users; const { byId: chatsById } = global.chats; const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId); const isGroup = chat && isChatGroup(chat); const isChannel = chat && isChatChannel(chat); const isBot = user && isUserBot(user); const hasMembersTab = !isTopicInfo && !isSavedDialog && (isGroup || (isChannel && isChatAdmin(chat))) && !chat?.isMonoforum; const members = chatFullInfo?.members; const adminMembersById = chatFullInfo?.adminMembersById; const areMembersHidden = hasMembersTab && chat && (chat.isForbidden || (chatFullInfo && !chatFullInfo.canViewMembers)); const canAddMembers = hasMembersTab && chat && (getHasAdminRight(chat, 'inviteUsers') || (!isChannel && !isUserRightBanned(chat, 'inviteUsers')) || chat.isCreator); const canDeleteMembers = hasMembersTab && chat && (getHasAdminRight(chat, 'banUsers') || chat.isCreator); const activeDownloads = selectActiveDownloads(global); const { similarChannelIds } = selectSimilarChannelIds(global, chatId) || {}; const { similarBotsIds } = selectSimilarBotsIds(global, chatId) || {}; const isCurrentUserPremium = selectIsCurrentUserPremium(global); const peer = user || chat; const peerFullInfo = userFullInfo || chatFullInfo; const hasCommonChatsTab = user && !user.isSelf && !isUserBot(user) && !isSavedDialog && Boolean(userFullInfo?.commonChatsCount); const commonChats = selectUserCommonChats(global, chatId); const hasPreviewMediaTab = userFullInfo?.botInfo?.hasPreviewMedia; const botPreviewMedia = global.users.previewMediaByBotId[chatId]; const hasStoriesTab = peer && (user?.isSelf || (!peer.areStoriesHidden && peerFullInfo?.hasPinnedStories)) && !isSavedDialog; const peerStories = hasStoriesTab ? selectPeerStories(global, peer.id) : undefined; const tabState = selectTabState(global); const { nextProfileTab, forceScrollProfileTab, savedGifts } = tabState; const selectedStoryAlbumId = selectActiveStoriesCollectionId(global); const storyIds = selectedStoryAlbumId !== 'all' ? peerStories?.idsByAlbumId?.[selectedStoryAlbumId]?.ids : peerStories?.profileIds; const pinnedStoryIds = peerStories?.pinnedIds; const storyByIds = peerStories?.byId; const archiveStoryIds = peerStories?.archiveIds; const hasGiftsTab = Boolean(peerFullInfo?.starGiftCount) && !isSavedDialog; const activeCollectionId = selectActiveGiftsCollectionId(global, chatId); const peerGifts = savedGifts.collectionsByPeerId[chatId]?.[activeCollectionId]; const storyAlbums = global.stories.albumsByPeerId?.[chatId]; const giftCollections = global.starGiftCollections?.byPeerId?.[chatId]; const monoforumChannel = selectMonoforumChannel(global, chatId); const isRestricted = chat && selectIsChatRestricted(global, chat.id); const hasAvatar = Boolean(peer?.avatarPhotoId); return { theme: selectTheme(global), isChannel, isBot, messagesById, foundIds, mediaSearchType, hasCommonChatsTab, hasStoriesTab, hasMembersTab, hasPreviewMediaTab, areMembersHidden, canAddMembers, canDeleteMembers, currentUserId: global.currentUserId, isRightColumnShown: selectIsRightColumnShown(global, isMobile), isRestricted, activeDownloads, usersById, userStatusesById, chatsById, storyIds, hasGiftsTab, gifts: peerGifts?.gifts, storyAlbums, giftCollections, pinnedStoryIds, archiveStoryIds, storyByIds, selectedStoryAlbumId, activeCollectionId, giftsFilter: savedGifts.filter, isChatProtected: chat?.isProtected, nextProfileTab, forceScrollProfileTab, animationLevel, shouldWarnAboutFiles, similarChannels: similarChannelIds, similarBots: similarBotsIds, botPreviewMedia, isCurrentUserPremium, isTopicInfo, isSavedDialog, isSynced: global.isSynced, limitSimilarPeers: selectPremiumLimit(global, 'recommendedChannels'), members: hasMembersTab ? members : undefined, adminMembersById: hasMembersTab ? adminMembersById : undefined, commonChatIds: commonChats?.ids, monoforumChannel, hasAvatar, }; }, )(Profile));