diff --git a/native/roles/change-roles-header-right-button.react.js b/native/roles/change-roles-header-right-button.react.js --- a/native/roles/change-roles-header-right-button.react.js +++ b/native/roles/change-roles-header-right-button.react.js @@ -10,12 +10,15 @@ changeThreadMemberRoles, changeThreadMemberRolesActionTypes, } from 'lib/actions/thread-actions.js'; +import { otherUsersButNoOtherAdmins } from 'lib/selectors/thread-selectors.js'; +import { roleIsAdminRole } from 'lib/shared/thread-utils.js'; import { useDispatchActionPromise, useServerCall, } from 'lib/utils/action-utils.js'; import type { NavigationRoute } from '../navigation/route-names'; +import { useSelector } from '../redux/redux-utils.js'; import { useColors } from '../themes/colors.js'; type Props = { @@ -25,12 +28,17 @@ function ChangeRolesHeaderRightButton(props: Props): React.Node { const { threadInfo, memberInfo, role: selectedRole } = props.route.params; const { role: initialRole } = memberInfo; + invariant(initialRole, 'Expected initial role to be defined'); invariant(selectedRole, 'Expected selected role to be defined'); const navigation = useNavigation(); const callChangeThreadMemberRoles = useServerCall(changeThreadMemberRoles); const dispatchActionPromise = useDispatchActionPromise(); + const otherUsersButNoOtherAdminsValue = useSelector( + otherUsersButNoOtherAdmins(threadInfo.id), + ); + const { purpleLink } = useColors(); const textStyle = React.useMemo( () => ({ @@ -46,6 +54,16 @@ return; } + const memberIsAdmin = roleIsAdminRole(threadInfo.roles[initialRole]); + + if (otherUsersButNoOtherAdminsValue && memberIsAdmin) { + navigation.setParams({ + ...props.route.params, + shouldShowError: true, + }); + return; + } + dispatchActionPromise( changeThreadMemberRolesActionTypes, callChangeThreadMemberRoles(threadInfo.id, [memberInfo.id], selectedRole), @@ -58,8 +76,11 @@ initialRole, memberInfo.id, navigation, + otherUsersButNoOtherAdminsValue, + props.route.params, selectedRole, threadInfo.id, + threadInfo.roles, ]); return ( diff --git a/native/roles/change-roles-screen.react.js b/native/roles/change-roles-screen.react.js --- a/native/roles/change-roles-screen.react.js +++ b/native/roles/change-roles-screen.react.js @@ -24,6 +24,7 @@ +threadInfo: ThreadInfo, +memberInfo: RelativeMemberInfo, +role: ?string, + +shouldShowError?: boolean, }; type Props = { @@ -37,7 +38,7 @@ function ChangeRolesScreen(props: Props): React.Node { const { navigation, route } = props; - const { threadInfo, memberInfo, role } = props.route.params; + const { threadInfo, memberInfo, role, shouldShowError } = props.route.params; invariant(role, 'Role must be defined'); const changeRolesLoadingStatus: LoadingStatus = useSelector( @@ -99,6 +100,7 @@ threadInfo, memberInfo, role: newRole, + shouldShowError: false, }); }, [navigation, setSelectedRole, roleOptions, memberInfo, threadInfo], @@ -122,7 +124,22 @@ }, onRoleChange, ); - }, [roleOptions, onRoleChange, activeTheme, showActionSheetWithOptions]); + }, [showActionSheetWithOptions, roleOptions, onRoleChange, activeTheme]); + + const errorMessage = React.useMemo(() => { + if (!shouldShowError) { + return null; + } + + return ( + + Cannot change role. + + There must be at least one admin at any given time. + + + ); + }, [shouldShowError, styles.errorMessage, styles.errorMessageContainer]); return ( @@ -143,6 +160,7 @@ + {errorMessage} ); } @@ -192,6 +210,16 @@ pencilIcon: { color: 'panelInputSecondaryForeground', }, + errorMessageContainer: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + marginTop: 20, + }, + errorMessage: { + color: 'redText', + fontSize: 16, + }, }; export default ChangeRolesScreen;