// SPDX-License-Identifier: AGPL-3.0-or-later import {Routes} from '@app/app/Routes'; import Accessibility from '@app/features/accessibility/state/Accessibility'; import {NativeDragRegion} from '@app/features/app/components/layout/NativeDragRegion'; import {GroupDMAvatar} from '@app/features/app/components/shared/GroupDMAvatar'; import { reportSkeletonChannelHeaderLayout, reportSkeletonChannelProjection, } from '@app/features/app/components/skeleton/SkeletonLayoutMemory'; import { measureSkeletonTextWidthPx, measureSkeletonWidthPx, useSkeletonLayoutReport, } from '@app/features/app/hooks/useSkeletonLayoutMemoryCapture'; import {useTextOverflow} from '@app/features/app/hooks/useTextOverflow'; import {ChannelDetailsBottomSheet} from '@app/features/channel/components/bottomsheets/ChannelDetailsBottomSheet'; import {ChannelSearchBottomSheet} from '@app/features/channel/components/bottomsheets/ChannelSearchBottomSheet'; import styles from '@app/features/channel/components/ChannelHeader.module.css'; import {CHANNEL_HEADER_DM_AVATAR_SIZE_PX} from '@app/features/channel/components/ChannelHeaderMetrics'; import {UserTag} from '@app/features/channel/components/ChannelUserTag'; import { ADD_FRIENDS_TO_GROUP_DESCRIPTOR, BACK_DESCRIPTOR, CHANNEL_ACTIONS_DESCRIPTOR, CREATE_GROUP_DM_DESCRIPTOR, EDIT_GROUP_DETAILS_DESCRIPTOR, HIDE_MEMBERS_DESCRIPTOR, MEMBERS_LIST_UNAVAILABLE_AT_THIS_SCREEN_WIDTH_DESCRIPTOR, OPEN_CHANNEL_DETAILS_FOR_DESCRIPTOR, OPEN_DIRECT_MESSAGE_DETAILS_FOR_DESCRIPTOR, OPEN_DIRECT_MESSAGE_PROFILE_DESCRIPTOR, OPEN_GROUP_DETAILS_FOR_DESCRIPTOR, OPEN_PROFILE_FOR_DESCRIPTOR, SEARCH_DESCRIPTOR, SHOW_CHANNEL_LIST_DESCRIPTOR, SHOW_MEMBERS_DESCRIPTOR, VIDEO_CALL_DESCRIPTOR, } from '@app/features/channel/components/channel_header/shared'; import {useChannelHeaderData} from '@app/features/channel/components/channel_header/useChannelHeaderData'; import {CallButtons} from '@app/features/channel/components/channel_header_components/CallButtons'; import {ChannelHeaderIcon} from '@app/features/channel/components/channel_header_components/ChannelHeaderIcon'; import {ChannelNotificationSettingsButton} from '@app/features/channel/components/channel_header_components/ChannelNotificationSettingsButton'; import {ChannelPinsButton} from '@app/features/channel/components/channel_header_components/ChannelPinsButton'; import { isUpdaterIconVisible, UpdaterIcon, } from '@app/features/channel/components/channel_header_components/UpdaterIcon'; import { InboxButton, isStaffToolsButtonVisible, StaffToolsButton, } from '@app/features/channel/components/channel_header_components/UtilityButtons'; import {useChannelSearchState} from '@app/features/channel/components/channel_view/useChannelSearchState'; import {MessageSearchBar} from '@app/features/channel/components/message_search_bar/MessageSearchBar'; import {ChannelTopicModal} from '@app/features/channel/components/modals/ChannelTopicModal'; import {CreateDMModal} from '@app/features/channel/components/modals/CreateDMModal'; import {EditGroupModal} from '@app/features/channel/components/modals/EditGroupModal'; import type {Channel} from '@app/features/channel/models/Channel'; import * as ChannelUtils from '@app/features/channel/utils/ChannelUtils'; import {isGroupDmFull} from '@app/features/channel/utils/GroupDmUtils'; import { ADD_TO_FAVORITES_DESCRIPTOR, CHANNEL_ADDED_TO_FAVORITES_DESCRIPTOR, CHANNEL_REMOVED_FROM_FAVORITES_DESCRIPTOR, HIDE_FAVORITES_DESCRIPTOR, REMOVE_FROM_FAVORITES_DESCRIPTOR, } from '@app/features/i18n/utils/CommonMessageDescriptors'; import {isKeyboardActivationKey} from '@app/features/input/utils/KeyboardUtils'; import type {LexicalSearchInputHandle} from '@app/features/lexical/search/LexicalSearchInput'; import {useCanFitMemberList} from '@app/features/member/hooks/useMemberListVisible'; import MemberList from '@app/features/member/state/MemberList'; import * as FavoritesCommands from '@app/features/messaging/commands/FavoritesCommands'; import {SafeMarkdown} from '@app/features/messaging/components/markdown'; import {MarkdownContext} from '@app/features/messaging/components/markdown/renderers/RendererTypes'; import Favorites from '@app/features/messaging/state/Favorites'; import * as NavigationCommands from '@app/features/navigation/commands/NavigationCommands'; import * as RouterUtils from '@app/features/navigation/utils/RouterUtils'; import {goBackOr} from '@app/features/platform/components/router/NavigationAdapter'; import {useLocation} from '@app/features/platform/components/router/RouterReact'; import {ComponentBus} from '@app/features/platform/utils/ComponentBus'; import {AddFriendsToGroupModal} from '@app/features/relationship/components/modals/AddFriendsToGroupModal'; import Relationships from '@app/features/relationship/state/Relationships'; import type {SearchSegment} from '@app/features/search/utils/SearchSegmentManager'; import markupStyles from '@app/features/theme/styles/Markup.module.css'; import {ChannelContextMenu} from '@app/features/ui/action_menu/ChannelContextMenu'; import {DMContextMenu} from '@app/features/ui/action_menu/DMContextMenu'; import {GroupDMContextMenu} from '@app/features/ui/action_menu/GroupDMContextMenu'; import {MenuGroup} from '@app/features/ui/action_menu/MenuGroup'; import {MenuItem} from '@app/features/ui/action_menu/MenuItem'; import * as ContextMenuCommands from '@app/features/ui/commands/ContextMenuCommands'; import * as LayoutCommands from '@app/features/ui/commands/LayoutCommands'; import * as ModalCommands from '@app/features/ui/commands/ModalCommands'; import {modal} from '@app/features/ui/commands/ModalCommands'; import * as ToastCommands from '@app/features/ui/commands/ToastCommands'; import {StatusAwareAvatar} from '@app/features/ui/components/StatusAwareAvatar'; import FocusRing from '@app/features/ui/focus_ring/FocusRing'; import ContextMenuState, {isContextMenuNodeTarget} from '@app/features/ui/state/ContextMenu'; import MobileLayout from '@app/features/ui/state/MobileLayout'; import PopoutState from '@app/features/ui/state/Popout'; import {Tooltip} from '@app/features/ui/tooltip/Tooltip'; import * as UserProfileCommands from '@app/features/user/commands/UserProfileCommands'; import * as CallCommands from '@app/features/voice/commands/CallCommands'; import MediaEngine from '@app/features/voice/engine/MediaEngineFacade'; import CallState from '@app/features/voice/state/CallState'; import {computeChannelE2EEStatus} from '@app/features/voice/state/ChannelE2EEStatus'; import * as CallUtils from '@app/features/voice/utils/CallUtils'; import {VOICE_CALL_DESCRIPTOR} from '@app/features/voice/utils/VoiceMessageDescriptors'; import {ME} from '@fluxer/constants/src/AppConstants'; import {ChannelTypes} from '@fluxer/constants/src/ChannelConstants'; import {RelationshipTypes} from '@fluxer/constants/src/UserConstants'; import {useLingui} from '@lingui/react/macro'; import { ArrowLeftIcon, CaretRightIcon, EyeSlashIcon, ListIcon, MagnifyingGlassIcon, PencilIcon, PhoneIcon, StarIcon, UserPlusIcon, UsersIcon, VideoCameraIcon, } from '@phosphor-icons/react'; import {clsx} from 'clsx'; import {observer} from 'mobx-react-lite'; import type React from 'react'; import {useCallback, useEffect, useRef, useState} from 'react'; const {VoiceCallButton, VideoCallButton} = CallButtons; interface ChannelHeaderProps { channel?: Channel; leftContent?: React.ReactNode; voiceCallHeaderSupplement?: React.ReactNode; showMembersToggle?: boolean; showPins?: boolean; onSearchSubmit?: (query: string, segments: Array) => void; onSearchClose?: () => void; isSearchResultsOpen?: boolean; forceVoiceCallStyle?: boolean; memberListDefaultHiddenForChannel?: boolean; onBackClick?: () => void; onVoiceCallChromePinChange?: (pinned: boolean) => void; } export const ChannelHeader = observer( ({ channel, leftContent, voiceCallHeaderSupplement = null, showMembersToggle = false, showPins = true, onSearchSubmit, onSearchClose, isSearchResultsOpen, forceVoiceCallStyle = false, memberListDefaultHiddenForChannel = false, onBackClick, onVoiceCallChromePinChange, }: ChannelHeaderProps) => { const {i18n} = useLingui(); const location = useLocation(); const headerRef = useRef(null); const {isMembersOpen} = MemberList; const isMobile = MobileLayout.isMobileLayout(); const isCallChannelConnected = Boolean(MediaEngine.connected && MediaEngine.channelId === channel?.id); const channelIsE2EEEncrypted = isCallChannelConnected && channel ? computeChannelE2EEStatus(channel.guildId ?? null, channel.id) === 'encrypted' : false; const e2eeIconOptions = channelIsE2EEEncrypted ? {e2eeEncrypted: true} : undefined; const isVoiceCallActive = !isMobile && Boolean( channel && (channel.type === ChannelTypes.DM || channel.type === ChannelTypes.GROUP_DM) && isCallChannelConnected && CallState.hasActiveCall(channel.id), ); const isVoiceHeaderActive = isVoiceCallActive || forceVoiceCallStyle; const canFitMemberList = useCanFitMemberList(); const memberListChannelId = channel?.id ?? null; const memberListUsesChannelOverride = Boolean(memberListDefaultHiddenForChannel && memberListChannelId); const isMembersToggleOpen = memberListUsesChannelOverride ? MemberList.isDefaultHiddenChannelMembersOpen(memberListChannelId) : isMembersOpen; const [channelDetailsOpen, setChannelDetailsOpen] = useState(false); const [searchSheetOpen, setSearchSheetOpen] = useState(false); const [initialTab, setInitialTab] = useState<'members' | 'pins'>('members'); const { searchQuery, searchSegments, isSearchActive, handleSearchInputChange: updateSearchInput, handleSearchSubmit: submitSearch, handleSearchClose: closeSearch, } = useChannelSearchState(channel); const latestSearchQueryRef = useRef(''); const latestSearchSegmentsRef = useRef>([]); const topicButtonRef = useRef(null); const [isTopicOverflowing, setIsTopicOverflowing] = useState(false); const contextMenuTarget = ContextMenuState.contextMenu?.target?.target ?? null; const isHeaderContextMenuOpen = Boolean( headerRef.current && isContextMenuNodeTarget(contextMenuTarget) && headerRef.current.contains(contextMenuTarget), ); const isHeaderPopoutOpen = PopoutState.isOpen('inbox') || PopoutState.isOpen('channel-pins'); const isVoiceCallChromePinned = isHeaderContextMenuOpen || isHeaderPopoutOpen; const channelId = channel?.id; const channelType = channel?.type; const channelHasTopic = Boolean(channel?.topic); const staffToolsVisible = isStaffToolsButtonVisible(); const updaterVisible = isUpdaterIconVisible(); const favoritesVisible = Accessibility.showFavorites; useSkeletonLayoutReport( () => reportSkeletonChannelHeaderLayout({staffToolsVisible, updaterVisible, favoritesVisible}), `${staffToolsVisible}|${updaterVisible}|${favoritesVisible}`, ); useEffect(() => { latestSearchQueryRef.current = searchQuery; latestSearchSegmentsRef.current = searchSegments; }, [searchQuery, searchSegments]); useEffect(() => { onVoiceCallChromePinChange?.(isVoiceCallChromePinned); }, [isVoiceCallChromePinned, onVoiceCallChromePinChange]); useEffect(() => { return () => { onVoiceCallChromePinChange?.(false); }; }, [onVoiceCallChromePinChange]); const searchInputRef = useRef(null); const isSearchResultsVisible = isSearchResultsOpen ?? isSearchActive; const dmNameRef = useRef(null); const groupDMNameRef = useRef(null); const guildChannelNameRef = useRef(null); const isDMNameOverflowing = useTextOverflow(dmNameRef); const isGroupDMNameOverflowing = useTextOverflow(groupDMNameRef); const isGuildChannelNameOverflowing = useTextOverflow(guildChannelNameRef); const {isDM, isGroupDM, isPersonalNotes, isGuildChannel, recipient, directMessageName, groupDMName, channelName} = useChannelHeaderData(channel); const isBotDMRecipient = isDM && (recipient?.bot || recipient?.system); const isFavorited = channel && !isPersonalNotes ? !!Favorites.getChannel(channel.id) : false; const channelDetailsLabel = isDM && directMessageName ? i18n._(OPEN_DIRECT_MESSAGE_DETAILS_FOR_DESCRIPTOR, {directMessageName}) : isGroupDM && groupDMName ? i18n._(OPEN_GROUP_DETAILS_FOR_DESCRIPTOR, {groupDMName}) : i18n._(OPEN_CHANNEL_DETAILS_FOR_DESCRIPTOR, {channelName}); const userProfileLabel = directMessageName ? i18n._(OPEN_PROFILE_FOR_DESCRIPTOR, {directMessageName}) : i18n._(OPEN_DIRECT_MESSAGE_PROFILE_DESCRIPTOR); const handleSearchInputChange = useCallback( (query: string, segments: Array) => { updateSearchInput(query, segments); latestSearchQueryRef.current = query; latestSearchSegmentsRef.current = segments; }, [updateSearchInput], ); const handleSearchSubmit = useCallback(() => { const query = latestSearchQueryRef.current; if (onSearchSubmit) { onSearchSubmit(query, latestSearchSegmentsRef.current); return; } submitSearch(query, latestSearchSegmentsRef.current); }, [onSearchSubmit, submitSearch]); const handleSearchClose = useCallback(() => { updateSearchInput('', []); latestSearchQueryRef.current = ''; latestSearchSegmentsRef.current = []; if (onSearchClose) { onSearchClose(); return; } closeSearch(); }, [updateSearchInput, onSearchClose, closeSearch]); const handleOpenCreateGroupDM = useCallback(() => { if (!channel) return; const initialRecipientIds = Array.from(channel.recipientIds); const excludeChannelId = channel.type === ChannelTypes.GROUP_DM ? channel.id : undefined; ModalCommands.push( modal(() => ( )), ); }, [channel]); const handleOpenEditGroup = useCallback(() => { if (!channel) return; ModalCommands.push( modal(() => ( )), ); }, [channel]); const handleOpenAddFriendsToGroup = useCallback(() => { if (!channel) return; ModalCommands.push( modal(() => ( )), ); }, [channel]); const handleToggleMembers = useCallback(() => { if (!canFitMemberList) return; if (memberListUsesChannelOverride) { MemberList.toggleDefaultHiddenChannelMembers(memberListChannelId); return; } LayoutCommands.toggleMembers(!isMembersOpen); }, [isMembersOpen, canFitMemberList, memberListChannelId, memberListUsesChannelOverride]); useEffect(() => { const handleChannelDetailsOpen = (payload?: unknown) => { const {initialTab} = (payload ?? {}) as {initialTab?: 'members' | 'pins'}; setInitialTab(initialTab || 'members'); setChannelDetailsOpen(true); }; return ComponentBus.subscribe('CHANNEL_DETAILS_OPEN', handleChannelDetailsOpen); }, []); useEffect(() => { if (!showMembersToggle) return; return ComponentBus.subscribe('CHANNEL_MEMBER_LIST_TOGGLE', () => { if (canFitMemberList) { if (memberListUsesChannelOverride) { MemberList.toggleDefaultHiddenChannelMembers(memberListChannelId); return; } LayoutCommands.toggleMembers(!isMembersOpen); } }); }, [showMembersToggle, canFitMemberList, isMembersOpen, memberListChannelId, memberListUsesChannelOverride]); useEffect(() => { if (!channel?.topic) { setIsTopicOverflowing(false); return; } const el = topicButtonRef.current; if (!el) return; let rafId: number | null = null; const checkOverflow = () => { rafId = null; const {scrollWidth, clientWidth} = el; setIsTopicOverflowing(scrollWidth - clientWidth > 1); }; const scheduleOverflowCheck = () => { if (rafId != null) return; rafId = requestAnimationFrame(checkOverflow); }; scheduleOverflowCheck(); const resizeObserver = new ResizeObserver(scheduleOverflowCheck); resizeObserver.observe(el); return () => { if (rafId != null) { cancelAnimationFrame(rafId); } resizeObserver.disconnect(); }; }, [channel?.topic]); const handleOpenUserProfile = useCallback(() => { if (!recipient) return; UserProfileCommands.openUserProfile(recipient.id); }, [recipient]); const handleBackClick = useCallback(() => { if (onBackClick) { onBackClick(); return; } if (isDM || isGroupDM || isPersonalNotes) { RouterUtils.transitionTo(Routes.ME); } else if (Routes.isFavoritesRoute(location.pathname)) { RouterUtils.transitionTo(Routes.FAVORITES); } else if (isGuildChannel && channel?.guildId) { NavigationCommands.selectChannel(channel.guildId); } else { goBackOr(Routes.ME); } }, [onBackClick, isDM, isGroupDM, isPersonalNotes, isGuildChannel, channel?.guildId, location.pathname]); const handleChannelDetailsClick = () => { setInitialTab('members'); setChannelDetailsOpen(true); }; const handleOpenChannelTopic = useCallback(() => { if (!channel) return; ModalCommands.push( modal(() => ), ); }, [channel]); const handleSearchClick = () => { setSearchSheetOpen(true); }; const handleContextMenu = useCallback( (event: React.MouseEvent) => { if (channel && isGuildChannel) { event.preventDefault(); event.stopPropagation(); ContextMenuCommands.openFromEvent(event, ({onClose}) => ( )); } }, [channel, isGuildChannel], ); const handleUserContextMenu = useCallback( (event: React.MouseEvent) => { if (!channel) return; event.preventDefault(); event.stopPropagation(); if (isGroupDM) { ContextMenuCommands.openFromEvent(event, ({onClose}) => ( )); } else if (isDM && recipient) { ContextMenuCommands.openFromEvent(event, ({onClose}) => ( )); } }, [channel, isDM, isGroupDM, recipient], ); const handleMobileVoiceCall = useCallback( async (event: React.MouseEvent) => { if (!channel) return; const isConnected = MediaEngine.connected; const connectedChannelId = MediaEngine.channelId; const isInCall = isConnected && connectedChannelId === channel.id; if (isInCall) { void CallCommands.leaveCall(channel.id); } else if (CallState.hasActiveCall(channel.id)) { CallCommands.joinCall(channel.id); } else { await CallUtils.requestStartCall( i18n, channel.id, CallUtils.getCallStartRequestOptions(event, {kind: 'voice'}), ); } }, [channel, i18n], ); const handleMobileVideoCall = useCallback( async (event: React.MouseEvent) => { if (!channel) return; const isConnected = MediaEngine.connected; const connectedChannelId = MediaEngine.channelId; const isInCall = isConnected && connectedChannelId === channel.id; if (isInCall) { void CallCommands.leaveCall(channel.id); } else if (CallState.hasActiveCall(channel.id)) { CallCommands.joinCall(channel.id); } else { await CallUtils.requestStartCall( i18n, channel.id, CallUtils.getCallStartRequestOptions(event, {kind: 'video'}), ); } }, [channel, i18n], ); const handleToggleFavorite = useCallback(() => { if (!channel || isPersonalNotes) return; if (isFavorited) { Favorites.removeChannel(channel.id); ToastCommands.createToast({type: 'success', children: i18n._(CHANNEL_REMOVED_FROM_FAVORITES_DESCRIPTOR)}); } else { Favorites.addChannel(channel.id, channel.guildId ?? ME); ToastCommands.createToast({type: 'success', children: i18n._(CHANNEL_ADDED_TO_FAVORITES_DESCRIPTOR)}); } }, [channel, isPersonalNotes, isFavorited]); const handleFavoriteContextMenu = useCallback( (event: React.MouseEvent) => { event.preventDefault(); event.stopPropagation(); ContextMenuCommands.openFromEvent(event, ({onClose}) => ( } onClick={() => { onClose(); FavoritesCommands.confirmHideFavorites(undefined, i18n); }} danger data-flx="channel.channel-header.handle-favorite-context-menu.menu-item.close" > {i18n._(HIDE_FAVORITES_DESCRIPTOR)} )); }, [i18n], ); const isGroupDMFull = isGroupDmFull(channel); const isFriendDM = isDM && recipient && !isBotDMRecipient && Relationships.getRelationship(recipient.id)?.type === RelationshipTypes.FRIEND; const shouldShowCreateGroupButton = !!channel && !isMobile && !isPersonalNotes && isFriendDM && !isGroupDM; const shouldShowAddFriendsButton = !!channel && !isMobile && !isPersonalNotes && isGroupDM && !isGroupDMFull; const hasChannel = Boolean(channel) && !isPersonalNotes; const hasCallableRecipients = hasChannel && (isDM || isGroupDM); let desktopLeadingActionCount = 0; if (hasChannel && isGuildChannel) { desktopLeadingActionCount += 1; } if (hasCallableRecipients && !(isDM && isBotDMRecipient)) { desktopLeadingActionCount += 2; } if (showPins && Boolean(channel)) { desktopLeadingActionCount += 1; } if (hasChannel && isFriendDM && !isGroupDM) { desktopLeadingActionCount += 1; } if (hasChannel && isGroupDM && !isGroupDMFull) { desktopLeadingActionCount += 1; } if (hasChannel && favoritesVisible) { desktopLeadingActionCount += 1; } if (showMembersToggle) { desktopLeadingActionCount += 1; } let mobileActionCount = 0; if (hasChannel && favoritesVisible) { mobileActionCount += 1; } if (hasCallableRecipients) { mobileActionCount += 2; } if (isGuildChannel) { mobileActionCount += 1; } const searchPanelOpen = Boolean(isSearchResultsVisible); useSkeletonLayoutReport( () => { if (channelId == null || channelType == null) { return; } const nameElement = dmNameRef.current ?? groupDMNameRef.current ?? guildChannelNameRef.current; reportSkeletonChannelProjection(channelId, channelType, { showTopic: channelHasTopic, nameWidthPx: measureSkeletonWidthPx(nameElement), topicWidthPx: measureSkeletonTextWidthPx(topicButtonRef.current), desktopLeadingActionCount, mobileActionCount, memberListVisible: isMembersToggleOpen, searchPanelOpen, }); }, `${channelId}|${channelType ?? ''}|${channelHasTopic}|${channel?.topic ?? ''}|${channelName}|${directMessageName}|${groupDMName}|${desktopLeadingActionCount}|${mobileActionCount}|${searchPanelOpen}|${isMembersToggleOpen}|${Accessibility.fontSize}|${Accessibility.zoomLevel}`, ); return ( <>
{isMobile ? ( ) : ( )}
{leftContent ? ( leftContent ) : channel ? ( isMobile ? ( ) : isDM && recipient ? ( ) : isGroupDM ? ( isMobile ? (
{groupDMName}
) : (
{ if (isKeyboardActivationKey(event.key)) { event.preventDefault(); handleOpenEditGroup(); } }} data-flx="channel.channel-header.group-dm-header-trigger.open-edit-group" >
{groupDMName}
) ) : isPersonalNotes ? (
{ChannelUtils.getIcon(channel, {className: styles.channelIcon}, e2eeIconOptions)} {channelName}
) : (
{ChannelUtils.getIcon(channel, {className: styles.channelIcon}, e2eeIconOptions)} {channelName} {channel.topic && ( <> •
{ if (!isKeyboardActivationKey(e.key)) return; e.preventDefault(); handleOpenChannelTopic(); }} tabIndex={0} data-flx="channel.channel-header.topic-button.push" >
)}
) ) : null}
{voiceCallHeaderSupplement && (
{voiceCallHeaderSupplement}
)} {isMobile && channel && !isPersonalNotes && Accessibility.showFavorites && ( )} {isMobile && (isDM || isGroupDM) && !isPersonalNotes && ( <> )} {isMobile && isGuildChannel && ( )} {channel && isGuildChannel && !isMobile && !isPersonalNotes && ( )} {(isDM || isGroupDM) && channel && !isMobile && !(isDM && isBotDMRecipient) && ( <> )} {showPins && channel && !isMobile && ( )} {shouldShowCreateGroupButton && ( )} {shouldShowAddFriendsButton && ( )} {channel && !isMobile && !isPersonalNotes && Accessibility.showFavorites && ( )} {showMembersToggle && !isMobile && ( )} {!isMobile && channel && !isPersonalNotes && (
)} {!isMobile && } {!isMobile && } {!isMobile && }
{channel && ( <> { setChannelDetailsOpen(false); setInitialTab('members'); }} channel={channel} initialTab={initialTab} data-flx="channel.channel-header.channel-details-bottom-sheet" /> setSearchSheetOpen(false)} channel={channel} data-flx="channel.channel-header.channel-search-bottom-sheet" /> )} ); }, );