Page MenuHomePhabricator

D8625.id29085.diff
No OneTemporary

D8625.id29085.diff

diff --git a/web/roles/delete-role-modal.css b/web/roles/delete-role-modal.css
new file mode 100644
--- /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
--- /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 (
+ <LoadingIndicator status={deleteRoleLoadingStatus} size="medium" />
+ );
+ }
+ return 'Yes, delete role';
+ }, [deleteRoleLoadingStatus]);
+
+ return (
+ <Modal name="Delete role" onClose={popModal} size="large">
+ <div className={css.roleDeletionText}>{message}</div>
+ <div className={css.buttonsContainer}>
+ <Button
+ variant="outline"
+ className={css.cancelButton}
+ buttonColor={buttonThemes.outline}
+ onClick={popModal}
+ >
+ Cancel
+ </Button>
+ <Button
+ variant="filled"
+ className={css.deleteRoleButton}
+ buttonColor={buttonThemes.danger}
+ onClick={onDeleteRole}
+ >
+ {deleteButtonContent}
+ </Button>
+ </div>
+ </Modal>
+ );
+}
+
+export default DeleteRoleModal;
diff --git a/web/roles/role-actions-menu.react.js b/web/roles/role-actions-menu.react.js
--- a/web/roles/role-actions-menu.react.js
+++ b/web/roles/role-actions-menu.react.js
@@ -1,5 +1,6 @@
// @flow
+import invariant from 'invariant';
import * as React from 'react';
import { useModalContext } from 'lib/components/modal-provider.react.js';
@@ -8,6 +9,7 @@
import type { ThreadInfo } from 'lib/types/thread-types.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';
@@ -26,9 +28,11 @@
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 isDeletableRole =
roleName !== 'Admins' && defaultRoleID !== existingRoleID;
@@ -56,7 +60,15 @@
threadInfo,
],
);
- const openDeleteRoleModal = React.useCallback(() => {}, []);
+ const openDeleteRoleModal = React.useCallback(() => {
+ pushModal(
+ <DeleteRoleModal
+ threadInfo={threadInfo}
+ defaultRoleID={defaultRoleID}
+ roleID={existingRoleID}
+ />,
+ );
+ }, [existingRoleID, pushModal, threadInfo, defaultRoleID]);
const items = React.useMemo(() => {
const availableOptions = [];

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 30, 5:03 PM (21 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2601707
Default Alt Text
D8625.id29085.diff (5 KB)

Event Timeline