diff --git a/web/roles/delete-role-modal.css b/web/roles/delete-role-modal.css new file mode 100644 index 000000000..443d4cc4f --- /dev/null +++ b/web/roles/delete-role-modal.css @@ -0,0 +1,20 @@ +.roleDeletionText { + color: var(--modal-fg); + font-size: var(--m-font-14); + padding: 16px 32px; +} + +.buttonsContainer { + display: flex; + flex-direction: row; + justify-content: flex-end; + padding: 16px 32px; +} + +.cancelButton { + width: 100px; +} + +.deleteRoleButton { + margin-left: 8px; +} diff --git a/web/roles/delete-role-modal.react.js b/web/roles/delete-role-modal.react.js new file mode 100644 index 000000000..5caa99bfc --- /dev/null +++ b/web/roles/delete-role-modal.react.js @@ -0,0 +1,111 @@ +// @flow + +import * as React from 'react'; + +import { + deleteCommunityRole, + deleteCommunityRoleActionTypes, +} from 'lib/actions/thread-actions.js'; +import { useModalContext } from 'lib/components/modal-provider.react.js'; +import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; +import { useRoleMemberCountsForCommunity } from 'lib/shared/thread-utils.js'; +import type { LoadingStatus } from 'lib/types/loading-types.js'; +import type { ThreadInfo } from 'lib/types/thread-types.js'; +import { + useServerCall, + useDispatchActionPromise, +} from 'lib/utils/action-utils.js'; +import { constructRoleDeletionMessagePrompt } from 'lib/utils/role-utils.js'; + +import css from './delete-role-modal.css'; +import Button, { buttonThemes } from '../components/button.react.js'; +import LoadingIndicator from '../loading-indicator.react.js'; +import Modal from '../modals/modal.react.js'; +import { useSelector } from '../redux/redux-utils.js'; + +const deleteRoleLoadingStatusSelector = createLoadingStatusSelector( + deleteCommunityRoleActionTypes, +); + +type DeleteRoleModalProps = { + +threadInfo: ThreadInfo, + +defaultRoleID: string, + +roleID: string, +}; + +function DeleteRoleModal(props: DeleteRoleModalProps): React.Node { + const { threadInfo, defaultRoleID, roleID } = props; + const { popModal } = useModalContext(); + + const callDeleteCommunityRole = useServerCall(deleteCommunityRole); + const dispatchActionPromise = useDispatchActionPromise(); + + const deleteRoleLoadingStatus: LoadingStatus = useSelector( + deleteRoleLoadingStatusSelector, + ); + + const roleNamesToMemberCounts = useRoleMemberCountsForCommunity(threadInfo); + const roleName = threadInfo.roles[roleID].name; + const memberCount = roleNamesToMemberCounts[roleName]; + const defaultRoleName = threadInfo.roles[defaultRoleID].name; + + const message = constructRoleDeletionMessagePrompt( + defaultRoleName, + memberCount, + ); + + const onDeleteRole = React.useCallback(() => { + dispatchActionPromise( + deleteCommunityRoleActionTypes, + (async () => { + const response = await callDeleteCommunityRole({ + community: threadInfo.id, + roleID: roleID, + }); + popModal(); + return response; + })(), + ); + }, [ + callDeleteCommunityRole, + dispatchActionPromise, + roleID, + threadInfo.id, + popModal, + ]); + + const deleteButtonContent = React.useMemo(() => { + if (deleteRoleLoadingStatus === 'loading') { + return ( + + ); + } + return 'Yes, delete role'; + }, [deleteRoleLoadingStatus]); + + return ( + +
{message}
+
+ + +
+
+ ); +} + +export default DeleteRoleModal; diff --git a/web/roles/role-actions-menu.react.js b/web/roles/role-actions-menu.react.js index 67c1da5f5..5839d4d4a 100644 --- a/web/roles/role-actions-menu.react.js +++ b/web/roles/role-actions-menu.react.js @@ -1,108 +1,117 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { useModalContext } from 'lib/components/modal-provider.react.js'; import SWMansionIcon from 'lib/components/SWMansionIcon.react.js'; import { useRoleUserSurfacedPermissions } from 'lib/shared/thread-utils.js'; import type { ThreadInfo } from 'lib/types/thread-types.js'; import { useRoleDeletableAndEditableStatus } from 'lib/utils/role-utils.js'; import CreateRolesModal from './create-roles-modal.react.js'; +import DeleteRoleModal from './delete-role-modal.react.js'; import css from './role-actions-menu.css'; import MenuItem from '../components/menu-item.react.js'; import Menu from '../components/menu.react.js'; const menuIcon = ; type RoleActionsMenuProps = { +threadInfo: ThreadInfo, +roleName: string, }; function RoleActionsMenu(props: RoleActionsMenuProps): React.Node { const { threadInfo, roleName } = props; const { pushModal } = useModalContext(); const defaultRoleID = Object.keys(threadInfo.roles).find( roleID => threadInfo.roles[roleID].isDefault, ); invariant(defaultRoleID, 'default role should exist'); const existingRoleID = Object.keys(threadInfo.roles).find( roleID => threadInfo.roles[roleID].name === roleName, ); invariant(existingRoleID, 'existing role should exist'); const roleOptions = useRoleDeletableAndEditableStatus( roleName, defaultRoleID, existingRoleID, ); const roleNamesToUserSurfacedPermissions = useRoleUserSurfacedPermissions(threadInfo); const openEditRoleModal = React.useCallback( () => pushModal( , ), [ existingRoleID, pushModal, roleName, roleNamesToUserSurfacedPermissions, threadInfo, ], ); - const openDeleteRoleModal = React.useCallback(() => {}, []); + const openDeleteRoleModal = React.useCallback(() => { + pushModal( + , + ); + }, [existingRoleID, pushModal, threadInfo, defaultRoleID]); const menuItems = React.useMemo(() => { const availableOptions = []; const { isDeletable, isEditable } = roleOptions; if (isEditable) { availableOptions.push( , ); } if (isDeletable) { availableOptions.push( , ); } return availableOptions; }, [roleOptions, openDeleteRoleModal, openEditRoleModal]); return (
{menuItems}
); } export default RoleActionsMenu;