diff --git a/web/modals/threads/members/change-member-role-modal.css b/web/modals/threads/members/change-member-role-modal.css --- a/web/modals/threads/members/change-member-role-modal.css +++ b/web/modals/threads/members/change-member-role-modal.css @@ -48,3 +48,9 @@ .roleModalBackButton { padding: 14px 32px; } + +.roleModalErrorMessage { + color: var(--error); + display: flex; + justify-content: center; +} diff --git a/web/modals/threads/members/change-member-role-modal.react.js b/web/modals/threads/members/change-member-role-modal.react.js --- a/web/modals/threads/members/change-member-role-modal.react.js +++ b/web/modals/threads/members/change-member-role-modal.react.js @@ -8,6 +8,8 @@ changeThreadMemberRolesActionTypes, } from 'lib/actions/thread-actions.js'; import { useModalContext } from 'lib/components/modal-provider.react.js'; +import { otherUsersButNoOtherAdmins } from 'lib/selectors/thread-selectors.js'; +import { roleIsAdminRole } from 'lib/shared/thread-utils.js'; import type { RelativeMemberInfo, ThreadInfo } from 'lib/types/thread-types'; import { useDispatchActionPromise, @@ -19,6 +21,7 @@ import Button, { buttonThemes } from '../../../components/button.react.js'; import Dropdown from '../../../components/dropdown.react.js'; import UserAvatar from '../../../components/user-avatar.react.js'; +import { useSelector } from '../../../redux/redux-utils.js'; import Modal from '../../modal.react.js'; import UnsavedChangesModal from '../../unsaved-changes-modal.react.js'; @@ -32,6 +35,10 @@ const { pushModal, popModal } = useModalContext(); const dispatchActionPromise = useDispatchActionPromise(); const callChangeThreadMemberRoles = useServerCall(changeThreadMemberRoles); + const [showErrorMessage, setShowErrorMessage] = React.useState(false); + const otherUsersButNoOtherAdminsValue = useSelector( + otherUsersButNoOtherAdmins(threadInfo.id), + ); const modalName = 'Change Role'; const roleOptions = React.useMemo( @@ -62,12 +69,39 @@ pushModal(); }, [initialSelectedRole, popModal, pushModal, selectedRole]); + const errorMessage = React.useMemo(() => { + if (!showErrorMessage) { + return null; + } + + return ( + <> +
Cannot change role.
+
+ There must be at least one admin at any given time. +
+ + ); + }, [showErrorMessage]); + const onSave = React.useCallback(() => { if (selectedRole === initialSelectedRole) { popModal(); return; } + const memberIsAdmin = memberInfo.role + ? roleIsAdminRole(threadInfo.roles[memberInfo.role]) + : true; + + // Handle case where the sole admin's role is being changed. + if (otherUsersButNoOtherAdminsValue && memberIsAdmin) { + setShowErrorMessage(true); + return; + } + + setShowErrorMessage(false); + const createChangeThreadMemberRolesPromise = () => { invariant(selectedRole, 'Expected selected role to be set'); return callChangeThreadMemberRoles( @@ -84,12 +118,13 @@ popModal(); }, [ callChangeThreadMemberRoles, + otherUsersButNoOtherAdminsValue, dispatchActionPromise, initialSelectedRole, - memberInfo.id, + memberInfo, popModal, selectedRole, - threadInfo.id, + threadInfo, ]); return ( @@ -111,6 +146,7 @@ setActiveSelection={setSelectedRole} /> + {errorMessage}