diff --git a/native/roles/create-roles-header-right-button.react.js b/native/roles/create-roles-header-right-button.react.js
index e7e3f5c0b..ba54f39f4 100644
--- a/native/roles/create-roles-header-right-button.react.js
+++ b/native/roles/create-roles-header-right-button.react.js
@@ -1,88 +1,103 @@
// @flow
import { useNavigation } from '@react-navigation/native';
import * as React from 'react';
import { TouchableOpacity, Text } from 'react-native';
import {
modifyCommunityRole,
modifyCommunityRoleActionTypes,
} from 'lib/actions/thread-actions.js';
import {
useServerCall,
useDispatchActionPromise,
} from 'lib/utils/action-utils.js';
+import { values } from 'lib/utils/objects.js';
import type { NavigationRoute } from '../navigation/route-names';
import { useStyles } from '../themes/colors.js';
type Props = {
+route: NavigationRoute<'CreateRolesScreen'>,
+ +setRoleCreationFailed: boolean => mixed,
};
function CreateRolesHeaderRightButton(props: Props): React.Node {
const { threadInfo, action, roleName, rolePermissions } = props.route.params;
+ const { setRoleCreationFailed } = props;
const navigation = useNavigation();
const styles = useStyles(unboundStyles);
const callModifyCommunityRole = useServerCall(modifyCommunityRole);
const dispatchActionPromise = useDispatchActionPromise();
+ const threadRoleNames = React.useMemo(
+ () => values(threadInfo.roles).map(role => role.name),
+ [threadInfo.roles],
+ );
+
const onPressCreate = React.useCallback(() => {
+ if (threadRoleNames.includes(roleName)) {
+ setRoleCreationFailed(true);
+ return;
+ }
+
dispatchActionPromise(
modifyCommunityRoleActionTypes,
callModifyCommunityRole({
community: threadInfo.id,
action,
name: roleName,
permissions: [...rolePermissions],
}),
);
navigation.goBack();
}, [
callModifyCommunityRole,
dispatchActionPromise,
threadInfo,
action,
roleName,
rolePermissions,
navigation,
+ setRoleCreationFailed,
+ threadRoleNames,
]);
const shouldHeaderRightBeDisabled = roleName.length === 0;
const createButton = React.useMemo(() => {
const textStyle = shouldHeaderRightBeDisabled
? styles.createButtonDisabled
: styles.createButton;
return (
Create
);
}, [
shouldHeaderRightBeDisabled,
styles.createButtonDisabled,
styles.createButton,
onPressCreate,
]);
return createButton;
}
const unboundStyles = {
createButtonDisabled: {
color: 'disabledButton',
marginRight: 10,
},
createButton: {
color: 'purpleLink',
marginRight: 10,
},
};
export default CreateRolesHeaderRightButton;
diff --git a/native/roles/create-roles-screen.react.js b/native/roles/create-roles-screen.react.js
index b37eae567..d4ed1f438 100644
--- a/native/roles/create-roles-screen.react.js
+++ b/native/roles/create-roles-screen.react.js
@@ -1,254 +1,288 @@
// @flow
import * as React from 'react';
import { View, Text, TouchableOpacity, ActivityIndicator } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import { modifyCommunityRoleActionTypes } from 'lib/actions/thread-actions.js';
import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js';
import type { LoadingStatus } from 'lib/types/loading-types.js';
import {
type UserSurfacedPermissionOption,
type UserSurfacedPermission,
} from 'lib/types/thread-permission-types.js';
import type { ThreadInfo } from 'lib/types/thread-types.js';
import { useFilterPermissionOptionsByThreadType } from 'lib/utils/role-utils.js';
import CreateRolesHeaderRightButton from './create-roles-header-right-button.react.js';
import type { RolesNavigationProp } from './roles-navigator.react.js';
import EnumSettingsOption from '../components/enum-settings-option.react.js';
import SWMansionIcon from '../components/swmansion-icon.react.js';
import TextInput from '../components/text-input.react.js';
import type { NavigationRoute } from '../navigation/route-names.js';
import { useSelector } from '../redux/redux-utils.js';
import { useStyles } from '../themes/colors.js';
export type CreateRolesScreenParams = {
+threadInfo: ThreadInfo,
+action: 'create_role' | 'edit_role',
+roleName: string,
+rolePermissions: $ReadOnlySet,
};
type CreateRolesScreenProps = {
+navigation: RolesNavigationProp<'CreateRolesScreen'>,
+route: NavigationRoute<'CreateRolesScreen'>,
};
const createRolesLoadingStatusSelector = createLoadingStatusSelector(
modifyCommunityRoleActionTypes,
);
function CreateRolesScreen(props: CreateRolesScreenProps): React.Node {
const {
threadInfo,
action,
roleName: defaultRoleName,
rolePermissions: defaultRolePermissions,
} = props.route.params;
const createRolesLoadingStatus: LoadingStatus = useSelector(
createRolesLoadingStatusSelector,
);
const [customRoleName, setCustomRoleName] =
React.useState(defaultRoleName);
const [selectedPermissions, setSelectedPermissions] = React.useState<
$ReadOnlySet,
>(defaultRolePermissions);
+ const [roleCreationFailed, setRoleCreationFailed] =
+ React.useState(false);
+
const styles = useStyles(unboundStyles);
+ const errorStyles = React.useMemo(
+ () =>
+ roleCreationFailed
+ ? [styles.errorContainer, styles.errorContainerVisible]
+ : styles.errorContainer,
+ [roleCreationFailed, styles.errorContainer, styles.errorContainerVisible],
+ );
+
const onClearPermissions = React.useCallback(() => {
setSelectedPermissions(new Set());
}, []);
const isSelectedPermissionsEmpty = selectedPermissions.size === 0;
const clearPermissionsText = React.useMemo(() => {
const textStyle = isSelectedPermissionsEmpty
? styles.clearPermissionsTextDisabled
: styles.clearPermissionsText;
return (
Clear permissions
);
}, [
isSelectedPermissionsEmpty,
onClearPermissions,
styles.clearPermissionsText,
styles.clearPermissionsTextDisabled,
]);
const isUserSurfacedPermissionSelected = React.useCallback(
(option: UserSurfacedPermissionOption) =>
selectedPermissions.has(option.userSurfacedPermission),
[selectedPermissions],
);
const onEnumValuePress = React.useCallback(
(option: UserSurfacedPermissionOption) =>
setSelectedPermissions(currentPermissions => {
if (currentPermissions.has(option.userSurfacedPermission)) {
const newPermissions = new Set(currentPermissions);
newPermissions.delete(option.userSurfacedPermission);
return newPermissions;
} else {
return new Set([
...currentPermissions,
option.userSurfacedPermission,
]);
}
}),
[],
);
React.useEffect(
() =>
props.navigation.setParams({
threadInfo,
action,
roleName: customRoleName,
rolePermissions: selectedPermissions,
}),
[props.navigation, threadInfo, action, customRoleName, selectedPermissions],
);
const filteredUserSurfacedPermissionOptions =
useFilterPermissionOptionsByThreadType(threadInfo.type);
const permissionsList = React.useMemo(
() =>
[...filteredUserSurfacedPermissionOptions].map(permission => (
onEnumValuePress(permission)}
/>
)),
[
isUserSurfacedPermissionSelected,
filteredUserSurfacedPermissionOptions,
onEnumValuePress,
],
);
const onChangeRoleNameInput = React.useCallback((roleName: string) => {
+ setRoleCreationFailed(false);
setCustomRoleName(roleName);
}, []);
React.useEffect(
() =>
props.navigation.setOptions({
// eslint-disable-next-line react/display-name
headerRight: () => {
if (createRolesLoadingStatus === 'loading') {
return (
);
}
- return ;
+ return (
+
+ );
},
}),
[
createRolesLoadingStatus,
props.navigation,
styles.activityIndicator,
props.route,
],
);
return (
ROLE NAME
+
+
+ There is already a role with this name in the community
+
+
PERMISSIONS
{clearPermissionsText}
{permissionsList}
);
}
const unboundStyles = {
roleNameContainer: {
marginTop: 30,
},
roleNameText: {
color: 'panelBackgroundLabel',
fontSize: 12,
marginBottom: 5,
marginLeft: 10,
},
roleInput: {
backgroundColor: 'panelForeground',
padding: 12,
flexDirection: 'row',
justifyContent: 'space-between',
},
roleInputComponent: {
color: 'panelForegroundLabel',
fontSize: 16,
},
pencilIcon: {
color: 'panelInputSecondaryForeground',
},
+ errorContainer: {
+ marginTop: 10,
+ alignItems: 'center',
+ opacity: 0,
+ },
+ errorContainerVisible: {
+ opacity: 1,
+ },
+ errorText: {
+ color: 'redText',
+ fontSize: 14,
+ },
permissionsContainer: {
marginTop: 20,
paddingBottom: 220,
},
permissionsHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
},
permissionsText: {
color: 'panelBackgroundLabel',
fontSize: 12,
marginLeft: 10,
},
clearPermissionsText: {
color: 'purpleLink',
fontSize: 12,
marginRight: 15,
},
clearPermissionsTextDisabled: {
color: 'disabledButton',
fontSize: 12,
marginRight: 15,
},
permissionsListContainer: {
backgroundColor: 'panelForeground',
marginTop: 10,
},
activityIndicator: {
paddingRight: 15,
},
};
export default CreateRolesScreen;