Show Members in Conversation
Click the membership count at the top right of a conversation, and you’ll see the conversation’s member list. Online members are placed at the top of the list and have a green dot, while offline members are below, and greyed out.
The conversationMembers/ConversationMembers/ConversationMembers.tsx
component displays the list of members that belong to the conversations.
It calls the fetchMembers command to get the list of members in the conversation from PubNub and stores the members in the local store.
The component also calls the fetchHereNow command from the Redux framework. This command uses presence to indicate if members in the conversation are online. Presence allows you to track the state of users in realtime. When users are connected to the app and present in the conversation, their "present" state is indicated with a green dot. If they are away from the app, their "away" state is indicated by removing the green dot and graying out their name.
export const getCurrentConversationMembers = createSelector(
[
getUsersById,
getCurrentConversationId,
getUsersByConversationId,
getPresenceByConversationId
],
(
users: UsersIndexedById,
conversationId: string,
conversationMemberships: MembershipHash,
conversationPresence: ConversationPresence
): UserFragment[] => {
let presence = conversationPresence[conversationId];
return conversationMemberships[conversationId]
? conversationMemberships[conversationId].map(user => {
return {
...users[user.id],
presence: presence
? presence.occupants.filter(occupant => {
return occupant.uuid === user.id;
}).length > 0
: false
};
})
: [];
}
);
const orderByPresence = (members: UserFragment[]) => {
return members.sort((userA, userB) =>
userA.presence === userB.presence ? 0 : userA.presence ? -1 : 1
);
};
const ConversationMembers = () => {
const members: UserFragment[] = useSelector(getCurrentConversationMembers);
const currentConversationId = useSelector(getCurrentConversationId);
const dispatch = useDispatch();
const pubnub = usePubNub();
const views = useSelector(getViewStates);
const themeContext = useContext(ThemeContext);
const isSmall = useMediaQuery(themeContext.breakpoint.mediaQuery.small);
useEffect(() => {
if (members.length === 0) {
dispatch(
fetchMembers({
spaceId: currentConversationId,
include: {
userFields: true,
customUserFields: true,
totalCount: false
}
})
);
dispatch(
fetchHereNow({
channels: [currentConversationId]
})
);
}
}, [members, currentConversationId, pubnub, dispatch]);
return (
<Wrapper
animate={views.ConversationMembers ? "open" : "closed"}
variants={getAnimatedWrapperVariants(isSmall)}
>
<Header>
<Title>
<BackIconWrapper
onClick={() => {
dispatch(conversationMembersViewHidden());
}}
>
<BackIcon title="back" />
</BackIconWrapper>
Members
</Title>
<CloseIcon
onClick={() => {
dispatch(conversationMembersViewHidden());
}}
>
<CrossIcon title="close members list" />
</CloseIcon>
</Header>
<ScrollableView>
{orderByPresence(members).map(user => (
<MemberDescription user={user} key={user.id} />
))}
</ScrollableView>
</Wrapper>
);
};
export { ConversationMembers };
Member Details
The MemberDescription
method (in conversationMembers/MemberDescription/MemberDescription.tsx
) displays the name, profile image, and title of each member.
The member list also displays user presence to indicate if users are online or offline within a conversation.
The app fetches the presence state from the user.presence
flag in the local store.
The next section provides more details on presence events.
const MemberDescription = ({ user }: MemberDescriptionProps) => {
return (
<Wrapper>
<Avatar>
<UserInitialsAvatar
size={36}
name={user.name}
userId={user.id}
muted={!user.presence}
/>
</Avatar>
<About>
<UserName muted={!user.presence}>
{user.name}{" "}
{user.presence && <PresenceDot presence={user.presence} />}
</UserName>
<UserTitle muted={!user.presence}>{user.custom.title}</UserTitle>
</About>
</Wrapper>
);
};
export { MemberDescription };
Presence Events
The features/memberPresence/memberPresenceModel.ts
file calls createPresenceReducer() reducer from the Redux framework.
This automatically updates the presence state for users in the local store.
The reducer responds to actions that are dispatched when presence join, leave, timeout or interval events are received by the app.
/**
* Create a reducer to presence information for conversation members
*/
const MemberPresenceReducer = createPresenceReducer();
export { MemberPresenceReducer };
Presence Occupancy
The currentConversation/ConversationOccupancy/ConversationOccupancy.tsx
component uses joinedCount
and presentCount
to show counts of all members in the conversation and the count of members who are currently online.
The component uses the getUsersByConversationId
selector to get all members from the local store and getPresenceByConversationId
to get online users from the local store.
export interface ConversationOccupancyFragment {
joinedCount: number;
presentCount: number;
}
export const getCurrentConversationOccupancy = createSelector(
[
getCurrentConversationId,
getUsersByConversationId,
getPresenceByConversationId
],
(
currentConversationId: string,
conversationMemberships: MembershipHash,
conversationPresence: ConversationPresence
): ConversationOccupancyFragment => {
const members = conversationMemberships[currentConversationId];
const presence = conversationPresence[currentConversationId];
return {
joinedCount: members ? members.length : 0,
presentCount: presence ? presence.occupancy : 0
};
}
);
const ConversationOccupancy = () => {
const {
joinedCount,
presentCount
}: ConversationOccupancyFragment = useSelector(
getCurrentConversationOccupancy
);
const views = useSelector(getViewStates);
const isConversationMembersLayoutVisible = views.ConversationMembers;
const dispatch = useDispatch();
return (
<Wrapper
highlighted={isConversationMembersLayoutVisible}
onClick={() => {
isConversationMembersLayoutVisible
? dispatch(conversationMembersViewHidden())
: dispatch(conversationMembersViewDisplayed());
}}
>
<OccupancyNumber>
<em>{presentCount}</em> | {joinedCount}
</OccupancyNumber>
<IconWrapper>
<PeopleGroupIcon
title={
isConversationMembersLayoutVisible
? "Hide members list"
: "Show conversation members"
}
active={isConversationMembersLayoutVisible}
/>
</IconWrapper>
</Wrapper>
);
};
export { ConversationOccupancy };