diff --git a/lib/utils/role-utils.js b/lib/utils/role-utils.js --- a/lib/utils/role-utils.js +++ b/lib/utils/role-utils.js @@ -2,6 +2,8 @@ import * as React from 'react'; +import { useSelector } from './redux-utils.js'; +import { threadInfoSelector } from '../selectors/thread-selectors.js'; import { type UserSurfacedPermissionOption, userSurfacedPermissions, @@ -9,6 +11,7 @@ } from '../types/thread-permission-types.js'; import type { ThreadType } from '../types/thread-types-enum.js'; import { threadTypes } from '../types/thread-types-enum.js'; +import type { ThreadInfo, RelativeMemberInfo } from '../types/thread-types.js'; function useFilterPermissionOptionsByThreadType( threadType: ThreadType, @@ -74,8 +77,58 @@ }, [roleName, defaultRoleID, existingRoleID]); } +function useRoleNameFromCommunityThreadInfo( + threadInfo: ThreadInfo, + memberInfo: RelativeMemberInfo, +): ?string { + // With the nature of how roles are set up, when introspecting into a + // non-community root thread, the roles in the threadInfo will just be + // "Members". This means that if we simply introspect into the threadInfo + // here, and try to pull the role, we'll potentially only get Members. + // The solution here is to pull the roles information from the top-most + // (i.e. community) threadInfo, and then use the roles from there. + // In the event that the threadInfo is a community thread, then we can just + // use the roles from the threadInfo directly. I'm choosing to fallback + // if (for some reason) the memberInfo from the 'topMostThreadInfo' is + // undefined to prevent the app from crashing, but this ideally shouldn't + // happen in the first place in non-GENESIS threads since a member part of a + // subchannel should be a member of the community. + const { community } = threadInfo; + const communityThreadInfo = useSelector(state => + community ? threadInfoSelector(state)[community] : null, + ); + const topMostThreadInfo = communityThreadInfo || threadInfo; + + // The special case is GENESIS threads, since per prior discussion + // (see context: https://linear.app/comm/issue/ENG-4077/), we don't really + // support roles for it. Also with GENESIS, the list of + // members are not populated in the community root. So in this case + // to prevent crashing, we should just return the role name from the + // current thread info. + if (topMostThreadInfo.type === threadTypes.GENESIS) { + return memberInfo.role && threadInfo.roles[memberInfo.role].name; + } + + const { roles } = topMostThreadInfo; + const memberInfoFromTopMostThreadInfo = topMostThreadInfo.members.find( + member => member.id === memberInfo.id, + ); + if (!memberInfoFromTopMostThreadInfo) { + console.warn( + `Could not find memberInfo for member ${memberInfo.id} in ` + + `thread ${topMostThreadInfo.id}. This is unexpected, ` + + `please investigate.`, + ); + } + + const { role } = memberInfoFromTopMostThreadInfo || memberInfo; + const roleName = role && roles[role].name; + return roleName; +} + export { useFilterPermissionOptionsByThreadType, constructRoleDeletionMessagePrompt, useRoleDeletableAndEditableStatus, + useRoleNameFromCommunityThreadInfo, }; diff --git a/native/chat/settings/thread-settings-member.react.js b/native/chat/settings/thread-settings-member.react.js --- a/native/chat/settings/thread-settings-member.react.js +++ b/native/chat/settings/thread-settings-member.react.js @@ -23,6 +23,7 @@ type ThreadInfo, type RelativeMemberInfo, } from 'lib/types/thread-types.js'; +import { useRoleNameFromCommunityThreadInfo } from 'lib/utils/role-utils.js'; import type { ThreadSettingsNavigate } from './thread-settings.react.js'; import UserAvatar from '../../avatars/user-avatar.react.js'; @@ -54,6 +55,7 @@ type Props = { ...BaseProps, // Redux state + +roleName: ?string, +removeUserLoadingStatus: LoadingStatus, +changeRoleLoadingStatus: LoadingStatus, +colors: Colors, @@ -114,14 +116,10 @@ ); } - const roleName = - this.props.memberInfo.role && - this.props.threadInfo.roles[this.props.memberInfo.role].name; - const roleInfo = ( - {roleName} + {this.props.roleName} ); @@ -274,10 +272,16 @@ const keyboardState = React.useContext(KeyboardContext); const overlayContext = React.useContext(OverlayContext); + const roleName = useRoleNameFromCommunityThreadInfo( + props.threadInfo, + props.memberInfo, + ); + return ( { @@ -110,8 +111,6 @@ [], ); - const roleName = role && roles[role].name; - const label = React.useMemo( () => , [roleName],