diff --git a/web/sidebar/app-switcher.react.js b/web/sidebar/app-switcher.react.js --- a/web/sidebar/app-switcher.react.js +++ b/web/sidebar/app-switcher.react.js @@ -1,11 +1,139 @@ // @flow +import invariant from 'invariant'; import * as React from 'react'; +import { useDispatch } from 'react-redux'; +import { + mostRecentReadThreadSelector, + unreadCount, +} from 'lib/selectors/thread-selectors'; + +import { useSelector } from '../redux/redux-utils'; +import SWMansionIcon from '../SWMansionIcon.react'; +import { updateNavInfoActionType } from '../types/nav-types'; +import css from './left-layout-aside.css'; import NavigationPanel from './navigation-panel.react'; function AppSwitcher(): React.Node { - return ; + const activeChatThreadID = useSelector( + state => state.navInfo.activeChatThreadID, + ); + const mostRecentReadThread = useSelector(mostRecentReadThreadSelector); + const activeThreadCurrentlyUnread = useSelector( + state => + !activeChatThreadID || + !!state.threadStore.threadInfos[activeChatThreadID]?.currentUser.unread, + ); + + const dispatch = useDispatch(); + + const onClickChat = React.useCallback( + (event: SyntheticEvent) => { + event.preventDefault(); + dispatch({ + type: updateNavInfoActionType, + payload: { + tab: 'chat', + activeChatThreadID: activeThreadCurrentlyUnread + ? mostRecentReadThread + : activeChatThreadID, + }, + }); + }, + [ + dispatch, + activeThreadCurrentlyUnread, + mostRecentReadThread, + activeChatThreadID, + ], + ); + + const viewerID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + + const boundUnreadCount = useSelector(unreadCount); + + invariant(viewerID, 'should be set'); + let chatBadge = null; + if (boundUnreadCount > 0) { + chatBadge =
{boundUnreadCount}
; + } + + const chatNavigationItem = React.useMemo( + () => ( + +

+ + + {chatBadge} + + Chat +

+
+ ), + [chatBadge, onClickChat], + ); + + const onClickCalendar = React.useCallback( + (event: SyntheticEvent) => { + event.preventDefault(); + dispatch({ + type: updateNavInfoActionType, + payload: { tab: 'calendar' }, + }); + }, + [dispatch], + ); + + const isCalendarEnabled = useSelector(state => state.enabledApps.calendar); + const calendarNavigationItem = React.useMemo(() => { + if (!isCalendarEnabled) { + return null; + } + return ( + +

+ + Calendar +

+
+ ); + }, [isCalendarEnabled, onClickCalendar]); + + const onClickApps = React.useCallback( + (event: SyntheticEvent) => { + event.preventDefault(); + dispatch({ + type: updateNavInfoActionType, + payload: { + tab: 'apps', + }, + }); + }, + [dispatch], + ); + + const appNavigationItem = React.useMemo( + () => ( + +

+ + Apps +

+
+ ), + [onClickApps], + ); + + return ( + + {chatNavigationItem} + {calendarNavigationItem} + {appNavigationItem} + + ); } export default AppSwitcher; diff --git a/web/sidebar/left-layout-aside.css b/web/sidebar/left-layout-aside.css --- a/web/sidebar/left-layout-aside.css +++ b/web/sidebar/left-layout-aside.css @@ -57,12 +57,12 @@ line-height: 1.25; } -p.current-tab svg { +li.current-tab svg { color: var(--fg); } -p.current-tab a, -p.current-tab svg { +li.current-tab a, +li.current-tab svg { fill: var(--fg); color: var(--fg); } diff --git a/web/sidebar/navigation-panel.react.js b/web/sidebar/navigation-panel.react.js --- a/web/sidebar/navigation-panel.react.js +++ b/web/sidebar/navigation-panel.react.js @@ -1,138 +1,62 @@ // @flow import classNames from 'classnames'; -import invariant from 'invariant'; import * as React from 'react'; -import { useDispatch } from 'react-redux'; - -import { - mostRecentReadThreadSelector, - unreadCount, -} from 'lib/selectors/thread-selectors'; import { useSelector } from '../redux/redux-utils'; -import SWMansionIcon from '../SWMansionIcon.react'; -import { updateNavInfoActionType } from '../types/nav-types'; +import type { NavigationTab } from '../types/nav-types'; import css from './left-layout-aside.css'; -function NavigationPanel(): React.Node { - const activeChatThreadID = useSelector( - state => state.navInfo.activeChatThreadID, - ); - const navInfo = useSelector(state => state.navInfo); - const mostRecentReadThread = useSelector(mostRecentReadThreadSelector); - const activeThreadCurrentlyUnread = useSelector( - state => - !activeChatThreadID || - !!state.threadStore.threadInfos[activeChatThreadID]?.currentUser.unread, - ); - const viewerID = useSelector( - state => state.currentUserInfo && state.currentUserInfo.id, - ); - - const isCalendarEnabled = useSelector(state => state.enabledApps.calendar); +type NavigationPanelItemProps = { + +tab: NavigationTab, + +children: React.Node, +}; - const dispatch = useDispatch(); +function NavigationPanelItem(props: NavigationPanelItemProps): React.Node { + const { children } = props; + return children; +} - const onClickCalendar = React.useCallback( - (event: SyntheticEvent) => { - event.preventDefault(); - dispatch({ - type: updateNavInfoActionType, - payload: { tab: 'calendar' }, - }); - }, - [dispatch], - ); +type NavigationPanelContainerProps = { + +children: React.ChildrenArray>, +}; - const onClickChat = React.useCallback( - (event: SyntheticEvent) => { - event.preventDefault(); - dispatch({ - type: updateNavInfoActionType, - payload: { - tab: 'chat', - activeChatThreadID: activeThreadCurrentlyUnread - ? mostRecentReadThread - : activeChatThreadID, - }, - }); - }, - [ - dispatch, - activeThreadCurrentlyUnread, - mostRecentReadThread, - activeChatThreadID, - ], - ); +function NavigationPanelContainer( + props: NavigationPanelContainerProps, +): React.Node { + const { children } = props; + const navInfo = useSelector(state => state.navInfo); - const onClickApps = React.useCallback( - (event: SyntheticEvent) => { - event.preventDefault(); - dispatch({ - type: updateNavInfoActionType, - payload: { - tab: 'apps', - }, - }); - }, - [dispatch], + const items = React.useMemo( + () => + React.Children.map(children, child => { + if (!child) { + return null; + } + return ( +
  • + {child} +
  • + ); + }), + [children, navInfo.tab], ); - const boundUnreadCount = useSelector(unreadCount); - - invariant(viewerID, 'should be set'); - let chatBadge = null; - if (boundUnreadCount > 0) { - chatBadge =
    {boundUnreadCount}
    ; - } - - const chatNavClasses = classNames({ - [css['current-tab']]: navInfo.tab === 'chat', - }); - const appsNavClasses = classNames({ - [css['current-tab']]: navInfo.tab === 'apps', - }); - - const calendarLink = React.useMemo(() => { - if (!isCalendarEnabled) { - return null; - } - const calendarNavClasses = classNames({ - [css['current-tab']]: navInfo.tab === 'calendar', - }); - return ( -
  • -

    - - Calendar -

    -
  • - ); - }, [isCalendarEnabled, navInfo.tab, onClickCalendar]); - return (
    -
      -
    • -

      - - - {chatBadge} - - Chat -

      -
    • - {calendarLink} -
    • -

      - - Apps -

      -
    • -
    +
      {items}
    ); } +const NavigationPanel = { + Item: NavigationPanelItem, + Container: NavigationPanelContainer, +}; + export default NavigationPanel;