import type { FC } from '../../../lib/teact/teact'; import React, { memo, useCallback, useEffect, useMemo, useRef, } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import type { GlobalState, TabState } from '../../../global/types'; import type { AnimationLevel, ISettings } from '../../../types'; import { LeftColumnContent, SettingsScreens } from '../../../types'; import { ANIMATION_LEVEL_MAX, ANIMATION_LEVEL_MIN, APP_NAME, ARCHIVED_FOLDER_ID, BETA_CHANGELOG_URL, DEBUG, FEEDBACK_URL, IS_BETA, IS_TEST, IS_ELECTRON, PRODUCTION_HOSTNAME, } from '../../../config'; import { IS_APP } from '../../../util/windowEnvironment'; import { INITIAL_PERFORMANCE_STATE_MAX, INITIAL_PERFORMANCE_STATE_MID, INITIAL_PERFORMANCE_STATE_MIN, } from '../../../global/initialState'; import buildClassName from '../../../util/buildClassName'; import { formatDateToString } from '../../../util/dateFormat'; import { setPermanentWebVersion } from '../../../util/permanentWebVersion'; import { clearWebsync } from '../../../util/websync'; import { selectCanSetPasscode, selectCurrentMessageList, selectIsCurrentUserPremium, selectTabState, selectTheme, } from '../../../global/selectors'; import useLang from '../../../hooks/useLang'; import useConnectionStatus from '../../../hooks/useConnectionStatus'; import { useHotkeys } from '../../../hooks/useHotkeys'; import { getPromptInstall } from '../../../util/installPrompt'; import captureEscKeyListener from '../../../util/captureEscKeyListener'; import useLeftHeaderButtonRtlForumTransition from './hooks/useLeftHeaderButtonRtlForumTransition'; import { useFullscreenStatus } from '../../../hooks/useFullscreen'; import useElectronDrag from '../../../hooks/useElectronDrag'; import { useFolderManagerForUnreadCounters } from '../../../hooks/useFolderManager'; import useAppLayout from '../../../hooks/useAppLayout'; import DropdownMenu from '../../ui/DropdownMenu'; import MenuItem from '../../ui/MenuItem'; import Button from '../../ui/Button'; import SearchInput from '../../ui/SearchInput'; import PickerSelectedItem from '../../common/PickerSelectedItem'; import Switcher from '../../ui/Switcher'; import ShowTransition from '../../ui/ShowTransition'; import ConnectionStatusOverlay from '../ConnectionStatusOverlay'; import StatusButton from './StatusButton'; import Toggle from '../../ui/Toggle'; import './LeftMainHeader.scss'; type OwnProps = { shouldHideSearch?: boolean; content: LeftColumnContent; contactsFilter: string; isClosingSearch?: boolean; shouldSkipTransition?: boolean; onSearchQuery: (query: string) => void; onSelectSettings: () => void; onSelectContacts: () => void; onSelectArchived: () => void; onReset: () => void; }; type StateProps = { searchQuery?: string; isLoading: boolean; currentUserId?: string; globalSearchChatId?: string; searchDate?: number; theme: ISettings['theme']; animationLevel: AnimationLevel; isMessageListOpen: boolean; isCurrentUserPremium?: boolean; isConnectionStatusMinimized: ISettings['isConnectionStatusMinimized']; areChatsLoaded?: boolean; hasPasscode?: boolean; canSetPasscode?: boolean; } & Pick & Pick; const WEBK_VERSION_URL = 'https://web.telegram.org/k/'; const LeftMainHeader: FC = ({ shouldHideSearch, content, contactsFilter, onSearchQuery, isClosingSearch, onSelectSettings, onSelectContacts, onSelectArchived, onReset, searchQuery, isLoading, isCurrentUserPremium, shouldSkipTransition, currentUserId, globalSearchChatId, searchDate, theme, animationLevel, connectionState, isSyncing, isMessageListOpen, isConnectionStatusMinimized, areChatsLoaded, hasPasscode, canSetPasscode, canInstall, archiveSettings, }) => { const { openChat, setGlobalSearchDate, setSettingOption, setGlobalSearchChatId, openChatByUsername, lockScreen, requestNextSettingsScreen, skipLockOnUnload, openUrl, updatePerformanceSettings, } = getActions(); const lang = useLang(); const { isMobile } = useAppLayout(); const hasMenu = content === LeftColumnContent.ChatList; const clearedDateSearchParam = { date: undefined }; const clearedChatSearchParam = { id: undefined }; const selectedSearchDate = useMemo(() => { return searchDate ? formatDateToString(new Date(searchDate * 1000)) : undefined; }, [searchDate]); const archivedUnreadChatsCount = useFolderManagerForUnreadCounters()[ARCHIVED_FOLDER_ID]?.chatsCount || 0; const { connectionStatus, connectionStatusText, connectionStatusPosition } = useConnectionStatus( lang, connectionState, isSyncing, isMessageListOpen, isConnectionStatusMinimized, !areChatsLoaded, ); const handleLockScreenHotkey = useCallback((e: KeyboardEvent) => { e.preventDefault(); e.stopPropagation(); if (hasPasscode) { lockScreen(); } else { requestNextSettingsScreen({ screen: SettingsScreens.PasscodeDisabled }); } }, [hasPasscode]); useHotkeys(canSetPasscode ? { 'Ctrl+Shift+L': handleLockScreenHotkey, 'Alt+Shift+L': handleLockScreenHotkey, 'Meta+Shift+L': handleLockScreenHotkey, ...(IS_APP && { 'Mod+L': handleLockScreenHotkey }), } : undefined); const withOtherVersions = window.location.hostname === PRODUCTION_HOSTNAME || IS_TEST; const MainButton: FC<{ onTrigger: () => void; isOpen?: boolean }> = useMemo(() => { return ({ onTrigger, isOpen }) => ( ); }, [hasMenu, isMobile, lang, onReset, shouldSkipTransition]); const handleSearchFocus = useCallback(() => { if (!searchQuery) { onSearchQuery(''); } }, [searchQuery, onSearchQuery]); const toggleConnectionStatus = useCallback(() => { setSettingOption({ isConnectionStatusMinimized: !isConnectionStatusMinimized }); }, [isConnectionStatusMinimized, setSettingOption]); const handleSelectSaved = useCallback(() => { openChat({ id: currentUserId, shouldReplaceHistory: true }); }, [currentUserId, openChat]); const handleDarkModeToggle = useCallback((e: React.SyntheticEvent) => { e.stopPropagation(); const newTheme = theme === 'light' ? 'dark' : 'light'; setSettingOption({ theme: newTheme }); setSettingOption({ shouldUseSystemTheme: false }); }, [setSettingOption, theme]); const handleAnimationLevelChange = useCallback((e: React.SyntheticEvent) => { e.stopPropagation(); let newLevel = animationLevel + 1; if (newLevel > ANIMATION_LEVEL_MAX) { newLevel = ANIMATION_LEVEL_MIN; } const performanceSettings = newLevel === ANIMATION_LEVEL_MIN ? INITIAL_PERFORMANCE_STATE_MIN : (newLevel === ANIMATION_LEVEL_MAX ? INITIAL_PERFORMANCE_STATE_MAX : INITIAL_PERFORMANCE_STATE_MID); setSettingOption({ animationLevel: newLevel as AnimationLevel }); updatePerformanceSettings(performanceSettings); }, [animationLevel, setSettingOption]); const handleChangelogClick = useCallback(() => { window.open(BETA_CHANGELOG_URL, '_blank', 'noopener'); }, []); const handleSwitchToWebK = useCallback(() => { setPermanentWebVersion('K'); clearWebsync(); skipLockOnUnload(); }, [skipLockOnUnload]); const handleOpenTipsChat = useCallback(() => { openChatByUsername({ username: lang('Settings.TipsUsername') }); }, [lang, openChatByUsername]); const handleBugReportClick = useCallback(() => { openUrl({ url: FEEDBACK_URL }); }, [openUrl]); const handleLockScreen = useCallback(() => { lockScreen(); }, [lockScreen]); const isSearchFocused = ( Boolean(globalSearchChatId) || content === LeftColumnContent.GlobalSearch || content === LeftColumnContent.Contacts ); useEffect(() => (isSearchFocused ? captureEscKeyListener(() => onReset()) : undefined), [isSearchFocused, onReset]); const searchInputPlaceholder = content === LeftColumnContent.Contacts ? lang('SearchFriends') : lang('Search'); const versionString = IS_BETA ? `${APP_VERSION} Beta (${APP_REVISION})` : (DEBUG ? APP_REVISION : APP_VERSION); const animationLevelValue = animationLevel !== ANIMATION_LEVEL_MIN ? (animationLevel === ANIMATION_LEVEL_MAX ? 'max' : 'mid') : 'min'; const isFullscreen = useFullscreenStatus(); // Disable dropdown menu RTL animation for resize const { shouldDisableDropdownMenuTransitionRef, handleDropdownMenuTransitionEnd, } = useLeftHeaderButtonRtlForumTransition(shouldHideSearch); // eslint-disable-next-line no-null/no-null const headerRef = useRef(null); useElectronDrag(headerRef); const menuItems = useMemo(() => ( <> {lang('SavedMessages')} {archiveSettings.isHidden && ( {lang('ArchivedChats')} {archivedUnreadChatsCount > 0 && (
{archivedUnreadChatsCount}
)}
)} {lang('Contacts')} {lang('Settings')} {lang('lng_menu_night_mode')} {lang('Appearance.Animations').toLowerCase()} {lang('TelegramFeatures')} Report Bug {IS_BETA && ( Beta Changelog )} {withOtherVersions && ( Switch to K Version )} {canInstall && ( Install App )} ), [ animationLevelValue, archivedUnreadChatsCount, canInstall, handleAnimationLevelChange, handleBugReportClick, lang, handleChangelogClick, handleDarkModeToggle, handleOpenTipsChat, handleSelectSaved, handleSwitchToWebK, onSelectArchived, onSelectContacts, onSelectSettings, theme, withOtherVersions, archiveSettings, ]); return (
{lang.isRtl &&
} {menuItems} {selectedSearchDate && ( )} {globalSearchChatId && ( )} {isCurrentUserPremium && } {hasPasscode && ( )}
); }; export default memo(withGlobal( (global): StateProps => { const tabState = selectTabState(global); const { query: searchQuery, fetchingStatus, chatId, date, } = tabState.globalSearch; const { currentUserId, connectionState, isSyncing, archiveSettings, } = global; const { isConnectionStatusMinimized, animationLevel } = global.settings.byKey; return { searchQuery, isLoading: fetchingStatus ? Boolean(fetchingStatus.chats || fetchingStatus.messages) : false, currentUserId, globalSearchChatId: chatId, searchDate: date, theme: selectTheme(global), animationLevel, connectionState, isSyncing, isMessageListOpen: Boolean(selectCurrentMessageList(global)), isConnectionStatusMinimized, isCurrentUserPremium: selectIsCurrentUserPremium(global), areChatsLoaded: Boolean(global.chats.listIds.active), hasPasscode: Boolean(global.passcode.hasPasscode), canInstall: Boolean(tabState.canInstall), archiveSettings, canSetPasscode: selectCanSetPasscode(global), }; }, )(LeftMainHeader));