Tutorial 3: Tracking users with APIDEO

>Level: intermediate
  • English
  • Français

In this third tutorial, we will learn how to send track users joining and leaving rooms in real time. We will use this tracking capability to develop a simple page listing users connected to a web page.

This tutorial will contain one web page. This web page will contain one textbox and a list.

The textbox is used by the user to enter his nickname. In the list, the nickname of connected users is dynamically updated.

For this tutorial, we will use the Prototype Javascript Framework. We will use the basic features of this framework to access the DOM and modify the table of users.

You can download Prototype here.

The template for this application is just below:

Application's template

<html>
    <head>
	<script src="[path to prototype]/prototype.js"></script>
        <script src="[path to apideo]/apideo/apideo.js"></script>
        <script type="text/javascript">
	function init() {
	    Event.observe($("nicknameTbx"), 'keyup', updateUser);
 
		myRoomFactory = Apideo.connect("112233445566778899")
		myRoom = myRoomFactory.joinRoom("userlistroom", "default nickname")
	}
 
	function updateUser() {
		var nickname = $("nicknameTbx").value
		alert(nickname)
	}
 
	Event.observe(window, 'load', init)
 
        </script>
    </head>
    <body>
	<div>
	    My nickname:
	    <input id="nicknameTbx" type="text" size="32" 
                value="default nickname" />
	</div>
	<div>
	List of connected users:
	<table id="userslist">
		<tr>
			<th>User name</th>
			<th>Arrived at</th>
		</tr>
	</table>
	</div>
    </body>
</html>

If you have already used prototype, this code should be familiar. Please be sure to replace [path to prototype]/ and [path to apideo]/ by the correct paths.

First, let's analyse the <body> tag. This tag contains a textbox that can be used by the user to enter its nickname. By default, the nickname for the user is "default nickname".

Below the nickname part, there is a table, with one header row. This table will receive the list of all connected users. Right now, it is empty.

Now, let's have a closer look at the Javascript part. If you have never used prototype before, you will certainly notice the Event.observe calls. These are the event handlers prototype uses. We set 2 event handlers. One for the "load" event of the window: when the page is loaded, the init function is executed. The other for the "keyup" event of the textbox. Each time a key is released in the textbox, the updateUser() function will be executed.

If you are not used to prototype, you might also want to know that $('nicknameTbx') is a shortcut for document.getElementById('nicknameTbx').

Now, let's have a look at the Apideo part of the code. If you have read the other tutorials, you probably already know the 2 functions that we used: Apideo.connect to connect to Apideo, and ApideoRoom.joinRoom to join a room.

However, you will notice that we passed 2 arguments to the ApideoRoom.joinRoom function. The first argument is the name of the room, as usual. The second argument is the user description. This can be any kind of JSON object. In our case, we only need to pass the nickname of the user, so we pass a simple string. If we needed to pass additional arguments, we could pass an object instead of the string.

Apideo room events handlers

Now, we need to display the users in the table.

First, we need to get the list of users. The list of users is automatically sent to the room when it is loaded. Therefore, we need to know the room has been fully loaded.

The ApideoRoom.onLoad function is an event handler function tied to the "room loaded" event. You can pass a function to ApideoRoom.onLoad, and this function will be executed when the room is loaded (see exemple below).

When the room finished loading, you can use the room.forEachUser() method to go through the list of users connected to the room.

The room.forEachUser() method takes a "callback" function in parameter that will be called for each user that is present in the room (including you).

The callback function takes 5 parameters:

  • userId (string): this is the unique ID of the user in that room
  • userObj (JSON object): this is the JSON object that has been passed by the user when joining the room (or when calling updateUserData)
  • joinTimeStamp (Date): this is the time when the user joined the room, expressed as a Date object
  • modifyTimeStamp (Date): this is the time marking the last call to the updateUserData function for that user, expressed as a Date object. If the object was never modified, this is equal to joinTimeStamp.

Our code will look like this:

myRoom.onLoad(function() {
    this.forEachUser(function(id, userObj, createTimeStamp) {
	var nickname = userObj
        nickname = nickname.replace("<", "&lt;")
	Element.insert($("userslist"), '<tr><td>\
            '+nickname+'</td><td>'+createTimeStamp+'</td></tr>')
    });
});

This code binds a function to the onLoad event of the room. It iterates over the list of users, gets back the nickname of each user (in the userObj parameter) and the date each user entered the room.

Take a close look at the line:

nickname = nickname.replace("<", "&lt;")

This replaces all < symbols in the nickname with &lt; symbols. This way, we are sure that a malicious user wanting to inject code in its nickname won't manage to run the code on the other users.

Then, for each user, the Element.insert function is called. This is a prototype function that inserts some HTML automatically at the end of the table.

The table is displayed when the room is first loaded. Now, we need to update the table when a new user joins the room, or when a user updates its nickname, or when a user quits the room.

Hopefully, to do this, we have the ApideoRoom.onUserJoin, ApideoRoom.onUserUpdate and ApideoRoom.onUserQuit functions.

First, when a user arrives:

myRoom.onUserJoin(function(userId, userObj, createTimeStamp) {
	var nickname = userObj
	nickname = nickname.replace("<", "&lt;")
	Element.insert($("userslist"), '<tr id="'+userId+'">\
		<td>'+nickname+'</td><td>'+createTimeStamp+'</td></tr>')
});

The ApideoRoom.onUserJoin callback function is passed 3 parameters: the user id, and the user object, and the date the user entered the room (that is now). It is called automatically each time a new user joins the room.

Now, we must handle disconnects:

myRoom.onUserQuit(function(userId, userObj, timeStamp) {
	Element.remove($(userId))
});

The ApideoRoom.onUserQuit callback function is also passed 3 parameters: the user id, and the user object and the date the user left the room (now). It is called automatically each time a new user quits the room.

Finally, we want to be able to update the nickname when the user types a new text in the textbox, and to receive the changes. We will do that through the ApideoRoom.updateUserData and ApideoRoom.onUserUpdate functions.

myRoom.onUserUpdate(function(userId, userObj, createTimeStamp, modifyTimeStamp) {
	var nickname = userObj
	nickname = nickname.replace("<", "&lt;")	
	Element.replace($(userId), '<tr id="'+userId+'">\
	    <td>'+nickname+'</td><td>'+createTimeStamp+'</td></tr>')
});

The ApideoRoom.onUserUpdate callback function is passed 4 parameters: the user id, the user object, the date the user joined the room and the date the user updated its profile (this is "now" expressed by the server time). It is called automatically each time a new user updates its object.

function updateUser() {
	var nickname = $("nicknameTbx").value
	myRoom.updateUserData(nickname)
 
	// Let's update my row too
        nickname = nickname.replace("<", "&lt;")
	Element.replace($(myRoom.myId), '<tr id="'+myRoom.myId+'">\
		<td>'+nickname+'</td>\
		<td>'+new Date(myRoom.userList[myRoom.myId].createTimeStamp)+'</td></tr>')
}

The ApideoRoom.updateUserData function is called by a user when he wants to update its profile. The user passes in parameter the object that must be sent to the other users (just like the second argument of the joinRoom function).

When ApideoRoom.updateUserData function is used, the onUserUpdate event will be triggered for all the users of the room, except for the user that is performing the update. Therefore, we have to do it manually. We can get the id of the current ID by calling ApideoRoom.getMyUserId().

The complete source code for the sameple is displayed below:

Complete source code

<html>
    <head>
	<script src="[path to prototype]/prototype.js"></script>
        <script src="[path to apideo]/apideo/apideo.js"></script>
        <script type="text/javascript">
	function init() {
	    Event.observe($("nicknameTbx"), 'keyup', updateUser);
 
	    myRoomFactory = Apideo.connect("112233445566778899")
	    myRoom = myRoomFactory.joinRoom("userlistroom", "default nickname")
 
 
            myRoom.onLoad(function() {
                this.forEachUser(function(id, userObj, createTimeStamp) {
            	var nickname = userObj
                    nickname = nickname.replace("<", "&lt;")
            	Element.insert($("userslist"), '<tr><td>\
                        '+nickname+'</td><td>'+createTimeStamp+'</td></tr>')
                })
            });
 
            myRoom.onUserJoin(function(userId, userObj, createTimeStamp) {
            	var nickname = userObj
            	nickname = nickname.replace("<", "&lt;")
            	Element.insert($("userslist"), '<tr id="'+userId+'">\
            		<td>'+nickname+'</td><td>'+createTimeStamp+'</td></tr>')
            });
 
            myRoom.onUserQuit(function(userId, userObj, timeStamp) {
            	Element.remove($(userId))
            });
 
            myRoom.onUserUpdate(function(userId, userObj, createTimeStamp, modifyTimeStamp) {
            	var nickname = userObj
            	nickname = nickname.replace("<", "&lt;")	
            	Element.replace($(userId), '<tr id="'+userId+'">\
            	    <td>'+nickname+'</td><td>'+createTimeStamp+'</td></tr>')
            });
	}
 
	function updateUser() {
	    var nickname = $("nicknameTbx").value
	    myRoom.updateUserData(nickname)
 
	    // Let's update my row too
            nickname = nickname.replace("<", "&lt;")
	    Element.replace($(myRoom.myId), '<tr id="'+myRoom.myId+'">\
		<td>'+nickname+'</td>\
		<td>'+new Date(myRoom.userList[myRoom.myId].createTimeStamp)+'\
                </td></tr>')
	}
 
	Event.observe(window, 'load', init)
 
        </script>
    </head>
    <body>
	<div>
	    My nickname:
	    <input id="nicknameTbx" type="text" size="32" 
                value="default nickname" />
	</div>
	<div>
	List of connected users:
	<table id="userslist">
		<tr>
			<th>User name</th>
			<th>Arrived at</th>
		</tr>
	</table>
	</div>
    </body>
</html>

That's it! Now, you know how to track users in real time in the Apideo rooms. Tracking users in a room can be useful in a many scenarios and the code in this sample was just a fun way to make a trip around the user management functions.

By combining tutorial 1, tutorial 2 and tutorial 3, you have enough knowledge to develop your own video conference application, featuring user list of connected people, call management, video conversation and chat.

Have fun with Apideo!