Live Support Lobby Application

The support representative uses the Live Support Lobby as a holding area for customers that are waiting in queue for support. The lobby's sole purpose is to keep a list of clients that need support. Representatives connect to the lobby to connect to the customer they wish to help. When connected, they open a unique Live Support Session, which you will build in the next section. All customers and representatives use only one instance of the lobby.

The Server-Side ActionScript

The Live Support Lobby uses only one Flash Communication Server UI component, the ConnectionLight.

On the server, this application is named LiveSupportLobby. To get ready, create a folder in your FlashCom applications directory called LiveSupportLobby. Then create an empty file called main.asc. This, as always, will be the starting point for creating the application. Next, load the components.asc file to load all the base code for using the components:

load( "components.asc" ); 

You need to consider what the application will require when it starts. The application.onAppStart method is always called before any users are connected. Just as its name implies, application.onAppStart is called when the application instance is started or run for the first time. This is where you place ActionScript that needs to run before accepting connections. There are four tasks this application needs to do:

  • Keep track of customers.

  • Keep track of representatives.

  • Keep a SharedObject with a list of customers.

  • Have some way to validate representatives.

Initializing the Application (onAppStart)

You can Create an ActionScript object that will be used to look up a client:

application.onAppStart = function(){ 
      /* Holds all connect clients, key: username value: Server-side client Object */
this.supportClients = new Object();

Later, you will add clients to this object in the application.onConnect method. To store representative connections, create a second new ActionScript object:

/* Holds all connect reps, key: username value: Server-side client Object */ 
this.supportReps = new Object();

Now you have a way to keep track of both customers and representatives, but you will want a way to share the customer list with other applications. You could create a SharedObject that holds the supportClients object, but that object was created for a way to easily look up customers. In the future, your application may require maintaining the order in which customers connect. To build for this feature, the application will use a separate array for the shared user list:

/* The list of users saved in a SharedObject */ 
this.userList = new Array();

And of course, create the SharedObject:

/* The SharedObject that holds the list of users */ 
this.users_so = SharedObject.get( "supportUsers" );
this.users_so.setProperty( "userList", this.userList );

This SharedObject is named supportUsers and is stored in the variable users_so. The property, userList, is then set to match the userList array you just created. Notice that you do not check to see if userList already existed. The application will never start with users already in queue anyway, because it was never there to accept them.

The last item on the list is to validate the representative's connection request. To do this, create another ActionScript Object:

/* Object that is used for rep validation */ 
this.rep_lookup = new Object();

This will be used later to look up a representative by username and match that to their password. Go ahead and add a couple of users below the previous ActionScript:

/* The representatives */ 
this.rep_lookup["greg"] = "burch";
this.rep_lookup["kevin"] = "towes";
this.rep_lookup["jessica"] = "smith";

Note

In a real-world scenario, you may want a more robust and secure validation system. Using Flash Remoting MX, you could connect to a ColdFusion web service that checks the username and password rather than having a hard-coded object, as was in this example.


My username is greg and my password is burch. That is all you need in your application.onAppStart to get going! Listing 16.1 is the complete ActionScript. Comments used earlier have been removed to save space.

Listing 16.1 Full listing of SSAS onAppStart
application.onAppStart = function(){
      this.supportClients = new Object();
      this.supportReps = new Object();
      this.userList = new Array();
      this.users_so = SharedObject.get( "supportUsers" );
      this.users_so.setProperty( "userList", this.userList );
      this.rep_lookup = new Object();
      this.rep_lookup["greg"] = "burch";
      this.rep_lookup["kevin"] = "towes";
      this.rep_lookup["jessica"] = "smith";
}
Handling Connection Requests (onConnect)

Next, the application needs an application.onConnect method. This method is called whenever a user connects. This is where the server will decide if the user that is trying to connect is a customer, a representative, or an invalid user.

You will be building your own application.onConnect validation scheme. On the server-side, there are two possibilities when a user connects:

  • The user connects and passes in only one parameter: a username.

  • The user connects and passes in two parameters: a username and a password.

The first scenario would mean the user connecting is a customer. The second means a representative is trying to connect. Customers require only a username in our application because there is no reason to validate their identity. The representative, however, needs to be validated because they will be using administrative controls, such as connecting to a user to help them, opening browser windows on the client's machine, and ending Live Support Sessions.

A simple if statement determines which type the user is. First check if they sent a password. If they didn't, that means they are a customer:

application.onConnect = function( newClient, username, pass ) { 
      if( pass == null ){
            trace( "Client Connected- "+username );
}else if...

If the password is not null (meaning the user supplied a password), this ActionScript challenge calls a separate method, called isRep(), which you will build later. The purpose of isRep() is to check the username and password of a representative. The code looks like this:

else if( this.isRep( username, pass ) ){ 
      trace( "Rep Connected- "+username );
}else...

If the user supplied a username and password that did not match, you have found an invalid user:

}else{ 
      trace( "Invalid User" );
}

Now you will need to assemble each case with the appropriate ActionScript operations. In the first case, the user is a customer. That means you have to put that customer's name in both of the ActionScript objects associated with a customer. You also need to update the users_so SharedObject:

if( pass == null ){ 
      trace( "Client Connected- "+username );
      /* Store this client in the supportClientsObject */
      this.supportClients[username] = newClient;
      /* Put it in the SharedObjects userList array */
      this.userList.push(username);
      /* Update the SharedObject */
      this.users_so.setProperty( "userList", this.userList );
}else if...

When a representative's connection has been accepted, store a variable in the client object that identifies the user as a representative. Later, when the user disconnects, you will use this variable to determine which list to remove the user from.

Lastly, add the representive's name to the representatives ActionScript object:

}else if( this.isRep( username, pass ) ){ 
      trace( "Rep Connected- "+username );
      /* Store that the connected user is a rep, for later use. */
      newClient.isRep = true;
      /* Store this rep in the supportReps object */
      this.supportReps[username] = newClient;
}else...

The invalid user is handled in the last case by rejecting the connection to the Flash Communication Server. To reject connections, use the application.rejectConnection method. Rejecting a connection invokes the NetConnection's onStatus function on the Flash player, sending it the Information object, NetConnection.connect.rejected. The script looks like this:

else{ 
      trace( "Invalid User" );

      /* Incorrect password */
      application.rejectConnection( newClient );
      /* Nothing more todo, return */
      return false;
}

Now that you have identified the user, let's move on. Declare the username within the Flash Communication Server's UI component framework so the username is recognized by any UI components used in the application:

/** 
* Inform the UI Component Framework of the username
* this name will be used by all UI components.
*/
gFrameworkFC.getClientGlobals( newClient ).username = username;

Finally, but most importantly, you have to accept the connection. To do this, use the method application.acceptConnection:

/* accept the connection */ 
      this.acceptConnection( newClient );
}

Listing 16.2 shows you the complete code, with comments removed to save space.

Listing 16.2 Full listing of SSAS onConnect
application.onConnect = function( newClient, username, pass ) {
      if( pass == null ){
            trace( "Client Connected- "+username );
            this.supportClients[username] = newClient;
            this.userList.push(username);
            this.users_so.setProperty( "userList", this.userList );

      }else if( this.isRep( username, pass ) ){
            trace( "Rep Connected- "+username );
            newClient.isRep = true;
            this.supportReps[username] = newClient;

      }else{
            application.rejectConnection( newClient );
            return false;

      }
      gFrameworkFC.getClientGlobals( newClient ).username = username;
      this.acceptConnection( newClient );
}
Handling User Disconnections (onDisconnect)

Now that the application can handle connection requests, it must also handle client disconnections. There are two user disconnect cases to handle: a representative and a customer disconnect. So, first check the isRep property set in the Client object. If isRep is true, remove the username from the array holding all of the representatives by retrieving the username from the global storage:

application.onDisconnect = function( oldClient ){ 
      /* Check to see if the client that disconnected is a rep */
      if( oldClient.isRep ){

            var username = gFrameworkFC.getClientGlobals(oldClient ).username;
            /* remove it from the supportReps object */
            delete application.supportReps[username];

}else...

A separate method will be used to clean up customers because there are a few more data bindings. A new function, removeUser, will be called receiving one parameter, the Client object:

}els1e{ 
/**
* Call the removeUser method passing it the client object. We create this
* method to clean up several things that were stored about the Customer.
*/

      this.removeUser(oldClient);

  }

}

Next, create two helper methods that you have already referenced, isRep and removeUser.

The isRep function is very simple. It is used to validate representatives. It will validate the parameter's username and password against the rep_lookup object created in the application.onAppStart method. Here's how it works:

application.isRep = function( username, pass ){ 

      /* If they don't have a password, they can't be a rep */
if( pass==null || pass=="" ) return false;

Now, just return the comparison between the value in the username property of rep_lookup and the given password:

/* Return the comparison of the password */ 
      return this.rep_lookup[username]==pass;
}

The method removeUser is called from the onDisconnect event and is used to clear a user (customer or representative) from the application. A customer's username is stored within the userList array as well as the supportClients object. To remove it from the object, you can simply reference it by the username. For the list, however, it's a bit trickier. You must first loop through the list until you find a match for the username, and then use the Array.splice() method with the array to delete the item:

application.removeUser=function( oldClient ){ 
      var username = gFrameworkFC.getClientGlobals( oldClient ).username;

      /* Remove the user from the supportClients object */
      delete this.supportClients[username];

      /* Find the user in the userList array and remove it */
      for( var i=0; i<this.userList.length; i++ ){
            if( this.userList[i] == username ){
                  this.userList.splice( i ,1 );
                  this.users_so.setProperty( "userList", this.userList );
                  return true;
            }
      }
}

Listing 16.3 shows a comment-free listing of all the onDisconnect methods and the custom functions that are used to clean up after a user disconnects.

Listing 16.3 Full listing of SSAS onAppStart
application.onDisconnect = function(oldClient){
      if(oldClient.isRep){
            var username = gFrameworkFC.getClientGlobals( oldClient ).username;
            delete this.supportReps[username];

      }else{
            this.removeUser( oldClient );
      }
}

application.isRep = function( username, pass ){

      /* If they don't have a password, they can't be a rep */
      if( pass==null || pass=="" ) return false;

      /* Return the comparison of the password */
      return this.rep_lookup[username]==pass;
}
application.removeUser=function( oldClient ){

      var username = gFrameworkFC.getClientGlobals( oldClient ).username;

      /* Remove the user from the supportClients object */
      delete this.supportClients[username];

      /* Find the user in the userList array and remove it */
      for( var i=0; i<this.userList.length; i++ ){
            if( this.userList[i] == username ){
                  this.userList.splice( i ,1 );
                  this.users_so.setProperty( "userList", this.userList );
                  return true;
            }
      }
}
Create a Unique Application Instance

The custom method connectionToClient is called from the Flash player to open a Live Support Session (application instance). This method is placed in the prototype of the client so it is available to each Flash player connected to the application. The connectToClient method requires two string parameters: rep and client:

Client.prototype.connectToClient = function( rep, client ){... 

This method is responsible for creating application instances used to establish a secure communication channel between the representative and the customer. It informs the Flash player of both the customer and representative what application instance to connect to. The variable, instanceId, is incremented each time an instance is created. The variable is incremented to make sure that each Live Support Session is unique.

To initialize an instanceId variable, add the following lines to the end of the application.onAppStart method you scripted earlier:

/** 
* This is incremented for each support session and used
* to generate instances of the applications.
*/
this.instanceId = 0;

This function performs three specific tasks:

  • Increments the instanceId

  • Creates a new URI for the users to connect to

  • Calls a method on the Flash player (client-side) that will connect the NetConnection object to the new LiveSupport instance

Listing 16.4 shows the code that accomplishes these tasks.

Note

The LiveSupport application will be created in the next section.


Listing 16.4 Full listing of connectionToClient
Client.prototype.connectToClient = function( rep, client ){

      /**
      * We are going to launch a new instance of the LiveSupport application
      * so we need to increment the instanceId variable
      */
      application.instanceId++;

      /* The URI that we are going to pass to the client */
      var rStr = "rtmp:/LiveSupport/"+application.instanceId;

      /* Tell both the rep and the client to connect to the new URI */
      application.supportReps[rep].call("startSupport", null, rStr);
      application.supportClients[client].call("startSupport", null, rStr);

}

Note

I refer to application.instanceId and not this.instanceId because this ActionScript exists within the Client object, not the Application object where instanceId exists.


The last two lines call the startSupport method for both users. The startSupport methods are defined on the client-side ActionScript and will handle the new URIs they have been assigned. That is it for the server-side code for the lobby. The lobby is the most complicated part of the entire support application because it uses a lot of custom ActionScript. The entire LiveSupport application is made up of the Flash Communication components so there is not much additional ActionScript required.

The Client-Side ActionScript

The client-side (Flash) ActionScript will handle both the Lobby and the Support Tool scenarios in one application. A customer will use a different Flash movie than a representative. You will begin by building the ActionScript needed for the user to connect and wait in the lobby. Then you will construct the user interfaces for the customer user and the representative.

Exercise 16.1: Building the Client User Interface

  1. To start building the client interface, create a new Flash movie called Client_Support_App.fla.

  2. Open that movie in Flash and create three layers. Name the top one Actions, the middle one Connection Light, and the last one Client Screens.

    The client Flash movie is about as simple as it gets. All the client must provide to enter the system is a username. Then, the client sits patiently and waits for a representative to transfer to them. With that in mind, there are only a few things that you need to build:

    • An input text field for clients to enter a username

    • A PushButton component (from the Flash UI components) that the user can press to log in

    • A dynamic text field for displaying status messages, such as "You need to log in" or "Please be patient; a representative will be with you shortly"

    • The ConnectionLight component so the client can see his connection status

    Use Figure 16.1 as a guide to layout your components.

    Figure 16.1. The first screen for the client application.

    graphics/16fig01.gif

  3. Select the Client Screens layer. Give the field an instance name of loginField and make sure it's set to an input field.

  4. Drag a PushButton from the Flash UI components onto the Client Screens layer next to the login field.

  5. Select the PushButton on the stage and give it an instance name of login_btn.

  6. Click on label in the Component Parameters of the PushButton and type in Login. You should see this update live on the screen in your button. Next, click on Click Handler and type in onLogin. That's it for the PushButton.

  7. Create another text field on the Client Screens layer and size it all enough to fit about three lines of text. Give it an instance name of statusField. Make sure that it is set to Dynamic Text. In the Properties panel, set it to multiline.

  8. Type the text Welcome to the Live Support Application. Please enter a username below to be placed in line. in the statusField text field.

  9. On the Connection Light layer place the ConnectionLight component and give it an instance name of connectionLight_mc.

    That's it for the user interface! Now you need to write the ActionScript that makes all of these work.

  10. Open the Actions panel (F9) and add the following code:

    /* Setup NetConnection */ 
    nc = new NetConnection();
    
  11. Instantiate the NetConnection object to the nc variable.

  12. Create the Click Handler onLogin, which is used to connect to the LiveSupportLobby application. The onLogin function will receive the username from the loginField and send it as an argument to LiveSupportLobby. Use the following code:

    onLogin = function(){ 
          username = loginField.text;
    nc.connect("rtmp:/LiveSupportLobby",username);
    
  13. Connect the ConnectionLight UI component connectionLight_mc:

    connectionLight_mc.connect(nc); 
    
  14. Update the statusField text box to inform that the user has entered the application:

    statusField.text="A Representitive Will be with you shortly. Thank you for your patience.
    graphics/ccc.gif" 
    

    When the user's connection is accepted, the login field and Login button will be deactivated because the CUSTOMER is waiting in the queue for a representative.

  15. Set the selectable property of the statusField to false, changing some colors to make the status change apparent to the user:

    loginField.selectable = false; 
    loginField.background = true;
    loginField.backgroundColor = 0xCCCCCC;
    loginField.textColor = 0x999999;
    
  16. Call the pushButton.setEnabled method for the Login button and pass it a value of false. Close off the bracket for the onLogin function:

          login_btn.setEnabled(false); 
    }
    
  17. Add a stop() action so the movie does not continue playing.

    /* Make sure to stop the playhead! */ 
    stop();
    

Listing 16.5 shows the complete code with limited comments. Use the code in Listing 16.5 to confirm your ActionScrtipt.

Listing 16.5 Full Flash ActionScript for Frame 1, ActionScript layer
nc = new NetConnection();
onLogin = function(){
      username = loginField.text;
      nc.connect("rtmp:/LiveSupportLobby",username);
      connectionLight_mc.connect(nc);
      statusField.text="A Representitive Will be with you shortly. Thank you for your 
graphics/ccc.gifpatience."
      loginField.selectable = false;
      loginField.background = true;
      loginField.backgroundColor = 0xCCCCCC;
      loginField.textColor = 0x999999;



      login_btn.setEnabled(false);

}
stop();

That does it for the lobby part of the application. Figure 16.2 shows the completed application. In the section "Live Support Session," you will be building upon this movie to handle live support sessions, as well. Next, you will create the Flash movie used by the representative user.

Figure 16.2. The user has logged in and is waiting for a representative.

graphics/16fig02.gif

The Representative User Interface

The representative user interface consists of two interface screens: one for the representative to log in and one to select the user to provide support.

Start by first creating a new Flash movie called Representative_Support_ App.fla. Open the movie in Flash MX, and then create three new layers as follows:

  • Actions

  • Connection Light

  • Representative Screens

Now you're ready to start by building the Login interface.

Exercise 16.2: Representative Login Interface

Follow these steps to create the Representative Login interface:

  1. Create two text fields on the Representative Screens layer. One will be used for a username and the other for a password. Give the username field the instance name of loginField and set it to single line input field. Give the password field an instance name of passwordField and set it to password where it says Single Line.

  2. Drag a PushButton component onto the same layer next to the two fields. Give this an instance name of login_btn. For the Label parameter of the component, type in Login, and for the Click Handler, type in onLogin.

  3. Drag a ConnectionLight component onto the Connection Light layer. Give it an instance name of connectionLight_mc.

    That completes the user interface setup of the representative login screen. Use Figure 16.3 as your guide to lay out the screen. Next, you will set up the Click Handler that connects the representative.

    Figure 16.3. The first screen that the representative sees.

    graphics/16fig03.gif

    Now that your interface is built, let's continue this exercise by adding the ActionScript required. Place the following ActionScript in Frame 1 on the Actions layer.

  4. Create your NetConnection object nc:

    /* Setup NetConnection */ 
    nc = new NetConnection();
    
  5. Create the function onLogin to be the Click Handler of the Login button. This method will return the text property from loginField for the username and the text property from passwordField for the password. It will connect the NetConnection object passing the connection string, rtmp:/LiveSupportLobby, and the argument's username and password. The code you need is:

    onLogin = function(){ 
          username = this.loginField.text;
          password = this.passwordField.text;
    nc.connect("rtmp:/LiveSupportLobby",this.username,this.password);
    
  6. Connect the ConnectionLight component, connectionLight_mc:

    connectionLight_mc.connect(nc); 
    
  7. Remember that SharedObject that holds a list of clients, users_so? You will use that now to keep the representative up-to-date on what clients need help. Get the reference to the SharedObject:

    supportUsers = SharedObject.getRemote("supportUsers",nc.uri); 
    
  8. Define an onSync method for updating the list when it has changed:

    supportUsers.onSync = function(args){ 
          /* Remove all items from the list box */
    user_lsb.removeAll();
    

    Always remember to place the onSync method before you connect; this technique will ensure that no onSync messages are missed.

  9. Loop through the items in the user_list property and add them to a Flash UI components ListBox. The ListBox will have the instance name user_lsb, and you will be adding it in the next exercise.

    /* add each item to the list box */ 
          for(var i in this.data["userList"]){
                user_lsb.addItem(this.data["userList"][i]);
          }
    }
    
  10. Connect the SharedObject:

    supportUsers.connect(nc); 
    
  11. Add a gotoAndStop() action to move the play head to the next screen (on Frame 2):

    gotoAndStop(2); 
    

    The screen will have a ListBox that will display a list of the customers and a PushButton to transfer the representative and the customer to a Live Support Session.

Exercise 16.3: Representative Customer Selection Interface

Now you are ready to create the user list screen.

  1. Insert an empty keyframe on Frame 2 of the Representative Screens layer by right-clicking on the second frame of the layer and selecting it from the list.

  2. On the ConnectionLight and Actions layers, right-click on Frame 2 and select Insert Frame.

  3. Select the second frame you just created on the Representative Screens layer and place a ListBox, and give it an instance name of user_lsb (see Figure 16.4). That's all you have to do for the ListBox.

    Figure 16.4. The screen where a representative chooses a client to help.

    graphics/16fig04.gif

  4. Drag a PushButton component on to the second frame of the Representative Screens layer. Give it an instance name of c2c_btn. This button will have a long label, so make its width 175 pixels.

  5. Type the phrase Connect to Selected Client in the label parameter of the PushButton. As its Click Handler, enter onConnectClient.

  6. Add the function onConnectClient to call the connectToClient server-side method you created.

  7. Return to Frame 1 of the Actions layer, and add the following ActionScript:

    onConnectClient = function(){ 
          /* Get the username of the selected user */
          var user = this.user_lsb.getSelectedItem().label;
    
          /* Make sure a user is in fact selected */
          if(user!=null){
                /**
                * Call the server-side method passing it the
                * Representatives username and the selected
                * Client's username
                */
                this.nc.call("connectToClient",null,this.username,user);
          }
    }
    

    Note

    The first parameter of the NetConnection call method is the method name and the second is the callback. The rest of the parameters can be what you want. Get used to passing in null as the second parameter if you do not plan on using callbacks.

  8. Add a stop() action as the last ActionScript item on the frame:

    /* Make sure to stop! */ 
    stop();
    

The lobby part of the representative Flash movie is done. In the next section, you will create the Live Support Session that will build on this movie. Listing 16.6 shows a complete and comment-free version of the code for this exercise.

Listing 16.6 Flash ActionScript listing for representative interface
nc = new NetConnection();

onLogin = function(){
      username = this.loginField.text;
      password = this.passwordField.text;
      nc.connect("rtmp:/LiveSupportLobby",this.username,this.password);
      connectionLight_mc.connect(nc);
      supportUsers = SharedObject.getRemote("supportUsers",nc.uri);
      supportUsers.onSync = function(args){
            user_lsb.removeAll();
            for(var i in this.data["userList"]){
                  user_lsb.addItem(this.data["userList"][i]);
            }
      }

      supportUsers.connect(nc);
      gotoAndStop(2);
}
onConnectClient = function(){
      var user = this.user_lsb.getSelectedItem().label;
      if(user!=null){
            this.nc.call("connectToClient",null,this.username,user);

      }
}

stop();


    Part I: 10 Quick Steps for Getting Started
     
    ASPTreeView.com
     
    Evaluation has ВФВЕФУМГёНёexpired.
    Info...