diff --git a/web/chat/thread-menu.css b/web/chat/thread-menu.css
index 31dd25cac..7e2503323 100644
--- a/web/chat/thread-menu.css
+++ b/web/chat/thread-menu.css
@@ -1,59 +1,7 @@
-button.topBarMenuButton {
- background-color: transparent;
- border: none;
- cursor: pointer;
- color: var(--thread-top-bar-menu-color);
-}
-
-div.topBarMenuActionList {
- position: absolute;
- right: 10px;
- top: 55px;
- z-index: 1;
- display: flex;
- flex-direction: column;
- background-color: var(--thread-menu-bg);
- border-radius: 4px;
- padding: 4px 0;
-}
-
-button.topBarMenuAction {
- z-index: 1;
- background-color: transparent;
- padding: 12px 16px;
- color: var(--thread-menu-color);
- background-color: var(--thread-menu-bg);
- font-size: var(--m-font-16);
- line-height: 1.5;
- border: none;
- cursor: pointer;
- display: flex;
- align-items: center;
-}
-
-button.topBarMenuAction:hover {
- color: var(--thread-menu-color-hover);
-}
-
-div.topBarMenuActionIcon {
- font-size: var(--l-font-18);
- display: flex;
- justify-content: center;
- margin-right: 8px;
- width: 20px;
-}
-
-button.topBarMenuActionDangerous {
- color: var(--thread-menu-color-dangerous);
-}
-button.topBarMenuActionDangerous:hover {
- color: var(--thread-menu-color-dangerous-hover);
-}
-
hr.separator {
height: 1px;
- background: var(--thread-menu-separator-color);
+ background: var(--menu-separator-color);
margin: 10px 16px;
max-width: 130px;
border: none;
}
diff --git a/web/chat/thread-menu.react.js b/web/chat/thread-menu.react.js
index 0f73d720e..f7b5cca0f 100644
--- a/web/chat/thread-menu.react.js
+++ b/web/chat/thread-menu.react.js
@@ -1,255 +1,216 @@
// @flow
import {
faArrowRight,
faBell,
faCog,
faCommentAlt,
faSignOutAlt,
faPlusCircle,
faUserFriends,
} from '@fortawesome/free-solid-svg-icons';
import * as React from 'react';
import {
leaveThread,
leaveThreadActionTypes,
} from 'lib/actions/thread-actions';
import { childThreadInfos } from 'lib/selectors/thread-selectors';
import {
threadHasPermission,
viewerIsMember,
threadIsChannel,
} from 'lib/shared/thread-utils';
import {
type ThreadInfo,
threadTypes,
threadPermissions,
} from 'lib/types/thread-types';
import {
useServerCall,
useDispatchActionPromise,
} from 'lib/utils/action-utils';
+import MenuItem from '../components/menu-item.react';
+import Menu from '../components/menu.react';
import SidebarListModal from '../modals/chat/sidebar-list-modal.react';
import { useModalContext } from '../modals/modal-provider.react';
import ConfirmLeaveThreadModal from '../modals/threads/confirm-leave-thread-modal.react';
import ThreadSettingsModal from '../modals/threads/thread-settings-modal.react';
import { useSelector } from '../redux/redux-utils';
import SWMansionIcon from '../SWMansionIcon.react';
-import ThreadMenuItem from './thread-menu-item.react';
import css from './thread-menu.css';
type ThreadMenuProps = {
+threadInfo: ThreadInfo,
};
function ThreadMenu(props: ThreadMenuProps): React.Node {
- const [isOpen, setIsOpen] = React.useState(false);
-
const { setModal, clearModal } = useModalContext();
const { threadInfo } = props;
const onClickSettings = React.useCallback(
() => setModal(),
[setModal, threadInfo.id],
);
const settingsItem = React.useMemo(() => {
return (
-
);
}, [onClickSettings]);
const membersItem = React.useMemo(() => {
if (threadInfo.type === threadTypes.PERSONAL) {
return null;
}
- return ;
+ return ;
}, [threadInfo.type]);
const childThreads = useSelector(
state => childThreadInfos(state)[threadInfo.id],
);
const hasSidebars = React.useMemo(() => {
return childThreads?.some(
childThreadInfo => childThreadInfo.type === threadTypes.SIDEBAR,
);
}, [childThreads]);
const onClickSidebars = React.useCallback(
() => setModal(),
[setModal, threadInfo],
);
const sidebarItem = React.useMemo(() => {
if (!hasSidebars) {
return null;
}
return (
-
);
}, [hasSidebars, onClickSidebars]);
const canCreateSubchannels = React.useMemo(
() => threadHasPermission(threadInfo, threadPermissions.CREATE_SUBCHANNELS),
[threadInfo],
);
const hasSubchannels = React.useMemo(() => {
return !!childThreads?.some(threadIsChannel);
}, [childThreads]);
const viewSubchannelsItem = React.useMemo(() => {
if (!hasSubchannels && !canCreateSubchannels) {
return null;
}
return (
-
+
);
}, [canCreateSubchannels, hasSubchannels]);
const createSubchannelsItem = React.useMemo(() => {
if (!canCreateSubchannels) {
return null;
}
return (
-
);
}, [canCreateSubchannels]);
const dispatchActionPromise = useDispatchActionPromise();
const callLeaveThread = useServerCall(leaveThread);
const onConfirmLeaveThread = React.useCallback(() => {
dispatchActionPromise(
leaveThreadActionTypes,
callLeaveThread(threadInfo.id),
);
clearModal();
}, [callLeaveThread, clearModal, dispatchActionPromise, threadInfo.id]);
const onClickLeaveThread = React.useCallback(
() =>
setModal(
,
),
[clearModal, onConfirmLeaveThread, setModal, threadInfo],
);
const leaveThreadItem = React.useMemo(() => {
const canLeaveThread = threadHasPermission(
threadInfo,
threadPermissions.LEAVE_THREAD,
);
if (!viewerIsMember(threadInfo) || !canLeaveThread) {
return null;
}
return (
-
);
}, [onClickLeaveThread, threadInfo]);
const menuItems = React.useMemo(() => {
const notificationsItem = (
-
+
);
const separator =
;
// TODO: Enable menu items when the modals are implemented
const SHOW_NOTIFICATIONS = false;
const SHOW_MEMBERS = false;
const SHOW_VIEW_SUBCHANNELS = false;
const SHOW_CREATE_SUBCHANNELS = false;
const items = [
settingsItem,
SHOW_NOTIFICATIONS && notificationsItem,
SHOW_MEMBERS && membersItem,
sidebarItem,
SHOW_VIEW_SUBCHANNELS && viewSubchannelsItem,
SHOW_CREATE_SUBCHANNELS && createSubchannelsItem,
leaveThreadItem && separator,
leaveThreadItem,
];
return items.filter(Boolean);
}, [
settingsItem,
membersItem,
sidebarItem,
viewSubchannelsItem,
createSubchannelsItem,
leaveThreadItem,
]);
-
- const closeMenuCallback = React.useCallback(() => {
- document.removeEventListener('click', closeMenuCallback);
- if (isOpen) {
- setIsOpen(false);
- }
- }, [isOpen]);
-
- React.useEffect(() => {
- if (!document || !isOpen) {
- return undefined;
- }
- document.addEventListener('click', closeMenuCallback);
- return () => document.removeEventListener('click', closeMenuCallback);
- }, [closeMenuCallback, isOpen]);
-
- const switchMenuCallback = React.useCallback(() => {
- setIsOpen(isMenuOpen => !isMenuOpen);
- }, []);
-
- if (menuItems.length === 0) {
- return null;
- }
-
- let menuActionList = null;
- if (isOpen) {
- menuActionList = (
- {menuItems}
- );
- }
-
- return (
- <>
-
- {menuActionList}
- >
+ const icon = React.useMemo(
+ () => ,
+ [],
);
+ return ;
}
export default ThreadMenu;
diff --git a/web/chat/thread-menu-item.react.js b/web/components/menu-item.react.js
similarity index 57%
rename from web/chat/thread-menu-item.react.js
rename to web/components/menu-item.react.js
index 5e93597f6..9a16b6cdb 100644
--- a/web/chat/thread-menu-item.react.js
+++ b/web/components/menu-item.react.js
@@ -1,37 +1,37 @@
// @flow
import type { IconDefinition } from '@fortawesome/fontawesome-common-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames';
import * as React from 'react';
-import css from './thread-menu.css';
+import css from './menu.css';
-type ThreadMenuItemProps = {
+type MenuItemProps = {
+onClick?: () => mixed,
+icon: IconDefinition,
+text: string,
+dangerous?: boolean,
};
-function ThreadMenuItem(props: ThreadMenuItemProps): React.Node {
+function MenuItem(props: MenuItemProps): React.Node {
const { onClick, icon, text, dangerous } = props;
- const itemClasses = classNames(css.topBarMenuAction, {
- [css.topBarMenuActionDangerous]: dangerous,
+ const itemClasses = classNames(css.menuAction, {
+ [css.menuActionDangerous]: dangerous,
});
return (