🎉 New! Web Push Notifications for Chatkit. Learn more in our latest blog post.
Hide
Products
chatkit_full-logo

Extensible API for in-app chat

channels_full-logo

Build scalable realtime features

beams_full-logo

Programmatic push notifications

Developers

Docs

Read the docs to learn how to use our products

Tutorials

Explore our tutorials to build apps with Pusher products

Support

Reach out to our support team for help and advice

Sign in
Sign up

How to enable browser notifications for Chatkit

  • Ayooluwa Isaiah

August 5th, 2019
You will need Node 8+ and npm installed on your machine.

It is important to provide some way of notifying users when new messages are received in a chat application. This helps to inform users about new activity in a chatroom so that they don’t miss it.

This article will describe how you can add browser notifications to a Chatkit powered application. I’ll demo this concept by building a small app that sends a browser notification whenever a new message is sent to a room.

Prerequisites

To follow along with this tutorial, you need to have Node.js (version 8 or later), and npm installed on your machine. Prior working experience with React and Chatkit is also required for you to gain a full understanding of how the code that will be written in the tutorial works.

Create a new Chatkit instance

Go to your Chatkit dashboard, 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. Once enabled, the endpoint from which the test token will be generated will be displayed for you to copy and paste. Note that the test token is not to be used in production. More information on the authentication process for production apps can be found here.

Next, click the Console tab and create a new user and a brand new room for your instance. Take note of the room ID as we’ll be needing it later on.

Create a new React app

If you don’t have it already, install create-react-app globally, then use it to bootstrap a new React application.

    npm install -g create-react-app
    create-react-app chatkit-browser-notifications

Once the app has been created, cd into the new 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 text editor and populate it with the following styles:

    // src/App.css

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

    .notification-toast {
      width: 100%;
      height: 50px;
      text-align: center;
      font-size: 20px;
      background-color: rebeccapurple;
      color: whitesmoke;
      line-height: 50px;
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
    }

    .notification-toast span {
      text-decoration: underline;
      cursor: pointer;
    }

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

    .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

Next, let’s set up a basic chat interface so we can add the ability to show browser notifications on new messages.

    // 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: "",
          showNotificationToast: false,
        };

        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,
          showNotificationToast,
        } = 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">
            {showNotificationToast ? (
            <div className="notification-toast">
              chatkit needs your permission to <span>enable desktop notifications</span>
            </div>
            ) : null}
            <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 to 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: 0,
          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.

Enable browser notifications for new messages

Once the user is logged in, we need to show a notice that prompts the user to enable notifications in the browser. Let’s add the following functions to methods.js:

    // src/methods.js

    // [..]

    function grantPermission() {
      if (!('Notification' in window)) {
        alert('This browser does not support system notifications');
        return;
      }

      if (Notification.permission === 'granted') {
        new Notification('You are already subscribed to web notifications');
        return;
      }

      if (
        Notification.permission !== 'denied' ||
        Notification.permission === 'default'
      ) {
        Notification.requestPermission().then(result => {
          if (result === 'granted') {
            new Notification(
              'New notification from Chatkit', {
                body: "Awesome, you will start receiving notifications for new messages"
              }
            );
          }
        });
      }

      this.setState({
        showNotificationToast: false
      });
    };

    function showNotificationToast() {
      if (window.Notification && Notification.permission === "granted") return

      this.setState({
        showNotificationToast: true
      });
    }

    export {
      handleInput,
      connectToRoom,
      connectToChatkit,
      sendMessage,
      grantPermission,
    }

Then update the connectToRoom function as follows:

    // src/methods.js

    function connectToRoom(roomId = "<your room id>") {
      // [..]

      return currentUser
        .subscribeToRoomMultipart({
          // [..]
        })
        .then(currentRoom => {
          // [..]

          showNotificationToast.call(this);
        })
        .catch(console.error);
    }

Import the grantPermission function in App.js and use it as follows:

    // src/App.js

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

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

    class App extends Component {
      constructor() {

        // [..]
        this.grantPermission = grantPermission.bind(this);
      }

      render() {
        // [..]

        return (
          <div className="App">
            {showNotificationToast ? (
            <div className="notification-toast">
              Chatkit needs your permission to <span onClick={this.grantPermission}>enable desktop notifications</span>
            </div>
            ) : null}

            // [..]
          </div>
        );
      }
    }

    export default App;

Now, once the notice is clicked, you’ll get a prompt asking you to grant permission to show notifications. Make sure to grant that permission, otherwise you won’t get any notifications.

Once permission is granted, we need a way to show a notification when a new message is sent to the room. This notification should only display if the message is sent by someone other than the current user.

Modify your src/methods.js file as follows:

    // src/methods.js

    function showNotification(message) {
      let messageText;
      message.parts.forEach(p => {
        messageText = p.payload.content;
      });

      new Notification(message.senderId, {
        body: messageText,
      });
    };

    function connectToRoom(roomId = "<your room id>") {
      // [..]

      return currentUser
        .subscribeToRoomMultipart({
          roomId,
          messageLimit: 0,
          hooks: {
            onMessage: message => {
              // [..]

              if (message.senderId !== currentUser.id) {
                showNotification(message)
              }
            },
          }
        })
        .then(currentRoom => {
          // [..]
        })
        .catch(console.error);
    }

Now, create another user on your Chatkit instance and add them to the room you created earlier. Then test the app by sending a few messages in the room using both users. It should work similarly to the GIF below:

Wrap up

This article has shown you how to show a browser notification for new messages in a Chatkit powered application. 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.

Clone the project repository
  • Chat
  • CSS
  • JavaScript
  • Node.js
  • React Native
  • Chatkit

Products

  • Channels
  • Chatkit
  • Beams

© 2019 Pusher Ltd. All rights reserved.

Pusher Limited is a company registered in England and Wales (No. 07489873) whose registered office is at 160 Old Street, London, EC1V 9BW.