import type { FC } from '../../lib/teact/teact'; import { memo, useCallback, useMemo, useRef, useState, } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; import type { IAnchorPosition, MessageListType, ThreadId } from '../../types'; import { MAIN_THREAD_ID } from '../../api/types'; import { ManagementScreens } from '../../types'; import { requestMeasure, requestNextMutation } from '../../lib/fasterdom/fasterdom'; import { getHasAdminRight, getIsSavedDialog, isAnonymousForwardsChat, isChatBasicGroup, isChatChannel, isChatSuperGroup, } from '../../global/helpers'; import { selectBot, selectCanAnimateInterface, selectCanTranslateChat, selectChat, selectChatFullInfo, selectIsChatBotNotStarted, selectIsChatRestricted, selectIsChatWithSelf, selectIsCurrentUserFrozen, selectIsInSelectMode, selectIsRightColumnShown, selectIsUserBlocked, selectLanguageCode, selectRequestedChatTranslationLanguage, selectTranslationLanguage, selectUserFullInfo, } from '../../global/selectors'; import { ARE_CALLS_SUPPORTED, IS_APP } from '../../util/browser/windowEnvironment'; import { isUserId } from '../../util/entities/ids'; import focusNoScroll from '../../util/focusNoScroll'; import { useHotkeys } from '../../hooks/useHotkeys'; import useLastCallback from '../../hooks/useLastCallback'; import useOldLang from '../../hooks/useOldLang'; import Button from '../ui/Button'; import DropdownMenu from '../ui/DropdownMenu'; import MenuItem from '../ui/MenuItem'; import MenuSeparator from '../ui/MenuSeparator'; import HeaderMenuContainer from './HeaderMenuContainer.async'; interface OwnProps { chatId: string; threadId: ThreadId; messageListType: MessageListType; canExpandActions: boolean; isForForum?: boolean; isMobile?: boolean; onTopicSearch?: NoneToVoidFunction; } interface StateProps { noMenu?: boolean; isChannel?: boolean; isRightColumnShown?: boolean; canStartBot?: boolean; canRestartBot?: boolean; canUnblock?: boolean; canSubscribe?: boolean; canSearch?: boolean; canCall?: boolean; canMute?: boolean; canViewStatistics?: boolean; canViewMonetization?: boolean; canViewBoosts?: boolean; canShowBoostModal?: boolean; canLeave?: boolean; canEnterVoiceChat?: boolean; canCreateVoiceChat?: boolean; channelMonoforumId?: string; pendingJoinRequests?: number; shouldJoinToSend?: boolean; shouldSendJoinRequest?: boolean; noAnimation?: boolean; canTranslate?: boolean; isTranslating?: boolean; translationLanguage: string; language: string; detectedChatLanguage?: string; doNotTranslate: string[]; isAccountFrozen?: boolean; } const HeaderActions: FC = ({ chatId, threadId, noMenu, isMobile, isChannel, canStartBot, canRestartBot, canUnblock, canSubscribe, canSearch, canCall, canMute, canViewStatistics, canViewMonetization, canViewBoosts, canShowBoostModal, canLeave, canEnterVoiceChat, canCreateVoiceChat, channelMonoforumId, pendingJoinRequests, isRightColumnShown, isForForum, canExpandActions, shouldJoinToSend, shouldSendJoinRequest, noAnimation, canTranslate, isTranslating, translationLanguage, language, detectedChatLanguage, doNotTranslate, isAccountFrozen, onTopicSearch, }) => { const { joinChannel, sendBotCommand, openMiddleSearch, restartBot, requestMasterAndRequestCall, requestNextManagementScreen, showNotification, openChat, requestChatTranslation, togglePeerTranslations, openChatLanguageModal, setSettingOption, unblockUser, setViewForumAsMessages, openFrozenAccountModal, } = getActions(); const menuButtonRef = useRef(); const lang = useOldLang(); const [isMenuOpen, setIsMenuOpen] = useState(false); const [menuAnchor, setMenuAnchor] = useState(undefined); const handleHeaderMenuOpen = useLastCallback(() => { setIsMenuOpen(true); const rect = menuButtonRef.current!.getBoundingClientRect(); setMenuAnchor({ x: rect.right, y: rect.bottom }); }); const handleHeaderMenuClose = useLastCallback(() => { setIsMenuOpen(false); }); const handleHeaderMenuHide = useLastCallback(() => { setMenuAnchor(undefined); }); const handleSubscribeClick = useLastCallback(() => { joinChannel({ chatId }); if (shouldSendJoinRequest) { showNotification({ message: isChannel ? lang('RequestToJoinChannelSentDescription') : lang('RequestToJoinGroupSentDescription'), }); } }); const handleStartBot = useLastCallback(() => { sendBotCommand({ command: '/start' }); }); const handleRestartBot = useLastCallback(() => { restartBot({ chatId }); }); const handleUnblock = useLastCallback(() => { unblockUser({ userId: chatId }); }); const handleTranslateClick = useLastCallback(() => { if (isTranslating) { requestChatTranslation({ chatId, toLanguageCode: undefined }); return; } requestChatTranslation({ chatId, toLanguageCode: translationLanguage }); }); const handleJoinRequestsClick = useLastCallback(() => { requestNextManagementScreen({ screen: ManagementScreens.JoinRequests }); }); const handleSearchClick = useLastCallback(() => { if (isForForum) { onTopicSearch?.(); return; } openMiddleSearch(); if (noAnimation) { // The second RAF is necessary because Teact must update the state and render the async component requestMeasure(() => { requestNextMutation(setFocusInSearchInput); }); } else { setFocusInSearchInput(); } }); const handleAsMessagesClick = useLastCallback(() => { openChat({ id: chatId }); setViewForumAsMessages({ chatId, isEnabled: true }); }); const handleRequestCall = useLastCallback(() => { if (isAccountFrozen) { openFrozenAccountModal(); return; } requestMasterAndRequestCall({ userId: chatId }); }); const handleHotkeySearchClick = useLastCallback((e: KeyboardEvent) => { if (!canSearch || !IS_APP || e.shiftKey) { return; } e.preventDefault(); handleSearchClick(); }); const getTextWithLanguage = useCallback((langKey: string, langCode: string) => { const simplified = langCode.split('-')[0]; const translationKey = `TranslateLanguage${simplified.toUpperCase()}`; const name = lang(translationKey); if (name !== translationKey) { return lang(langKey, name); } const translatedNames = new Intl.DisplayNames([language], { type: 'language' }); const translatedName = translatedNames.of(langCode)!; return lang(`${langKey}Other`, translatedName); }, [language, lang]); const buttonText = useMemo(() => { if (isTranslating) return lang('ShowOriginalButton'); return getTextWithLanguage('TranslateToButton', translationLanguage); }, [translationLanguage, getTextWithLanguage, isTranslating, lang]); const doNotTranslateText = useMemo(() => { if (!detectedChatLanguage) return undefined; return getTextWithLanguage('DoNotTranslateLanguage', detectedChatLanguage); }, [getTextWithLanguage, detectedChatLanguage]); const handleHide = useLastCallback(() => { togglePeerTranslations({ chatId, isEnabled: false }); requestChatTranslation({ chatId, toLanguageCode: undefined }); }); const handleChangeLanguage = useLastCallback(() => { openChatLanguageModal({ chatId }); }); const handleDoNotTranslate = useLastCallback(() => { if (!detectedChatLanguage) return; setSettingOption({ doNotTranslate: [...doNotTranslate, detectedChatLanguage], }); requestChatTranslation({ chatId, toLanguageCode: undefined }); showNotification({ message: getTextWithLanguage('AddedToDoNotTranslate', detectedChatLanguage) }); }); useHotkeys(useMemo(() => ({ 'Mod+F': handleHotkeySearchClick, }), [])); const MoreMenuButton: FC<{ onTrigger: () => void; isOpen?: boolean }> = useMemo(() => { return ({ onTrigger, isOpen }) => ( )} {canExpandActions && shouldSendJoinRequest && ( )} {canExpandActions && canStartBot && ( )} {canExpandActions && canRestartBot && ( )} {canExpandActions && canUnblock && ( )} {canSearch && ( )}