Gaming

Enabling Users to Create Private Chat via Private Channels

Michael Carroll on Aug 20, 2014
Enabling Users to Create Private Chat via Private Channels

Waving Hand

Good News! We've launched an all new Chat Resource Center.

We recommend checking out our new Chat Resource Center, which includes overviews, tutorials, and design patterns for building and deploying mobile and web chat.

Take me to the Chat Resource Center →

In this tutorial, we'll continue talking about creating multiple channels to spawn chatrooms on demand. In this case, we'll walk you through how to enable users to spawn private chat with another user on demand. This way, users can select who they want to chat with, then initiate a private chat with that user.


We've now covered both building a multiplayer game lobby with a chatroom and the different ways we can use matchmaking to connect two different users. Here's what we've covered so far:

This blog post is Part Eight of PubNub Developer Evangelist Ian Jennings‘s series on creating a multiplayer game with JavaScript.


Refactoring to Object Oriented Patterns

Now that we can spawn chatrooms on demand, let's allow one user to spawn a new private chat on a private channel with another user.

You’ll first need to sign up for a PubNub account. Once you sign up, you can get your unique PubNub keys in the PubNub Developer Portal. Once you have, clone the GitHub repository, and enter your unique PubNub keys on the PubNub initialization, for example:

Up until our most recent tutorial, our code has been only functional. Like last time, our code this time will be object oriented. If we added the ability to spawn chatrooms to our old functional code, things would get confusing very quickly. We'll start by converting our functional code into object oriented code. This will make it easier for us to manage user to user communications.

Object Constructor Pattern and the “this” JavaScript Keyword

We're going to make use of what's known as the Object Constructor Pattern. Let's start with our Alarm closure example from earlier.

What if we wanted to expose the name variable from outside the Alarm() function? We use the this keyword to expose the variable outside of our function.

  • In JavaScript this always refers to the “owner” of the function we're executing, or rather, to the object that a function is a method of. Read more about the “this” JavaScript keyword here.

The “new” Keyword

Notice the changes made to our last lines. Specifically how we changed Alarm() to new Alarm().

  • The new operator creates an instance of a user-defined object type or of one of the built-in object types that has a constructor function.

Instead of simply executing Alarm(), we call new_alarm = new Alarm() which assigns the instance of the function to the variable new_alarm.

We need to use the word new because it instructs the the value of this to be bound to the context of the function. If you forget to use the new keyword, this will be assigned to the global object.

  • If you forget to include the new prefix when calling a constructor function, then this will not be bound to the new object. Sadly, this will be bound to the global object, so instead of augmenting your new object, you will be clobbering global variables. That is really bad. There is no compile warning, and there is no runtime warning.

Douglas Crockford has a great writeup of the object constructor pattern, this, private and public methods and more.

So now let's start refactoring our old challenge example into an Object Constructor pattern.

Self = This

Notice that instead of using this directly, we assign var self = this. This is because the this variable will have a different value within nested functions. We want to ensure that we're always referring to the User context, so we store the context of this in self so we can access it from inside any function.

Calling return self; at the end of the function allows us to string function methods together. For example, we could call a_user.chat().leave().

Remove Data Tied to DOM

We also gain a huge organizational advantage switching to an object oriented pattern. We're able to store references to the DOM elements within the objects themselves, allowing us to modify the page without actually selecting nodes by hand.

Instead of calling $('#' + data.uuid).remove(); we can write:

The variable a_user has stored the DOM node in $tpl so we can access it directly instead of looking it up.

Remove pubnub.here_now()

Remember that we've been using PubNub Presence with JavaScript for user detection of user state. Notice the second parameter, state. Now that we keep users in objects, we can keep the value of their state as a property. This means when we need to find an opponent for matchmaking, we can avoid using the pubnub.here_now() call. We'll already have a list of users and their state ready!

We're going to support an unlimited number of users, so creating objects one at a time will quickly get messy. Instead, we'll create a factory pattern that will keep track of ourUser()s in an array.

Object Extension

The largest change to our app are the changes to me. Instead of me just representing a username, we're going to make it into an object that turns the browser window into an extension of a User.

We start out by assigning the variable self to a new User() with a randomName(). This means Client() will be an extended from User() and have all it's properties likeUser.chat() and User.leave().

We add a couple more methods to the Client: onRequest() and onResponse(). These handle our challenge requests and responses. We only add them to our Client instead of every User as we don't want to accidentally challenge or respond on behalf of another user.

Also, because the Client() extends the User() we can still get the client username from me.uuid. We bind to the DOM events inside of the App() object and then act on behalf of the me object when fired.

Routing PubNub Requests

Last, we create an App() object to initialize our application and handle incoming traffic.

Scoping

It's worth noting a couple additional changes to scope. me turned into a global variable as is Users and some of our global template elements. The function randomName() is still available globally and our skill generator has been turned into randomSkill().

Full Refactored Code

Put it all together and we have the following.

Private Chat with Private Channels Demo

Check out the live demo below. It looks and behaves exactly as our previous example did, but the code has been organized into an object oriented pattern.

See the Pen Memewarz – Private Chat #2 by Ian Jennings (@ianjennings) on CodePen.0

In our next blog post, we'll talk about how to spawn private chatroom popups.