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 @@ -3,7 +3,7 @@ import { useNavigation } from '@react-navigation/native'; import invariant from 'invariant'; import * as React from 'react'; -import { Text } from 'react-native'; +import { Text, View } from 'react-native'; import { TouchableOpacity } from 'react-native-gesture-handler'; import { @@ -20,24 +20,27 @@ type Props = { +route: NavigationRoute<'ChangeRolesScreen'>, + +shouldRoleChangeBeDisabled: boolean, }; function ChangeRolesHeaderRightButton(props: Props): React.Node { const { threadInfo, memberInfo, role: selectedRole } = props.route.params; + const { shouldRoleChangeBeDisabled } = props; 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 { purpleLink } = useColors(); + const { disabledButton, purpleLink } = useColors(); const textStyle = React.useMemo( () => ({ - color: purpleLink, + color: shouldRoleChangeBeDisabled ? disabledButton : purpleLink, marginRight: 10, }), - [purpleLink], + [disabledButton, purpleLink, shouldRoleChangeBeDisabled], ); const handleSave = React.useCallback(() => { @@ -62,11 +65,23 @@ threadInfo.id, ]); - return ( - - Save - - ); + const saveButton = React.useMemo(() => { + if (shouldRoleChangeBeDisabled) { + return ( + + Save + + ); + } + + return ( + + Save + + ); + }, [shouldRoleChangeBeDisabled, textStyle, handleSave]); + + return saveButton; } export default ChangeRolesHeaderRightButton; 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 @@ -9,6 +9,8 @@ import { changeThreadMemberRolesActionTypes } from 'lib/actions/thread-actions.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; +import { otherUsersButNoOtherAdmins } from 'lib/selectors/thread-selectors.js'; +import { roleIsAdminRole } from 'lib/shared/thread-utils.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; import type { RelativeMemberInfo, ThreadInfo } from 'lib/types/thread-types.js'; import { values } from 'lib/utils/objects.js'; @@ -120,6 +122,62 @@ showActionSheetWithOptions, ]); + const otherUsersButNoOtherAdminsValue = useSelector( + otherUsersButNoOtherAdmins(threadInfo.id), + ); + + const memberIsAdmin = React.useMemo(() => { + invariant(memberInfo.role, 'Expected member role to be defined'); + return roleIsAdminRole(threadInfo.roles[memberInfo.role]); + }, [threadInfo.roles, memberInfo.role]); + + const shouldRoleChangeBeDisabled = React.useMemo( + () => otherUsersButNoOtherAdminsValue && memberIsAdmin, + [otherUsersButNoOtherAdminsValue, memberIsAdmin], + ); + + const roleSelector = React.useMemo(() => { + if (shouldRoleChangeBeDisabled) { + return ( + + {selectedRoleName} + + + ); + } + + return ( + + {selectedRoleName} + + + ); + }, [showActionSheet, styles, selectedRoleName, shouldRoleChangeBeDisabled]); + + const disabledRoleChangeMessage = React.useMemo(() => { + if (!shouldRoleChangeBeDisabled) { + return null; + } + + return ( + + + + There must be at least one admin at any given time in a community. + + + ); + }, [ + shouldRoleChangeBeDisabled, + styles.disabledWarningBackground, + styles.infoIcon, + styles.disabledWarningText, + ]); + React.useEffect(() => { navigation.setOptions({ // eslint-disable-next-line react/display-name @@ -133,10 +191,21 @@ /> ); } - return ; + return ( + + ); }, }); - }, [changeRolesLoadingStatus, navigation, activityIndicatorStyle, route]); + }, [ + changeRolesLoadingStatus, + navigation, + activityIndicatorStyle, + route, + shouldRoleChangeBeDisabled, + ]); return ( @@ -150,13 +219,8 @@ {memberInfo.username} - - ROLE - - {selectedRoleName} - - - + {roleSelector} + {disabledRoleChangeMessage} ); } @@ -203,9 +267,38 @@ color: 'panelForegroundSecondaryLabel', fontSize: 16, }, + disabledCurrentRole: { + color: 'disabledButton', + fontSize: 16, + }, pencilIcon: { color: 'panelInputSecondaryForeground', }, + disabledPencilIcon: { + color: 'disabledButton', + }, + disabledWarningBackground: { + backgroundColor: 'disabledButton', + padding: 16, + display: 'flex', + marginTop: 20, + flexDirection: 'row', + justifyContent: 'center', + width: '75%', + alignSelf: 'center', + }, + disabledWarningText: { + color: 'panelForegroundSecondaryLabel', + fontSize: 14, + marginRight: 8, + display: 'flex', + }, + infoIcon: { + color: 'panelForegroundSecondaryLabel', + marginRight: 8, + marginLeft: 8, + marginBottom: 12, + }, }; export default ChangeRolesScreen;