// Copyright 2023 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import type { Key, ReactNode } from 'react'; import React, { useState } from 'react'; import { Tabs, TabList, Tab, TabPanel } from 'react-aria-components'; import classNames from 'classnames'; import { Avatar, AvatarSize } from './Avatar'; import type { LocalizerType, ThemeType } from '../types/Util'; import type { ConversationType } from '../state/ducks/conversations'; import type { BadgeType } from '../badges/types'; import { NavTab, ProfileEditorPage, SettingsPage } from '../types/Nav'; import type { Location } from '../types/Nav'; import { Tooltip, TooltipPlacement } from './Tooltip'; import { Theme } from '../util/theme'; import type { UnreadStats } from '../util/countUnreadStats'; import { ProfileMovedModal } from './ProfileMovedModal'; type NavTabsItemBadgesProps = Readonly<{ i18n: LocalizerType; hasError?: boolean; hasPendingUpdate?: boolean; unreadStats: UnreadStats | null; }>; function NavTabsItemBadges({ i18n, hasError, hasPendingUpdate, unreadStats, }: NavTabsItemBadgesProps) { if (hasError) { return ( {i18n('icu:NavTabs__ItemIconLabel--HasError')} ! ); } if (hasPendingUpdate) { return
; } if (unreadStats != null) { if (unreadStats.unreadCount > 0) { return ( {i18n('icu:NavTabs__ItemIconLabel--UnreadCount', { count: unreadStats.unreadCount, })} {unreadStats.unreadCount} ); } if (unreadStats.markedUnread) { return ( {i18n('icu:NavTabs__ItemIconLabel--MarkedUnread')} ); } } return null; } type NavTabProps = Readonly<{ hasError?: boolean; i18n: LocalizerType; iconClassName: string; id: NavTab; label: string; navTabClassName: string; unreadStats: UnreadStats | null; hasPendingUpdate?: boolean; }>; function NavTabsItem({ hasError, i18n, iconClassName, id, label, navTabClassName, unreadStats, hasPendingUpdate, }: NavTabProps) { const isRTL = i18n.getLocaleDirection() === 'rtl'; return ( {label} ); } export type NavTabPanelProps = Readonly<{ otherTabsUnreadStats: UnreadStats; collapsed: boolean; hasFailedStorySends: boolean; hasPendingUpdate: boolean; onToggleCollapse(collapsed: boolean): void; }>; export type NavTabsToggleProps = Readonly<{ otherTabsUnreadStats: UnreadStats | null; i18n: LocalizerType; hasFailedStorySends: boolean; hasPendingUpdate: boolean; navTabsCollapsed: boolean; onToggleNavTabsCollapse(navTabsCollapsed: boolean): void; }>; export function NavTabsToggle({ i18n, hasFailedStorySends, hasPendingUpdate, navTabsCollapsed, otherTabsUnreadStats, onToggleNavTabsCollapse, }: NavTabsToggleProps): JSX.Element { function handleToggle() { onToggleNavTabsCollapse(!navTabsCollapsed); } const label = navTabsCollapsed ? i18n('icu:NavTabsToggle__showTabs') : i18n('icu:NavTabsToggle__hideTabs'); const isRTL = i18n.getLocaleDirection() === 'rtl'; return ( ); } export type NavTabsProps = Readonly<{ badge: BadgeType | undefined; hasFailedStorySends: boolean; hasPendingUpdate: boolean; i18n: LocalizerType; me: ConversationType; navTabsCollapsed: boolean; onChangeLocation: (location: Location) => void; onDismissProfileMovedModal: () => void; onToggleNavTabsCollapse: (collapsed: boolean) => void; profileMovedModalNeeded: boolean; renderCallsTab: () => ReactNode; renderChatsTab: () => ReactNode; renderStoriesTab: () => ReactNode; renderSettingsTab: () => ReactNode; selectedNavTab: NavTab; shouldShowProfileIcon: boolean; storiesEnabled: boolean; theme: ThemeType; unreadCallsCount: number; unreadConversationsStats: UnreadStats; unreadStoriesCount: number; }>; export function NavTabs({ badge, hasFailedStorySends, hasPendingUpdate, i18n, me, navTabsCollapsed, onChangeLocation, onDismissProfileMovedModal, onToggleNavTabsCollapse, profileMovedModalNeeded, renderCallsTab, renderChatsTab, renderStoriesTab, renderSettingsTab, selectedNavTab, shouldShowProfileIcon, storiesEnabled, theme, unreadCallsCount, unreadConversationsStats, unreadStoriesCount, }: NavTabsProps): JSX.Element { const [showingProfileMovedModal, setShowingProfileMovedModal] = useState(false); function handleSelectionChange(key: Key) { const tab = key as NavTab; if (tab === NavTab.Settings) { onChangeLocation({ tab: NavTab.Settings, details: { page: SettingsPage.Profile, state: ProfileEditorPage.None, }, }); } else { onChangeLocation({ tab }); } } const isRTL = i18n.getLocaleDirection() === 'rtl'; return ( {showingProfileMovedModal ? ( { setShowingProfileMovedModal(false); onDismissProfileMovedModal(); }} theme={theme} /> ) : undefined} {renderChatsTab} {renderCallsTab} {renderStoriesTab} {renderSettingsTab} ); }