diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js
--- a/lib/shared/thread-utils.js
+++ b/lib/shared/thread-utils.js
@@ -1533,6 +1533,41 @@
return threadInfo.community === communityID || threadInfo.id === communityID;
}
+type RoleAndMemberCount = {
+ [roleName: string]: number,
+};
+
+function useRoleMemberCountsForCommunity(
+ threadInfo: ThreadInfo,
+): RoleAndMemberCount {
+ return React.useMemo(() => {
+ const roleIDsToNames = {};
+
+ Object.keys(threadInfo.roles).forEach(roleID => {
+ roleIDsToNames[roleID] = threadInfo.roles[roleID].name;
+ });
+
+ const roleNamesToMemberCount: RoleAndMemberCount = {};
+
+ threadInfo.members.forEach(({ role: roleID }) => {
+ invariant(roleID, 'Community member should have a role');
+ const roleName = roleIDsToNames[roleID];
+
+ roleNamesToMemberCount[roleName] =
+ (roleNamesToMemberCount[roleName] ?? 0) + 1;
+ });
+
+ // For all community roles with no members, add them to the list with 0
+ Object.keys(roleIDsToNames).forEach(roleName => {
+ if (roleNamesToMemberCount[roleIDsToNames[roleName]] === undefined) {
+ roleNamesToMemberCount[roleIDsToNames[roleName]] = 0;
+ }
+ });
+
+ return roleNamesToMemberCount;
+ }, [threadInfo]);
+}
+
export {
threadHasPermission,
viewerIsMember,
@@ -1598,4 +1633,5 @@
chatNameMaxLength,
patchThreadInfoToIncludeMentionedMembersOfParent,
threadInfoInsideCommunity,
+ useRoleMemberCountsForCommunity,
};
diff --git a/native/roles/community-roles-screen.react.js b/native/roles/community-roles-screen.react.js
--- a/native/roles/community-roles-screen.react.js
+++ b/native/roles/community-roles-screen.react.js
@@ -1,11 +1,17 @@
// @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 { useStyles } from '../themes/colors.js';
export type CommunityRolesScreenParams = {
+threadInfo: ThreadInfo,
@@ -16,9 +22,117 @@
+route: NavigationRoute<'CommunityRolesScreen'>,
};
-// eslint-disable-next-line no-unused-vars
function CommunityRolesScreen(props: CommunityRolesScreenProps): React.Node {
- return null;
+ 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(() => {}, []);
+
+ 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/role-panel-entry.react.js b/native/roles/role-panel-entry.react.js
new file mode 100644
--- /dev/null
+++ b/native/roles/role-panel-entry.react.js
@@ -0,0 +1,50 @@
+// @flow
+
+import * as React from 'react';
+import { View, Text } from 'react-native';
+
+import CommIcon from '../components/comm-icon.react.js';
+import { useStyles } from '../themes/colors.js';
+
+type RolePanelEntryProps = {
+ +roleName: string,
+ +memberCount: number,
+};
+
+function RolePanelEntry(props: RolePanelEntryProps): React.Node {
+ const { roleName, memberCount } = props;
+ const styles = useStyles(unboundStyles);
+
+ return (
+
+ {roleName}
+
+ {memberCount}
+
+
+
+ );
+}
+
+const unboundStyles = {
+ rolePanelEntry: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: 8,
+ },
+ rolePanelNameEntry: {
+ color: 'panelForegroundLabel',
+ fontWeight: '600',
+ fontSize: 14,
+ },
+ rolePanelCountEntry: {
+ color: 'panelForegroundLabel',
+ fontWeight: '600',
+ fontSize: 14,
+ marginRight: 72,
+ padding: 8,
+ },
+};
+
+export default RolePanelEntry;