import type { FC } from '../../lib/teact/teact'; import React, { useEffect, useCallback, memo, useState, } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; import type { ApiUser, ApiChat, ApiUserStatus, ApiTopic, } from '../../api/types'; import type { GlobalState } from '../../global/types'; import type { AnimationLevel } from '../../types'; import { MediaViewerOrigin } from '../../types'; import { IS_TOUCH_ENV } from '../../util/environment'; import { MEMO_EMPTY_ARRAY } from '../../util/memo'; import { selectTabState, selectChat, selectCurrentMessageList, selectThreadMessagesCount, selectUser, selectUserStatus, } from '../../global/selectors'; import { getUserStatus, isChatChannel, isUserOnline } from '../../global/helpers'; import { captureEvents, SwipeDirection } from '../../util/captureEvents'; import buildClassName from '../../util/buildClassName'; import renderText from './helpers/renderText'; import usePhotosPreload from './hooks/usePhotosPreload'; import useLang from '../../hooks/useLang'; import usePrevious from '../../hooks/usePrevious'; import FullNameTitle from './FullNameTitle'; import ProfilePhoto from './ProfilePhoto'; import Transition from '../ui/Transition'; import TopicIcon from './TopicIcon'; import Avatar from './Avatar'; import './ProfileInfo.scss'; import styles from './ProfileInfo.module.scss'; type OwnProps = { userId: string; forceShowSelf?: boolean; canPlayVideo: boolean; }; type StateProps = { user?: ApiUser; userStatus?: ApiUserStatus; chat?: ApiChat; isSavedMessages?: boolean; animationLevel: AnimationLevel; mediaId?: number; avatarOwnerId?: string; topic?: ApiTopic; messagesCount?: number; } & Pick; const EMOJI_STATUS_SIZE = 24; const EMOJI_TOPIC_SIZE = 120; const ProfileInfo: FC = ({ forceShowSelf, canPlayVideo, user, userStatus, chat, isSavedMessages, connectionState, animationLevel, mediaId, avatarOwnerId, topic, messagesCount, }) => { const { loadFullUser, openMediaViewer, openPremiumModal, } = getActions(); const lang = useLang(); const { id: userId } = user || {}; const { id: chatId } = chat || {}; const photos = user?.photos || chat?.photos || MEMO_EMPTY_ARRAY; const prevMediaId = usePrevious(mediaId); const prevAvatarOwnerId = usePrevious(avatarOwnerId); const [hasSlideAnimation, setHasSlideAnimation] = useState(true); const slideAnimation = hasSlideAnimation ? animationLevel >= 1 ? (lang.isRtl ? 'slide-optimized-rtl' : 'slide-optimized') : 'none' : 'none'; const [currentPhotoIndex, setCurrentPhotoIndex] = useState(0); const isFirst = isSavedMessages || photos.length <= 1 || currentPhotoIndex === 0; const isLast = isSavedMessages || photos.length <= 1 || currentPhotoIndex === photos.length - 1; // Set the current avatar photo to the last selected photo in Media Viewer after it is closed useEffect(() => { if (prevAvatarOwnerId && prevMediaId !== undefined && mediaId === undefined) { setHasSlideAnimation(false); setCurrentPhotoIndex(prevMediaId); } }, [mediaId, prevMediaId, prevAvatarOwnerId]); // Deleting the last profile photo may result in an error useEffect(() => { if (currentPhotoIndex > photos.length) { setCurrentPhotoIndex(Math.max(0, photos.length - 1)); } }, [currentPhotoIndex, photos.length]); useEffect(() => { if (connectionState === 'connectionStateReady' && userId && !forceShowSelf) { loadFullUser({ userId }); } }, [userId, loadFullUser, connectionState, forceShowSelf]); usePhotosPreload(user || chat, photos, currentPhotoIndex); const handleProfilePhotoClick = useCallback(() => { openMediaViewer({ avatarOwnerId: userId || chatId, mediaId: currentPhotoIndex, origin: forceShowSelf ? MediaViewerOrigin.SettingsAvatar : MediaViewerOrigin.ProfileAvatar, }); }, [openMediaViewer, userId, chatId, currentPhotoIndex, forceShowSelf]); const handleClickPremium = useCallback(() => { if (!user) return; openPremiumModal({ fromUserId: user.id }); }, [openPremiumModal, user]); const selectPreviousMedia = useCallback(() => { if (isFirst) { return; } setHasSlideAnimation(true); setCurrentPhotoIndex(currentPhotoIndex - 1); }, [currentPhotoIndex, isFirst]); const selectNextMedia = useCallback(() => { if (isLast) { return; } setHasSlideAnimation(true); setCurrentPhotoIndex(currentPhotoIndex + 1); }, [currentPhotoIndex, isLast]); function handleSelectFallbackPhoto() { if (!isFirst) return; setHasSlideAnimation(true); setCurrentPhotoIndex(photos.length - 1); } // Swipe gestures useEffect(() => { const element = document.querySelector(`.${styles.photoWrapper}`); if (!element) { return undefined; } return captureEvents(element, { selectorToPreventScroll: '.Profile, .settings-content', onSwipe: IS_TOUCH_ENV ? (e, direction) => { if (direction === SwipeDirection.Right) { selectPreviousMedia(); return true; } else if (direction === SwipeDirection.Left) { selectNextMedia(); return true; } return false; } : undefined, }); }, [selectNextMedia, selectPreviousMedia]); if (!user && !chat) { return undefined; } function renderTopic() { return (

{renderText(topic!.title)}

{messagesCount ? lang('Chat.Title.Topic', messagesCount, 'i') : lang('lng_forum_no_messages')}

); } function renderPhotoTabs() { if (isSavedMessages || !photos || photos.length <= 1) { return undefined; } return (
{photos.map((_, i) => ( ))}
); } function renderPhoto(isActive?: boolean) { const photo = !isSavedMessages && photos.length > 0 ? photos[currentPhotoIndex] : undefined; return ( ); } function renderStatus() { if (user) { return (
{getUserStatus(lang, user, userStatus)}
); } return ( { isChatChannel(chat!) ? lang('Subscribers', chat!.membersCount ?? 0, 'i') : lang('Members', chat!.membersCount ?? 0, 'i') } ); } if (topic) { return renderTopic(); } return (
{renderPhotoTabs()} {!forceShowSelf && user?.fullInfo?.personalPhoto && (
{lang(user.fullInfo.personalPhoto.isVideo ? 'UserInfo.CustomVideo' : 'UserInfo.CustomPhoto')}
)} {forceShowSelf && user?.fullInfo?.fallbackPhoto && (
{!isLast && ( )} {lang(user.fullInfo.fallbackPhoto.isVideo ? 'UserInfo.PublicVideo' : 'UserInfo.PublicPhoto')}
)} {renderPhoto} {!isFirst && (
{(user || chat) && ( )} {!isSavedMessages && renderStatus()}
); }; export default memo(withGlobal( (global, { userId, forceShowSelf }): StateProps => { const { connectionState } = global; const user = selectUser(global, userId); const userStatus = selectUserStatus(global, userId); const chat = selectChat(global, userId); const isSavedMessages = !forceShowSelf && user && user.isSelf; const { animationLevel } = global.settings.byKey; const { mediaId, avatarOwnerId } = selectTabState(global).mediaViewer; const isForum = chat?.isForum; const { threadId: currentTopicId } = selectCurrentMessageList(global) || {}; const topic = isForum && currentTopicId ? chat?.topics?.[currentTopicId] : undefined; return { connectionState, user, userStatus, chat, isSavedMessages, animationLevel, mediaId, avatarOwnerId, ...(topic && { topic, messagesCount: selectThreadMessagesCount(global, userId, currentTopicId!), }), }; }, )(ProfileInfo));