Team Chat Powered by PubNub

Team Chat Powered by PubNub

    ›Tutorial

    Quickstart

    • Introduction
    • Run Locally
    • Advanced Features

    Tutorial

    • Overview
    • Setup Theming
    • Add PubNub Keys
    • Populate Data
    • Login
    • Show User Details
    • Show Conversations
    • Send Messages
    • Join Conversation
    • Leave Conversation
    • Show Members
    • Send Typing Indicators
    • Profanity Filter

    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.

    members list

    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 };
    
    ← Leave ConversationSend Typing Indicators →
    • Member Details
    • Presence Events
    • Presence Occupancy
    Docs
    QuickstartTutorialThemeingPubNub Chat
    Source
    GitHubStar
    PubNub
    Copyright © 2020 PubNub