Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3295133
D3341.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
D3341.diff
View Options
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 <NavigationPanel />;
+ 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<HTMLAnchorElement>) => {
+ 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 = <span className={css.chatBadge}>{boundUnreadCount}</span>;
+ }
+
+ const chatNavigationItem = React.useMemo(
+ () => (
+ <NavigationPanel.Item tab="chat">
+ <p>
+ <span className={css.chatIconWrapper}>
+ <SWMansionIcon icon="message-square" size={24} />
+ {chatBadge}
+ </span>
+ <a onClick={onClickChat}>Chat</a>
+ </p>
+ </NavigationPanel.Item>
+ ),
+ [chatBadge, onClickChat],
+ );
+
+ const onClickCalendar = React.useCallback(
+ (event: SyntheticEvent<HTMLAnchorElement>) => {
+ 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 (
+ <NavigationPanel.Item tab="calendar">
+ <p>
+ <SWMansionIcon icon="calendar" size={24} />
+ <a onClick={onClickCalendar}>Calendar</a>
+ </p>
+ </NavigationPanel.Item>
+ );
+ }, [isCalendarEnabled, onClickCalendar]);
+
+ const onClickApps = React.useCallback(
+ (event: SyntheticEvent<HTMLAnchorElement>) => {
+ event.preventDefault();
+ dispatch({
+ type: updateNavInfoActionType,
+ payload: {
+ tab: 'apps',
+ },
+ });
+ },
+ [dispatch],
+ );
+
+ const appNavigationItem = React.useMemo(
+ () => (
+ <NavigationPanel.Item tab="apps">
+ <p>
+ <SWMansionIcon icon="wrench" size={24} />
+ <a onClick={onClickApps}>Apps</a>
+ </p>
+ </NavigationPanel.Item>
+ ),
+ [onClickApps],
+ );
+
+ return (
+ <NavigationPanel.Container>
+ {chatNavigationItem}
+ {calendarNavigationItem}
+ {appNavigationItem}
+ </NavigationPanel.Container>
+ );
}
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<HTMLAnchorElement>) => {
- event.preventDefault();
- dispatch({
- type: updateNavInfoActionType,
- payload: { tab: 'calendar' },
- });
- },
- [dispatch],
- );
+type NavigationPanelContainerProps = {
+ +children: React.ChildrenArray<?React.Element<typeof NavigationPanelItem>>,
+};
- const onClickChat = React.useCallback(
- (event: SyntheticEvent<HTMLAnchorElement>) => {
- 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<HTMLAnchorElement>) => {
- event.preventDefault();
- dispatch({
- type: updateNavInfoActionType,
- payload: {
- tab: 'apps',
- },
- });
- },
- [dispatch],
+ const items = React.useMemo(
+ () =>
+ React.Children.map(children, child => {
+ if (!child) {
+ return null;
+ }
+ return (
+ <li
+ key={child.props.tab}
+ className={classNames({
+ [css['current-tab']]: navInfo.tab === child.props.tab,
+ })}
+ >
+ {child}
+ </li>
+ );
+ }),
+ [children, navInfo.tab],
);
- const boundUnreadCount = useSelector(unreadCount);
-
- invariant(viewerID, 'should be set');
- let chatBadge = null;
- if (boundUnreadCount > 0) {
- chatBadge = <span className={css.chatBadge}>{boundUnreadCount}</span>;
- }
-
- 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 (
- <li>
- <p className={calendarNavClasses}>
- <SWMansionIcon icon="calendar" size={24} />
- <a onClick={onClickCalendar}>Calendar</a>
- </p>
- </li>
- );
- }, [isCalendarEnabled, navInfo.tab, onClickCalendar]);
-
return (
<div className={css.navigationPanelContainer}>
- <ul>
- <li>
- <p className={chatNavClasses}>
- <span className={css.chatIconWrapper}>
- <SWMansionIcon icon="message-square" size={24} />
- {chatBadge}
- </span>
- <a onClick={onClickChat}>Chat</a>
- </p>
- </li>
- {calendarLink}
- <li>
- <p className={appsNavClasses}>
- <SWMansionIcon icon="wrench" size={24} />
- <a onClick={onClickApps}>Apps</a>
- </p>
- </li>
- </ul>
+ <ul>{items}</ul>
</div>
);
}
+const NavigationPanel = {
+ Item: NavigationPanelItem,
+ Container: NavigationPanelContainer,
+};
+
export default NavigationPanel;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Nov 17, 9:00 PM (20 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2531815
Default Alt Text
D3341.diff (9 KB)
Attached To
Mode
D3341: [web] Make NavigationPanel reusable
Attached
Detach File
Event Timeline
Log In to Comment