Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32199164
D8359.1765106256.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D8359.1765106256.diff
View Options
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(
+ <RolePanelEntry
+ key={roleName}
+ roleName={roleName}
+ memberCount={roleNamesToMembers[roleName]}
+ />,
+ );
+ });
+
+ return rolePanelEntries;
+ }, [roleNamesToMembers]);
+
+ const navigateToCreateRole = React.useCallback(() => {}, []);
+
+ return (
+ <View>
+ <View style={styles.rolesInfoContainer}>
+ <Text style={styles.rolesInfoTextFirstLine}>
+ 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.
+ </Text>
+ <Text style={styles.rolesInfoTextSecondLine}>
+ When people join the community, they are automatically assigned the
+ Members role.
+ </Text>
+ </View>
+ <View style={styles.rolesPanel}>
+ <View style={styles.rolePanelHeadersContainer}>
+ <Text style={styles.rolePanelHeaderLeft}>ROLES</Text>
+ <Text style={styles.rolePanelHeaderRight}>MEMBERS</Text>
+ </View>
+ <ScrollView style={styles.rolePanelList}>{rolePanelList}</ScrollView>
+ <View style={styles.buttonContainer}>
+ <Button
+ style={styles.createRoleButton}
+ onPress={navigateToCreateRole}
+ >
+ <Text style={styles.createRoleButtonText}>Create role</Text>
+ </Button>
+ </View>
+ </View>
+ </View>
+ );
}
+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 (
+ <View style={styles.rolePanelEntry}>
+ <Text style={styles.rolePanelNameEntry}>{roleName}</Text>
+ <Text style={styles.rolePanelCountEntry}>
+ {memberCount}
+ <CommIcon name="user-filled" size={14} />
+ </Text>
+ </View>
+ );
+}
+
+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;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Dec 7, 11:17 AM (16 h, 9 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5841862
Default Alt Text
D8359.1765106256.diff (6 KB)
Attached To
Mode
D8359: [native] Create the community roles screen
Attached
Detach File
Event Timeline
Log In to Comment