How to show unread message count with React

How to show unread message count with React

In this article, you'll learn how to show unread message count with React

Originally published by Ayooluwa Isaiah at https://pusher.com

In this tutorial, we’ll go over how this feature works in Chatkit. In the end, you should have a chat app that looks and works like this:

Most chat apps present the unread count for messages in a room somewhere in the application interface. This allows the user to scan the interface and quickly see how many unread messages they have and in what rooms.

Prerequisites

This tutorial assumes prior experience with working with React and Chatkit. If this is your first attempt at using Chatkit, you should probably start here first, then come back to this tutorial at a later time.

You also need to have Node.js (version 8 or later), and npm installed on your machine. Installation instructions can be found on this page.

Sign up for Chatkit

Open this link in a new browser tab, and create a new Chatkit account or sign into your existing account. Once you are logged in, create a new Chatkit instance for this tutorial and take note of your Instance Locator and Secret Key in the Credentials tab.

Also make sure your Test Token Provider is enabled as shown below. One enabled, the endpoint from which the test token will be generated will be displayed for you to copy and paste.

Next, click the Console tab and create a new user and room for your instance. You can follow the instructions on this page to learn how to do so. Once the room has been created, take note of the room ID as we’ll be using it later on.

Create a new React app

Run the command below to install create-react-app globally on your machine, then use it to bootstrap a new react React application:

npm install -g create-react-app 
create-react-app chatkit-unread-messages

Once the app has been created, cd into the new chatkit-unread-messages directory and install the following additional dependencies that we’ll be needing in the course of building the chat app:

npm install @pusher/chatkit-client prop-types skeleton-css --save

You can now start your development server by running npm start then navigate to http://localhost:3000 in your browser to view the app.

Add the styles for the app

Open up src/App.css in your code editor, and change it to look like this:

   // src/App.css

   .App {      width: 100vw;      height: 100vh;      display: flex;      overflow: hidden;    }

   .sidebar {      height: 100%;      width: 20%;      background-color: blanchedalmond;    }

   .login {      padding: 5px 20px;    }

   .sidebar input {      width: 100%;    }

   .chat-rooms .active {      background-color: whitesmoke;      color: #181919;    }

   .chat-rooms li {      display: flex;      align-items: center;      justify-content: space-between;      padding: 15px 20px;      font-size: 18px;      color: #181919;      cursor: pointer;      border-bottom: 1px solid #eee;      margin-bottom: 0;    }

   .room-list h3 {      padding-left: 20px;      padding-right: 20px;    }

   .room-unread {      display: inline-block;      width: 20px;      height: 20px;      line-height: 20px;      border-radius: 50%;      font-size: 16px;      text-align: center;      padding: 5px;      background-color: greenyellow;      color: #222;    }

   .chat-rooms li:hover {      background-color: #D8D1D1;    }

   .chat-screen {      display: flex;      flex-direction: column;      height: 100vh;      width: calc(100vw - 20%);    }

   .chat-header {      height: 70px;      flex-shrink: 0;      border-bottom: 1px solid #ccc;      padding-left: 10px;      padding-right: 20px;      display: flex;      flex-direction: column;      justify-content: center;    }

   .chat-header h3 {      margin-bottom: 0;      text-align: center;    }

   .chat-messages {      flex-grow: 1;      overflow-y: scroll;      display: flex;      flex-direction: column;      justify-content: flex-end;      margin-bottom: 0;      min-height: min-content;    }

   .message {      padding-left: 20px;      padding-right: 20px;      margin-bottom: 10px;      display: flex;      justify-content: space-between;      align-items: flex-start;    }

   .message span {      display: block;      text-align: left;    }

   .message .user-id {      font-weight: bold;    }

   .message-form {      border-top: 1px solid #ccc;    }

   .message-form, .message-input {      width: 100%;      margin-bottom: 0;    }

   input[type="text"].message-input {      height: 50px;      border: none;      padding-left: 20px;    }

Create a basic chat application

Let’s set up a basic chat interface so that we can add the functionality to show the unread message count. The code below should be straight-forward enough to understand if you’ve worked with Chatkit before:

   // src/App.js

   import React, { Component } from "react";    import {      handleInput,      connectToChatkit,      connectToRoom,      sendMessage,    } from "./methods";

   import "skeleton-css/css/normalize.css";    import "skeleton-css/css/skeleton.css";    import "./App.css";

   class App extends Component {      constructor() {        super();        this.state = {          userId: "",          currentUser: null,          currentRoom: null,          rooms: [],          messages: [],          newMessage: "",        };

       this.handleInput = handleInput.bind(this);        this.connectToChatkit = connectToChatkit.bind(this);        this.connectToRoom = connectToRoom.bind(this);        this.sendMessage = sendMessage.bind(this);      }

     render() {        const {          rooms,          currentRoom,          currentUser,          messages,          newMessage,        } = this.state;

       const roomList = rooms.map(room => {          const isRoomActive = room.id === currentRoom.id ? 'active' : '';          return (            <li               className={isRoomActive}               key={room.id}               onClick={() => this.connectToRoom(room.id)}             >               <span className="room-name">{room.name}</span>             </li>          );        });

       const messageList = messages.map(message => {          const arr = message.parts.map(p => {              return (                <span className="message-text">{p.payload.content}</span>              );          });

         return (            <li className="message" key={message.id}>               <div>                 <span className="user-id">{message.senderId}</span>                 {arr}               </div>             </li>          )        });

       return (          <div className="App">            <aside className="sidebar left-sidebar">              {!currentUser ? (                  <div className="login">                    <h3>Join Chat</h3>                    <form id="login" onSubmit={this.connectToChatkit}>                      <input                        onChange={this.handleInput}                        className="userId"                        type="text"                        name="userId"                        placeholder="Enter your username"                      />                    </form>                  </div>                ) : null              }              {currentRoom ? (                <div className="room-list">                  <h3>Rooms</h3>                  <ul className="chat-rooms">                    {roomList}                  </ul>                </div>                ) : null              }            </aside>            {              currentUser ? (                <section className="chat-screen">                  <ul className="chat-messages">                    {messageList}                  </ul>                  <footer className="chat-footer">                    <form onSubmit={this.sendMessage} className="message-form">                      <input                        type="text"                        value={newMessage}                        name="newMessage"                        className="message-input"                        placeholder="Type your message and hit ENTER to send"                        onChange={this.handleInput}                      />                    </form>                  </footer>                </section>              ) : null            }          </div>        );      }    }

   export default App;

Create a new methods.js file within the src directory and add the following code into it:

   // src/methods.js

   import { ChatManager, TokenProvider } from "@pusher/chatkit-client";

   function handleInput(event) {      const { value, name } = event.target;

     this.setState({        [name]: value      });    }

   function connectToChatkit(event) {      event.preventDefault();      const { userId } = this.state;

     const tokenProvider = new TokenProvider({        url:          "<test token provider endpoint>"      });

     const chatManager = new ChatManager({        instanceLocator: "<your chatkit instance locator>",        userId,        tokenProvider      });

     return chatManager        .connect()        .then(currentUser => {          this.setState(            {              currentUser,            },            () => connectToRoom.call(this)          );        })        .catch(console.error);    }

   function connectToRoom(roomId = "<your chatkit room id>") {      const { currentUser } = this.state;      this.setState({        messages: []      });

     return currentUser        .subscribeToRoomMultipart({          roomId,          messageLimit: 10,          hooks: {            onMessage: message => {              this.setState({                messages: [...this.state.messages, message],              });            },          }        })        .then(currentRoom => {          this.setState({            currentRoom,            rooms: currentUser.rooms,          });        })        .catch(console.error);    }

   function sendMessage(event) {      event.preventDefault();      const { newMessage, currentUser, currentRoom } = this.state;      const parts = [];

     if (newMessage.trim() === "") return;

     parts.push({        type: "text/plain",        content: newMessage      });

     currentUser.sendMultipartMessage({        roomId: ${currentRoom.id},        parts      });

     this.setState({        newMessage: "",      });    }

   export {      handleInput,      connectToRoom,      connectToChatkit,      sendMessage,    }

Make sure to replace the <test token provider endpoint>, <your chatkit instance locator> and <your chatkit room id> placeholders above with the appropriate values from your Chatkit dashboard.

Open the app in a few different tabs and join the chatroom under different usernames. Send a few messages for each user. It should work just fine, similar to the GIF below:

Show the unread message count

Chatkit provides an unreadCount property that we can hook into to display the number of unread messages for the current user in a room. But how do we know when a user has read a message? This is where read cursors come in.

When a user enters a room, messages in the room are loaded into the chat interface according to the messageLimit property. At this point, it is safe to say that the user has read all the messages in that room and we can indicate this by setting a read cursor on the last message.

Change the onMessage hook in the connectToRoom function as follows:

   // src/methods.js

   function connectToRoom(roomId = "21431542") {      const { currentUser } = this.state;      this.setState({        messages: []      });

     return currentUser        .subscribeToRoomMultipart({          roomId,          messageLimit: 10,          hooks: {            onMessage: message => {              this.setState({                messages: [...this.state.messages, message],              });

             const { currentRoom } = this.state;

             if (currentRoom === null) return;

             return currentUser.setReadCursor({                roomId: currentRoom.id,                position: message.id,              });            },          }        })        .then(currentRoom => {          this.setState({            currentRoom,            rooms: currentUser.rooms,          });        })        .catch(console.error);    }

What this means is that, if the user leaves the room by any means (such as by going offline, or switching to another room) any messages sent after the last read cursor position would count towards the unreadCount.

We can then use the onRoomUpdated connection hook to update the room in the application state and display the unread count in the chat interface. Add the hook as shown below:

   // src/methods.js

   function connectToChatkit(event) {      // [..]

     return chatManager        .connect({          onRoomUpdated: room => {            const { rooms } = this.state;            const index = rooms.findIndex(r => r.id === room.id);            rooms[index] = room;            this.setState({              rooms,            });          }        })        .then(currentUser => {          this.setState(            {              currentUser,            },            () => connectToRoom.call(this)          );        })        .catch(console.error);    }

Finally, we want to display a room’s unread count if the number of unread messages is greater than zero. Update the roomList variable in the render() method of our chat app as shown below:

   // src/App.js

   const roomList = rooms.map(room => {      const isRoomActive = room.id === currentRoom.id ? 'active' : '';      return (        <li           className={isRoomActive}           key={room.id}           onClick={() => this.connectToRoom(room.id)}         >           <span className="room-name">{room.name}</span>           {room.unreadCount > 0 ? (             <span className="room-unread">{room.unreadCount}</span>           ): null}         </li>      );    }); 

You can simulate unread messages by creating a different room and switching to it as one user, then send messages to the first room as another user. It will display the unread count for any of the users as appropriate:

Wrap up

In this tutorial, you learned how add unread message count to your Chatkit app. We explored how to use read cursors to determine if a user has read a message, and how to display the number of unread messages in the application interface.

You can checkout other things Chatkit can do by viewing its extensive documentation. Don't forget to grab the complete source code in this GitHub repository.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading about React

React - The Complete Guide (incl Hooks, React Router, Redux)

Modern React with Redux [2019 Update]

Best 50 React Interview Questions for Frontend Developers in 2019

JavaScript Basics Before You Learn React

Microfrontends — Connecting JavaScript frameworks together (React, Angular, Vue etc)

Reactjs vs. Angularjs — Which Is Best For Web Development

React + TypeScript : Why and How

How To Write Better Code in React

React Router: Add the Power of Navigation

Getting started with React Router

Using React Router for optimizing React apps


reactjs javascript css node-js chatbot

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How to Hire Node.js Developers And How Much Does It Cost?

A Guide to Hire Node.js Developers who can help you create fast and efficient web applications. Also, know how much does it cost to hire Node.js Developers.

What is Difference Between Node.js and ReactJS?

What is difference between Node.js and ReactJS? The main difference between Node. js and React. js is, while Node. js a back-end framework, React is used for developing user interfaces.

Top 7 Most Popular Node.js Frameworks You Should Know

Node.js is an open-source, cross-platform, runtime environment that allows developers to run JavaScript outside of a browser. In this post, you'll see top 7 of the most popular Node frameworks at this point in time (ranked from high to low by GitHub stars).

Hire Node.JS Developers | Skenix Infotech

We are providing robust Node.JS Development Services with expert Node.js Developers. Get affordable Node.JS Web Development services from Skenix Infotech.