Chat

Create a Chat App with PubNub and Chirp

6 min read Chandler Mayo on Apr 19, 2019
Create a Chat App with PubNub and Chirp featured image.png

This is the second part of a series of tutorials on building a chat application in JavaScript that uses audible chirps to connect users to the same channel using the Chirp WebAssembly SDK and PubNub JavaScript SDK.

Check out the first post, How to Send Chat Invites Using Chirp, before continuing with this section. When you’re finished with this section check out the last post Using Chirp with PubNub for Chat.

Looking for the completed project? Download it from the Chirp PubNub Chat GitHub repo or try the demo.

Use Chirp to connect users to the same chat.

Getting started

Create a new directory with the name of your project.

Download the Chirp WebAssembly SDK and move the files “chirp-connect.js” and “chirp-connect.wasm” into that directory.

Create 3 empty files: “index.html”, “styles.css”, and “chat.js”.

Creating the chat with Chirp

We’ll start by creating a simple chat app and two modals to tell our users what’s going on.

The “msg-group” div will be used to display messages as they are received.

The “input-group” contains two buttons: “chirp-button” for transmitting a chirp to enable users to join and “send-button” to publish the text in the “input-box” to PubNub.

“welcomeModal” will show when the page loads to explain that the application is listening and button to join as host.

“newChatModal” will show if a chirp is detected and the user is already in a channel. This to let users switch chats without having to refresh the page.

At the bottom of the file, we’ll include the PubNub JavaScript SDK and the Chirp WebAssembly SDK.

Edit “index.html” and replace its contents:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <link rel="icon" type="image/png" href="icon.png">
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
        <link rel="stylesheet" href="styles.css">
        <title>PubNub Chirp Chat</title>
    </head>
    <body>
        <div class="modal" id="welcomeModal" tabindex="-1" role="dialog">
            <div class="modal-dialog modal-dialog-centered" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title">Waiting for chirp...</h5>
                    </div>
                    <div class="modal-body">
                        <p>Allow access to your microphone to listen for chirps. If a chirp is detected you will be connected to the chat. Your microphone is not recorded or streamed to any server.</p>
                        <p>Create a chat to start your own channel and broadcast a chirp for others to join you.</p>
                    </div>
                    <div class="modal-footer">
                        <button id="host-button" type="button" class="btn btn-primary">Create Chat</button>
                    </div>
                </div>
            </div>
        </div>
        <div class="modal" id="newChatModal" tabindex="-1" role="dialog">
            <div class="modal-dialog modal-dialog-centered" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title">Chirp Detected</h5>
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
                    </div>
                    <div class="modal-body">
                        <p>A chirp was detected. Do you want to stay in your current chat or join the new chat?</p>
                    </div>
                    <div class="modal-footer">
                        <button type="button" data-dismiss="modal" class="btn btn-secondary">Cancel</button>
                        <button id="join-button" type="button" class="btn btn-primary">Join Chat</button>
                    </div>
                </div>
            </div>
        </div>
        <div class="msg-group center">                 
        </div>
        <div class="input-group mb-3">
            <div class="input-group-prepend">
                <button id="chirp-button" class="btn btn-secondary" type="button">Chirp</button>
            </div>
            <textarea id="input-box" class="form-control" rows="1"></textarea>
            <div class="input-group-append">
                <button id="send-button" class="btn btn-primary" type="button">Send Message</button>
            </div>
        </div>
        <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
        <script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.23.0.js"></script>
        <script src="chirp-connect.js"></script>
        <script src="chat.js"></script>
    </body>
</html>

Styling the Chirp chat app

Edit “styles.css” and replace its contents:

.center {
  position: absolute;
  right: 0px;
  left: 0px;
  margin: auto;
}
.msg-group {
  position: absolute;
  max-width: 700px;
  height: 90%;
  overflow-y: scroll;
}
.card {
  padding: 8px 0px 8px 0px;
}
.input-group {
  position: absolute;
  height: 10%;
  bottom: 0px;
}
textarea {
  resize: none;
}

Integrating Chirp and PubNub for a unique web chat app

We’ll use PubNub for chat and Chirp to establish the chat connection.

Chirp and PubNub keys are referenced at the top of the file.

Use the class “chatControl” to format our chat messages for the “msg-group” div.

Add a listener to receive messages from PubNub and determine who published the messages. Additionally, display a welcome message when the user has connected.

Create a function to publish messages to PubNub using the current channel.

Initialize Chirp with a listener for receiving channel names.

Button functions for transmitting a chirp, starting a new chat as host, and joining a new chat when switching channels.

Edit “chat.js” and replace its contents:

const { Chirp, toAscii } = ChirpConnectSDK;
const key = 'YOUR_CHIRP_KEY_HERE'; // Your key from Chirp.io.
var sdk;
var newChannel = ""; // Stores new channel names when detected with Chirp.
var channel = ""; // Stores current chat channel.
pubnub = new PubNub({ // Your PubNub keys here. Get them from https://admin.pubnub.com.
    publishKey : 'YOUR_PUBNUB_PUBLISH_KEY_HERE',
    subscribeKey : 'YOUR_PUBNUB_SUBSCRIBE_KEY_HERE'
});
msgList = $('.msg-group');
inputBox = $('#input-box');
sendButton = $('#send-button');
joinButton = $('#join-button');
hostButton = $('#host-button');
chirpButton = $('#chirp-button'); // Button to play chip sound for current channel.
welcomeModal = $('#welcomeModal'); // Modal for connection instructions.
newChatModal = $('#newChatModal'); // Modal for when a new channel is detected.
class chatControl { // Formats messages.
    publishMessage(name, msg) {
        msgList.append(this.msg(name, msg, 'right', 'primary'));
        this.scrollToBottom(); 
    }
    receiveMessage(name, msg) {
        msgList.append(this.msg(name, msg, 'left', 'secondary'));
        this.scrollToBottom(); 
    }
    msg(name, msg, side, style) {
        var msgTemp = `
            <div class="card text-white bg-${style}">
                 <div class="card-body">
                     <h6 class="card-subtitle mb-2 text-${side}">${name}</h6>
                     <p class="card-text float-${side}">${msg}</p>
                 </div>
            </div>
            `;
        return msgTemp;
    }
    scrollToBottom() {
        msgList.scrollTop(msgList[0].scrollHeight);
    }
}
var chat = new chatControl();
pubnub.addListener({
    status: function(statusEvent) {
        if (statusEvent.category === "PNConnectedCategory") { // Hide modals and show welcome message.
            welcomeModal.modal('hide');
            newChatModal.modal('hide');
            chat.receiveMessage('Welcome', "You're connected to the chat! Press 'Chirp' to share your chat with a nearby device.");
        }
    },
    message: function(msg) {
    	console.log(msg);
        if (msg.publisher == pubnub.getUUID()) { // Check who sent the message.
            chat.publishMessage('You', msg.message);
        } else {
            chat.receiveMessage('Guest', msg.message);
        }
    },
});
function publishMessage() { // Send messages with PubNub.
  var msg = inputBox.val().trim().replace(/(?:
||
)/g, '<br>'); // Format message. inputBox.val(''); if (msg != '') { var publishConfig = { channel: channel, message: msg }; pubnub.publish(publishConfig, function(status, response) { // Publish message to current channel. console.log(status, response); }); } }; sendButton.on('click', publishMessage.bind()); inputBox.keyup(function (e) { if(e.keyCode == 13 && !e.shiftKey) { publishMessage(); } if (e.keyCode === 27) { inputBox.blur(); } }) Chirp({ key: key, onReceived: data => { if (data.length > 0) { newChannel = toAscii(data); console.log("Channel detected: "+toAscii(newChannel)); if (channel == "") { // First time connecting to chat. channel = newChannel; pubnub.subscribe({ channels: [channel] }); } else if (channel != newChannel) { // Ask if the user wants to connect to the new channel. newChatModal.modal('show'); } } } }) .then(_sdk => { sdk = _sdk; }) .catch(console.warn); function chripSend() { // Chirp current channel name. if (channel != "") { const payload = new TextEncoder('utf-8').encode(channel); sdk.send(payload); } }; chirpButton.on('click', chripSend.bind()); function hostChat() { // Join a chat with the last 8 characters of the users UUID as the channel name. if (channel == "") { channel = pubnub.getUUID().slice(-8); pubnub.subscribe({ channels: [channel] }); } }; hostButton.on('click', hostChat.bind()); function joinChat() { // Join a channel that was detected from a chirp. pubnub.unsubscribe({ // Unsubscribe from old channel. channels: [channel] }); channel = newChannel; // Switch to new channel. inputBox.val(''); msgList.html(''); // Clear messages from old channel. pubnub.subscribe({ channels: [channel] // Join new channel. }); }; joinButton.on('click', joinChat.bind()); welcomeModal.modal({ // Show welcome modal. backdrop: 'static', keyboard: false });

Replace “YOUR_PUBNUB_PUBLISH_KEY_HERE” and “YOUR_PUBNUB_SUBSCRIBE_KEY_HERE” with your Publish Key and Subscribe Key from your PubNub Admin Dashboard.

Replace “YOUR_CHIRP_KEY_HERE” with your Chirp key from the applications page.

Testing the Chirp chat app

Although Chirp sends data completely offline, registration credentials are needed to identify your user account to the SDK, and to configure the SDK with your profile-specific features.

For web-based applications using the Chirp JavaScript or WebAssembly SDKs, applications are identified using your application key, and must be hosted on one of the web origins that you specify in the origins pane of your application.

CORS rules won’t allow the browser to load the Chirp WebAssembly SDK directly from the file system. You must host the files using a server. I recommend a http-server.

Continue with part three of this tutorial, Using Chirp with PubNub for Chat.

0