diff --git a/native/roles/community-roles-screen.react.js b/native/roles/community-roles-screen.react.js index 2d97557c8..bd64cf5bb 100644 --- a/native/roles/community-roles-screen.react.js +++ b/native/roles/community-roles-screen.react.js @@ -1,146 +1,148 @@ // @flow import * as React from 'react'; import { View, Text } from 'react-native'; import { ScrollView } from 'react-native-gesture-handler'; import { useRoleMemberCountsForCommunity } from 'lib/shared/thread-utils.js'; import type { ThreadInfo } from 'lib/types/thread-types.js'; import RolePanelEntry from './role-panel-entry.react.js'; import type { RolesNavigationProp } from './roles-navigator.react.js'; import Button from '../components/button.react.js'; import type { NavigationRoute } from '../navigation/route-names.js'; import { CreateRolesScreenRouteName } from '../navigation/route-names.js'; import { useStyles } from '../themes/colors.js'; export type CommunityRolesScreenParams = { +threadInfo: ThreadInfo, }; type CommunityRolesScreenProps = { +navigation: RolesNavigationProp<'CommunityRolesScreen'>, +route: NavigationRoute<'CommunityRolesScreen'>, }; function CommunityRolesScreen(props: CommunityRolesScreenProps): React.Node { const { threadInfo } = props.route.params; const styles = useStyles(unboundStyles); const roleNamesToMembers = useRoleMemberCountsForCommunity(threadInfo); const rolePanelList = React.useMemo(() => { const rolePanelEntries = []; Object.keys(roleNamesToMembers).forEach(roleName => { rolePanelEntries.push( , ); }); return rolePanelEntries; }, [roleNamesToMembers]); const navigateToCreateRole = React.useCallback( () => props.navigation.navigate(CreateRolesScreenRouteName, { threadInfo, action: 'create_role', + roleName: 'New role', + rolePermissions: new Set(), }), [threadInfo, props.navigation], ); return ( Roles help you group community members together and assign them certain permissions. The Admins and Members roles are set by default and cannot be edited or deleted. When people join the community, they are automatically assigned the Members role. ROLES MEMBERS {rolePanelList} ); } const unboundStyles = { rolesInfoContainer: { backgroundColor: 'panelForeground', padding: 16, }, rolesInfoTextFirstLine: { color: 'panelBackgroundLabel', fontSize: 14, marginBottom: 14, }, rolesInfoTextSecondLine: { color: 'panelBackgroundLabel', fontSize: 14, }, rolesPanel: { marginTop: 30, }, rolePanelHeadersContainer: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 8, }, rolePanelHeaderLeft: { color: 'panelBackgroundLabel', fontSize: 14, }, rolePanelHeaderRight: { color: 'panelBackgroundLabel', fontSize: 14, marginRight: 72, }, rolePanelList: { backgroundColor: 'panelForeground', marginTop: 8, padding: 4, maxHeight: 325, }, buttonContainer: { backgroundColor: 'panelForeground', padding: 2, }, createRoleButton: { justifyContent: 'center', alignItems: 'center', margin: 10, backgroundColor: 'purpleButton', height: 48, borderRadius: 10, }, createRoleButtonText: { color: 'whiteText', fontSize: 16, fontWeight: '500', }, }; export default CommunityRolesScreen; diff --git a/native/roles/create-roles-header-right-button.react.js b/native/roles/create-roles-header-right-button.react.js new file mode 100644 index 000000000..b47598286 --- /dev/null +++ b/native/roles/create-roles-header-right-button.react.js @@ -0,0 +1,56 @@ +// @flow + +import { useNavigation } from '@react-navigation/native'; +import * as React from 'react'; +import { TouchableOpacity, Text } from 'react-native'; + +import { modifyCommunityRole } from 'lib/actions/thread-actions.js'; +import { useServerCall } from 'lib/utils/action-utils.js'; + +import type { NavigationRoute } from '../navigation/route-names'; +import { useStyles } from '../themes/colors.js'; + +type Props = { + +route: NavigationRoute<'CreateRolesScreen'>, +}; + +function CreateRolesHeaderRightButton(props: Props): React.Node { + const { threadInfo, action, roleName, rolePermissions } = props.route.params; + const navigation = useNavigation(); + const styles = useStyles(unboundStyles); + + const callModifyCommunityRole = useServerCall(modifyCommunityRole); + + const onPressCreate = React.useCallback(() => { + callModifyCommunityRole({ + community: threadInfo.id, + action, + name: roleName, + permissions: [...rolePermissions], + }); + + navigation.goBack(); + }, [ + callModifyCommunityRole, + threadInfo, + action, + roleName, + rolePermissions, + navigation, + ]); + + return ( + + Create + + ); +} + +const unboundStyles = { + 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 ebc772a24..a70c9ed54 100644 --- a/native/roles/create-roles-screen.react.js +++ b/native/roles/create-roles-screen.react.js @@ -1,195 +1,222 @@ // @flow import * as React from 'react'; import { View, Text, TouchableOpacity } from 'react-native'; import { ScrollView } from 'react-native-gesture-handler'; 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 { 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'>, }; function CreateRolesScreen(props: CreateRolesScreenProps): React.Node { - // eslint-disable-next-line no-unused-vars - const { threadInfo, action } = props.route.params; + const { + threadInfo, + action, + roleName: defaultRoleName, + rolePermissions: defaultRolePermissions, + } = props.route.params; const [customRoleName, setCustomRoleName] = - React.useState('New role'); + React.useState(defaultRoleName); const [selectedPermissions, setSelectedPermissions] = React.useState< $ReadOnlySet, - >(new Set()); + >(defaultRolePermissions); const styles = useStyles(unboundStyles); 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) => { setCustomRoleName(roleName); }, []); + React.useEffect( + () => + props.navigation.setOptions({ + // eslint-disable-next-line react/display-name + headerRight: () => , + }), + [props.navigation, props.route], + ); + return ( ROLE NAME 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', }, 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, }, }; export default CreateRolesScreen;