diff --git a/web/chat/thread-menu.react.js b/web/chat/thread-menu.react.js index e7cdcc3f8..d64483960 100644 --- a/web/chat/thread-menu.react.js +++ b/web/chat/thread-menu.react.js @@ -1,250 +1,263 @@ // @flow import * as React from 'react'; import { leaveThread, leaveThreadActionTypes, } from 'lib/actions/thread-actions'; import { usePromoteSidebar } from 'lib/hooks/promote-sidebar.react'; 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 { isDev } from 'lib/utils/dev-utils'; import MenuItem from '../components/menu-item.react'; import Menu from '../components/menu.react'; import SidebarListModal from '../modals/chat/sidebar-list-modal.react'; +import SidebarPromoteModal from '../modals/chat/sidebar-promote-modal.react'; import { useModalContext } from '../modals/modal-provider.react'; import ConfirmLeaveThreadModal from '../modals/threads/confirm-leave-thread-modal.react'; import ThreadMembersModal from '../modals/threads/members/members-modal.react'; import SubchannelsModal from '../modals/threads/subchannels/subchannels-modal.react'; import ThreadSettingsModal from '../modals/threads/thread-settings-modal.react'; import { useSelector } from '../redux/redux-utils'; import SWMansionIcon from '../SWMansionIcon.react'; import css from './thread-menu.css'; type ThreadMenuProps = { +threadInfo: ThreadInfo, }; function ThreadMenu(props: ThreadMenuProps): React.Node { const { pushModal, popModal } = useModalContext(); const { threadInfo } = props; const { onPromoteSidebar } = usePromoteSidebar(threadInfo); const onClickSettings = React.useCallback( () => pushModal(), [pushModal, threadInfo.id], ); const settingsItem = React.useMemo(() => { return ( ); }, [onClickSettings]); const onClickMembers = React.useCallback( () => pushModal( , ), [popModal, pushModal, threadInfo.id], ); const membersItem = React.useMemo(() => { if (threadInfo.type === threadTypes.PERSONAL) { return null; } return ( ); }, [onClickMembers, 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( () => pushModal(), [pushModal, 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 onClickViewSubchannels = React.useCallback( () => pushModal( , ), [popModal, pushModal, threadInfo.id], ); const viewSubchannelsItem = React.useMemo(() => { if (!hasSubchannels) { return null; } return ( ); }, [hasSubchannels, onClickViewSubchannels]); 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), ); popModal(); }, [callLeaveThread, popModal, dispatchActionPromise, threadInfo.id]); const onClickLeaveThread = React.useCallback( () => pushModal( , ), [popModal, onConfirmLeaveThread, pushModal, threadInfo], ); const leaveThreadItem = React.useMemo(() => { const canLeaveThread = threadHasPermission( threadInfo, threadPermissions.LEAVE_THREAD, ); if (!viewerIsMember(threadInfo) || !canLeaveThread) { return null; } return ( ); }, [onClickLeaveThread, threadInfo]); + const onClickPromoteSidebarToThread = React.useCallback( + () => + pushModal( + , + ), + [pushModal, threadInfo, popModal, onPromoteSidebar], + ); + const promoteSidebar = React.useMemo(() => { return ( ); - }, [onPromoteSidebar]); + }, [onClickPromoteSidebarToThread]); const menuItems = React.useMemo(() => { const notificationsItem = ( ); const separator =
; // TODO: Enable menu items when the modals are implemented const SHOW_NOTIFICATIONS = false; const SHOW_CREATE_SUBCHANNELS = false; const SHOW_PROMOTE_SIDEBAR = isDev; const items = [ settingsItem, SHOW_NOTIFICATIONS && notificationsItem, membersItem, sidebarItem, viewSubchannelsItem, SHOW_CREATE_SUBCHANNELS && createSubchannelsItem, leaveThreadItem && separator, SHOW_PROMOTE_SIDEBAR && promoteSidebar, leaveThreadItem, ]; return items.filter(Boolean); }, [ settingsItem, membersItem, sidebarItem, viewSubchannelsItem, promoteSidebar, createSubchannelsItem, leaveThreadItem, ]); const icon = React.useMemo( () => , [], ); return {menuItems}; } export default ThreadMenu; diff --git a/web/modals/chat/sidebar-promote-modal.css b/web/modals/chat/sidebar-promote-modal.css new file mode 100644 index 000000000..3dca55379 --- /dev/null +++ b/web/modals/chat/sidebar-promote-modal.css @@ -0,0 +1,16 @@ +div.modal_body { + padding: 20px; + color: var(--fg); + background-color: var(--modal-bg); + border-radius: 16px; + flex: 1; + display: flex; + flex-direction: column; +} + +div.buttonContainer { + padding-top: 32px; + display: flex; + gap: 24px; + justify-content: flex-end; +} diff --git a/web/modals/chat/sidebar-promote-modal.react.js b/web/modals/chat/sidebar-promote-modal.react.js new file mode 100644 index 000000000..9268e37d1 --- /dev/null +++ b/web/modals/chat/sidebar-promote-modal.react.js @@ -0,0 +1,49 @@ +// @flow + +import * as React from 'react'; + +import type { ThreadInfo } from 'lib/types/thread-types'; + +import Button from '../../components/button.react'; +import Modal from '../modal.react'; +import css from './sidebar-promote-modal.css'; + +type Props = { + +onClose: () => void, + +onConfirm: () => void, + +threadInfo: ThreadInfo, +}; + +function SidebarPromoteModal(props: Props): React.Node { + const { threadInfo, onClose, onConfirm } = props; + const { uiName } = threadInfo; + + const handleConfirm = React.useCallback(() => { + onConfirm(); + onClose(); + }, [onClose, onConfirm]); + + return ( + +
+

{`Are you sure you want to promote "${uiName}"?`}

+

Promoting a sidebar to a full thread cannot be undone.

+
+ + +
+
+
+ ); +} + +export default SidebarPromoteModal;