Page MenuHomePhabricator

D8161.id27768.diff
No OneTemporary

D8161.id27768.diff

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 (
- <TouchableOpacity onPress={handleSave}>
- <Text style={textStyle}>Save</Text>
- </TouchableOpacity>
- );
+ const saveButton = React.useMemo(() => {
+ if (shouldRoleChangeBeDisabled) {
+ return (
+ <View>
+ <Text style={textStyle}>Save</Text>
+ </View>
+ );
+ }
+
+ return (
+ <TouchableOpacity onPress={handleSave}>
+ <Text style={textStyle}>Save</Text>
+ </TouchableOpacity>
+ );
+ }, [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 (
+ <View style={styles.roleSelector}>
+ <Text style={styles.disabledCurrentRole}>{selectedRoleName}</Text>
+ <SWMansionIcon
+ name="edit-1"
+ size={20}
+ style={styles.disabledPencilIcon}
+ />
+ </View>
+ );
+ }
+
+ return (
+ <TouchableOpacity onPress={showActionSheet} style={styles.roleSelector}>
+ <Text style={styles.currentRole}>{selectedRoleName}</Text>
+ <SWMansionIcon name="edit-1" size={20} style={styles.pencilIcon} />
+ </TouchableOpacity>
+ );
+ }, [showActionSheet, styles, selectedRoleName, shouldRoleChangeBeDisabled]);
+
+ const disabledRoleChangeMessage = React.useMemo(() => {
+ if (!shouldRoleChangeBeDisabled) {
+ return null;
+ }
+
+ return (
+ <View style={styles.disabledWarningBackground}>
+ <SWMansionIcon name="info-circle" size={24} style={styles.infoIcon} />
+ <Text style={styles.disabledWarningText}>
+ There must be at least one admin at any given time in a community.
+ </Text>
+ </View>
+ );
+ }, [
+ shouldRoleChangeBeDisabled,
+ styles.disabledWarningBackground,
+ styles.infoIcon,
+ styles.disabledWarningText,
+ ]);
+
React.useEffect(() => {
navigation.setOptions({
// eslint-disable-next-line react/display-name
@@ -133,10 +191,21 @@
/>
);
}
- return <ChangeRolesHeaderRightButton route={route} />;
+ return (
+ <ChangeRolesHeaderRightButton
+ route={route}
+ shouldRoleChangeBeDisabled={shouldRoleChangeBeDisabled}
+ />
+ );
},
});
- }, [changeRolesLoadingStatus, navigation, activityIndicatorStyle, route]);
+ }, [
+ changeRolesLoadingStatus,
+ navigation,
+ activityIndicatorStyle,
+ route,
+ shouldRoleChangeBeDisabled,
+ ]);
return (
<View>
@@ -150,13 +219,8 @@
<UserAvatar userID={memberInfo.id} size="profile" />
<Text style={styles.memberInfoUsername}>{memberInfo.username}</Text>
</View>
- <View>
- <Text style={styles.roleSelectorLabel}>ROLE</Text>
- <TouchableOpacity onPress={showActionSheet} style={styles.roleSelector}>
- <Text style={styles.currentRole}>{selectedRoleName}</Text>
- <SWMansionIcon name="edit-1" size={20} style={styles.pencilIcon} />
- </TouchableOpacity>
- </View>
+ {roleSelector}
+ {disabledRoleChangeMessage}
</View>
);
}
@@ -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;

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 15, 3:26 PM (21 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2494654
Default Alt Text
D8161.id27768.diff (6 KB)

Event Timeline