import type { FC } from '../../../lib/teact/teact'; import React, { memo, useMemo, } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import type { ApiBotVerification, ApiChat, ApiCountryCode, ApiUser, ApiUserFullInfo, ApiUsername, } from '../../../api/types'; import type { BotAppPermissions } from '../../../types'; import { MAIN_THREAD_ID } from '../../../api/types'; import { FRAGMENT_PHONE_CODE, FRAGMENT_PHONE_LENGTH } from '../../../config'; import { buildStaticMapHash, getChatLink, getHasAdminRight, isChatChannel, isUserRightBanned, } from '../../../global/helpers'; import { getIsChatMuted } from '../../../global/helpers/notifications'; import { selectBotAppPermissions, selectChat, selectChatFullInfo, selectCurrentMessageList, selectNotifyDefaults, selectNotifyException, selectTopicLink, selectUser, selectUserFullInfo, } from '../../../global/selectors'; import { copyTextToClipboard } from '../../../util/clipboard'; import { formatPhoneNumberWithCode } from '../../../util/phoneNumber'; import stopEvent from '../../../util/stopEvent'; import { extractCurrentThemeParams } from '../../../util/themeStyle'; import { ChatAnimationTypes } from '../../left/main/hooks'; import formatUsername from '../helpers/formatUsername'; import renderText from '../helpers/renderText'; import useEffectWithPrevDeps from '../../../hooks/useEffectWithPrevDeps'; import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; import useMedia from '../../../hooks/useMedia'; import useOldLang from '../../../hooks/useOldLang'; import useDevicePixelRatio from '../../../hooks/window/useDevicePixelRatio'; import Chat from '../../left/main/Chat'; import Button from '../../ui/Button'; import ListItem from '../../ui/ListItem'; import Skeleton from '../../ui/placeholder/Skeleton'; import Switcher from '../../ui/Switcher'; import CustomEmoji from '../CustomEmoji'; import SafeLink from '../SafeLink'; import BusinessHours from './BusinessHours'; import UserBirthday from './UserBirthday'; import styles from './ChatExtra.module.scss'; type OwnProps = { chatOrUserId: string; isSavedDialog?: boolean; isInSettings?: boolean; }; type StateProps = { user?: ApiUser; chat?: ApiChat; userFullInfo?: ApiUserFullInfo; canInviteUsers?: boolean; isMuted?: boolean; phoneCodeList: ApiCountryCode[]; topicId?: number; description?: string; chatInviteLink?: string; topicLink?: string; hasSavedMessages?: boolean; personalChannel?: ApiChat; hasMainMiniApp?: boolean; isBotCanManageEmojiStatus?: boolean; botAppPermissions?: BotAppPermissions; botVerification?: ApiBotVerification; }; const DEFAULT_MAP_CONFIG = { width: 64, height: 64, zoom: 15, }; const BOT_VERIFICATION_ICON_SIZE = 16; const ChatExtra: FC = ({ chatOrUserId, user, chat, userFullInfo, isInSettings, canInviteUsers, isMuted, phoneCodeList, topicId, description, chatInviteLink, topicLink, hasSavedMessages, personalChannel, hasMainMiniApp, isBotCanManageEmojiStatus, botAppPermissions, botVerification, }) => { const { showNotification, updateChatMutedState, updateTopicMutedState, loadPeerStories, openSavedDialog, openMapModal, requestCollectibleInfo, requestMainWebView, toggleUserEmojiStatusPermission, toggleUserLocationPermission, } = getActions(); const { id: userId, usernames, phoneNumber, isSelf, } = user || {}; const { id: chatId, usernames: chatUsernames } = chat || {}; const peerId = userId || chatId; const { businessLocation, businessWorkHours, personalChannelMessageId, birthday, } = userFullInfo || {}; const oldLang = useOldLang(); const lang = useLang(); useEffectWithPrevDeps(([prevPeerId]) => { if (!peerId || prevPeerId === peerId) return; if (user || (chat && isChatChannel(chat))) { loadPeerStories({ peerId }); } }, [peerId, chat, user]); const { width, height, zoom } = DEFAULT_MAP_CONFIG; const dpr = useDevicePixelRatio(); const locationMediaHash = businessLocation?.geo && buildStaticMapHash(businessLocation.geo, width, height, zoom, dpr); const locationBlobUrl = useMedia(locationMediaHash); const locationRightComponent = useMemo(() => { if (!businessLocation?.geo) return undefined; if (locationBlobUrl) { return ; } return ; }, [businessLocation, locationBlobUrl]); const isTopicInfo = Boolean(topicId && topicId !== MAIN_THREAD_ID); const shouldRenderAllLinks = (chat && isChatChannel(chat)) || user?.isPremium; const activeUsernames = useMemo(() => { const result = usernames?.filter((u) => u.isActive); return result?.length ? result : undefined; }, [usernames]); const activeChatUsernames = useMemo(() => { const result = !user ? chatUsernames?.filter((u) => u.isActive) : undefined; return result?.length ? result : undefined; }, [chatUsernames, user]); const link = useMemo(() => { if (!chat) { return undefined; } return isTopicInfo ? topicLink! : getChatLink(chat) || chatInviteLink; }, [chat, isTopicInfo, topicLink, chatInviteLink]); const handleClickLocation = useLastCallback(() => { const { address, geo } = businessLocation!; if (!geo) { copyTextToClipboard(address); showNotification({ message: oldLang('BusinessLocationCopied') }); return; } openMapModal({ geoPoint: geo, zoom }); }); const handleNotificationChange = useLastCallback(() => { if (isTopicInfo) { updateTopicMutedState({ chatId: chatId!, topicId: topicId!, isMuted: !isMuted, }); } else { updateChatMutedState({ chatId: chatId!, isMuted: !isMuted }); } }); const manageEmojiStatusChange = useLastCallback(() => { if (!user) return; toggleUserEmojiStatusPermission({ botId: user.id, isEnabled: !isBotCanManageEmojiStatus }); }); const handleLocationPermissionChange = useLastCallback(() => { if (!user) return; toggleUserLocationPermission({ botId: user.id, isAccessGranted: !botAppPermissions?.geolocation }); }); const handleOpenSavedDialog = useLastCallback(() => { openSavedDialog({ chatId: chatOrUserId }); }); function copy(text: string, entity: string) { copyTextToClipboard(text); showNotification({ message: `${entity} was copied` }); } const formattedNumber = phoneNumber && formatPhoneNumberWithCode(phoneCodeList, phoneNumber); const handlePhoneClick = useLastCallback(() => { if (phoneNumber?.length === FRAGMENT_PHONE_LENGTH && phoneNumber.startsWith(FRAGMENT_PHONE_CODE)) { requestCollectibleInfo({ collectible: phoneNumber, peerId: peerId!, type: 'phone' }); return; } copy(formattedNumber!, oldLang('Phone')); }); const handleUsernameClick = useLastCallback((username: ApiUsername, isChat?: boolean) => { if (!username.isEditable) { requestCollectibleInfo({ collectible: username.username, peerId: peerId!, type: 'username' }); return; } copy(formatUsername(username.username, isChat), oldLang(isChat ? 'Link' : 'Username')); }); const handleOpenApp = useLastCallback(() => { const botId = user?.id; if (!botId) { return; } const theme = extractCurrentThemeParams(); requestMainWebView({ botId, peerId: botId, theme, shouldMarkBotTrusted: true, }); }); const appTermsInfo = lang('ProfileOpenAppAbout', { terms: ( ), }, { withNodes: true }); if (chat?.isRestricted || (isSelf && !isInSettings)) { return undefined; } function renderUsernames(usernameList: ApiUsername[], isChat?: boolean) { const [mainUsername, ...otherUsernames] = usernameList; const usernameLinks = otherUsernames.length ? (oldLang('UsernameAlso', '%USERNAMES%') as string) .split('%') .map((s) => { return (s === 'USERNAMES' ? ( <> {otherUsernames.map((username, idx) => { return ( <> {idx > 0 ? ', ' : ''} { stopEvent(e); handleUsernameClick(username, isChat); }} className="text-entity-link username-link" > {formatUsername(username.username)} ); })} ) : s); }) : undefined; return ( { handleUsernameClick(mainUsername, isChat); }} > {formatUsername(mainUsername.username, isChat)} {usernameLinks && {usernameLinks}} {oldLang(isChat ? 'Link' : 'Username')} ); } return (
{personalChannel && (

{oldLang('ProfileChannel')}

{oldLang('Subscribers', personalChannel.membersCount, 'i')}
)} {Boolean(formattedNumber?.length) && ( // eslint-disable-next-line react/jsx-no-bind {formattedNumber} {oldLang('Phone')} )} {activeUsernames && renderUsernames(activeUsernames)} {description && Boolean(description.length) && ( { renderText(description, [ 'br', shouldRenderAllLinks ? 'links' : 'tg_links', 'emoji', ]) } {oldLang(userId ? 'UserBio' : 'Info')} )} {activeChatUsernames && !isTopicInfo && renderUsernames(activeChatUsernames, true)} {((!activeChatUsernames && canInviteUsers) || isTopicInfo) && link && ( copy(link, oldLang('SetUrlPlaceholder'))} >
{link}
{oldLang('SetUrlPlaceholder')}
)} {birthday && ( )} { hasMainMiniApp && (
{appTermsInfo}
)} {!isInSettings && ( {oldLang('Notifications')} )} {businessWorkHours && ( )} {businessLocation && (
{businessLocation.address}
{oldLang('BusinessProfileLocation')}
)} {hasSavedMessages && !isInSettings && ( {oldLang('SavedMessagesTab')} )} {userFullInfo && 'isBotAccessEmojiGranted' in userFullInfo && ( {oldLang('BotProfilePermissionEmojiStatus')} )} {botAppPermissions?.geolocation !== undefined && ( {oldLang('BotProfilePermissionLocation')} )} {botVerification && (
{botVerification.description}
)}
); }; export default memo(withGlobal( (global, { chatOrUserId, isSavedDialog }): StateProps => { const { countryList: { phoneCodes: phoneCodeList } } = global; const chat = chatOrUserId ? selectChat(global, chatOrUserId) : undefined; const user = chatOrUserId ? selectUser(global, chatOrUserId) : undefined; const botAppPermissions = chatOrUserId ? selectBotAppPermissions(global, chatOrUserId) : undefined; const isForum = chat?.isForum; const isMuted = chat && getIsChatMuted(chat, selectNotifyDefaults(global), selectNotifyException(global, chat.id)); const { threadId } = selectCurrentMessageList(global) || {}; const topicId = isForum && threadId ? Number(threadId) : undefined; const chatFullInfo = chat && selectChatFullInfo(global, chat.id); const userFullInfo = user && selectUserFullInfo(global, user.id); const botVerification = userFullInfo?.botVerification || chatFullInfo?.botVerification; const chatInviteLink = chatFullInfo?.inviteLink; const description = userFullInfo?.bio || chatFullInfo?.about; const canInviteUsers = chat && !user && ( (!isChatChannel(chat) && !isUserRightBanned(chat, 'inviteUsers')) || getHasAdminRight(chat, 'inviteUsers') ); const topicLink = topicId ? selectTopicLink(global, chatOrUserId, topicId) : undefined; const hasSavedMessages = !isSavedDialog && global.chats.listIds.saved?.includes(chatOrUserId); const personalChannel = userFullInfo?.personalChannelId ? selectChat(global, userFullInfo.personalChannelId) : undefined; const hasMainMiniApp = user?.hasMainMiniApp; return { phoneCodeList, chat, user, userFullInfo, canInviteUsers, botAppPermissions, isMuted, topicId, chatInviteLink, description, topicLink, hasSavedMessages, personalChannel, hasMainMiniApp, isBotCanManageEmojiStatus: userFullInfo?.isBotCanManageEmojiStatus, botVerification, }; }, )(ChatExtra));