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,111 @@ // @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 viewerID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + + const dispatch = useDispatch(); + + const onClickCalendar = React.useCallback( + (event: SyntheticEvent) => { + event.preventDefault(); + dispatch({ + type: updateNavInfoActionType, + payload: { tab: 'calendar' }, + }); + }, + [dispatch], + ); + + const onClickChat = React.useCallback( + (event: SyntheticEvent) => { + event.preventDefault(); + dispatch({ + type: updateNavInfoActionType, + payload: { + tab: 'chat', + activeChatThreadID: activeThreadCurrentlyUnread + ? mostRecentReadThread + : activeChatThreadID, + }, + }); + }, + [ + dispatch, + activeThreadCurrentlyUnread, + mostRecentReadThread, + activeChatThreadID, + ], + ); + + 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 calendarNavigationItem = React.useMemo( + () => ( +

+ + Calendar +

+ ), + [onClickCalendar], + ); + + const navigationItems = React.useMemo( + () => [ + { tab: 'chat', link: chatNavigationItem }, + { + tab: 'calendar', + link: calendarNavigationItem, + }, + ], + [calendarNavigationItem, chatNavigationItem], + ); + + return ; } 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,103 +1,41 @@ // @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 dispatch = useDispatch(); +type Props = { + +navigationItems: $ReadOnlyArray<{ + +tab: NavigationTab, + +link: React.Node, + }>, +}; - const onClickCalendar = React.useCallback( - (event: SyntheticEvent) => { - event.preventDefault(); - dispatch({ - type: updateNavInfoActionType, - payload: { tab: 'calendar' }, - }); - }, - [dispatch], - ); +function NavigationPanel(props: Props): React.Node { + const { navigationItems } = props; + const navInfo = useSelector(state => state.navInfo); - const onClickChat = React.useCallback( - (event: SyntheticEvent) => { - event.preventDefault(); - dispatch({ - type: updateNavInfoActionType, - payload: { - tab: 'chat', - activeChatThreadID: activeThreadCurrentlyUnread - ? mostRecentReadThread - : activeChatThreadID, - }, - }); - }, - [ - dispatch, - activeThreadCurrentlyUnread, - mostRecentReadThread, - activeChatThreadID, - ], + const items = React.useMemo( + () => + navigationItems.map(item => ( +
  • + {item.link} +
  • + )), + [navInfo.tab, navigationItems], ); - const boundUnreadCount = useSelector(unreadCount); - - invariant(viewerID, 'should be set'); - let chatBadge = null; - if (boundUnreadCount > 0) { - chatBadge =
    {boundUnreadCount}
    ; - } - - const calendarNavClasses = classNames({ - [css['current-tab']]: navInfo.tab === 'calendar', - }); - const chatNavClasses = classNames({ - [css['current-tab']]: navInfo.tab === 'chat', - }); - return (
    - +
      {items}
    ); }