Creating a Web Messenger control from Scratch – Part 11

In this the final installment of this series of tutorials for the moment we will be completing the Web Messenger control by adding the ability for users to actually hold conversations. 

messengerconversation_thumb_24a40832 Developer

We will also add some clean-up code, and generally tidy up some things that were left.

So first off lets re-visit our signInCompleted routine. As you remember, when a user signs in, two events will get fired. The first being authentication completed then once the user has authenticated correctly, sign in completed will get called. We have already wired this event handler up but there are a couple of additions to make to it.  Firstly we need to add a delegate for whenever a conversation property takes place. This happens for example if you’ve signed in and another user starts a conversation with you :-

//Add delegate for when conversation changes
msgruser.get_conversations().add_propertyChanged(Delegate.create(null, conversation_collectionChanged));

 

If you remember way back to the beginning of this series, the user class has a property :-

messengerconv_thumb_06c58431 Developer

This property retries the Conversation Collection for the user. The Conversation collection class has a number of events that you can tie into :-

messengerconversationevents_thumb_41acb6fa Developer

  • CollectionChanged – Occurs when the collection changes.
  • PropertyChanged – Occurs when a property changes.

The one we’re interested in is the PropertyChanged event. In the code snippet above, we simply get the Conversation Collection for the signed in user, then hook into the PropertyChanged event and point it to a function conversation_collectionChanged.

The other change we’re making to the signInCompleted event handler is to create a TimeOut to call a function a little later. We are doing this due to the way that we initially create and display Contacts :-

//Now we need to set a single timout so that we have time for the users
//contacts to be enumerated through and the ContactsList array to be
//created. Then we can assign the contact presence changed delegate
//We'll set the timeout to 10 seconds.
contactPresenceTimeout = setTimeout(ContactPresenceDelegate, 10000);

 

 

 

 

The function that we call simply iterates through the our array of contact objects and sets an event handler up to call a function whenever a contact’s online presence changes :-

function ContactPresenceDelegate()
{
    if (ContactsList != null)
    {
        //Only clear the timeout if the contacts list has been built.
        clearTimeout(contactPresenceTimeout);
        var contactcount = ContactsList.getLength();
        for (var counter = 0; counter < contactcount; counter++)
        {
            var address = ContactsList[counter].get_Address();
            //This will fire whenever a contacts online presence changes.
            address.get_presence().add_propertyChanged(Delegate.create(null, presence_PropertyChanged));
        }
        //We also want to close the groups here
        ToggleContacts();
    }
}

 

 

First off we clear the Timeout so that it doesn’t fire again and at the end of the routine we simply collapse the Contacts list displayed on screen so that it look a bit tidier.

/* Called by the Messenger Library when presence status changes occur (i.e. contacts sign in or out). */
function presence_PropertyChanged(sender, e)
{
    displayContacts();
}

 

 

The presence_PropertyChanged function that we hook into simply calls the displayContacts routines which will refresh all the contacts and their statuses.

 

function conversation_collectionChanged(sender, e)
{
    displayConversations();
}

 

 

The conversation_collectionChanged routine simply calls displayConversations() function as shown above.

 
function displayConversations()
{
    ConversationArray = new Array();
    var sb = new StringBuilder('<p><b>Active Conversations: (click a conversation to resume):</b></p>');
    var item = 0;
    var enum1 = msgruser.get_conversations().getEnumerator();
    while (enum1.moveNext())
    {
        var conv = enum1.get_current();
        ConversationArray.push(conv + ":" + item);
        if (conv.get_closed())
            continue;
        sb.append(conversationLink(conv, item));
        sb.append("<hr />");
        item++;
    }
    $get('divConversations').innerHTML = sb.toString();
}

 

 

The displayConversations simply adds a link to our open conversations box, one for each conversation that is currently ongoing as you can see below :-

messengerdisplayconversations_thumb_43e93fb6 Developer

function conversationLink(conv, item)
{
    var roster = conv.get_roster();
    var enum1 = roster.getEnumerator();
    var names = new Array();
 
    while (enum1.moveNext())
    {
        var dispName = enum1.get_current().get_presence().get_displayName();
        var dispEmail = enum1.get_current().get_address();
        if (dispName !== '')
        {
            names.push(dispName);
        } else
        {
            names.push(dispEmail);
        }
    }
 
    var sb = new StringBuilder();
    sb.append('<a href="javascript:switchConv(' + item + ')">');
    if (conv == Conversations)
        sb.append('<b>');
    sb.append(names.join(', '));
    if (conv == Conversations)
        sb.append('</b>');
    sb.append('</a>');
    sb.append('&nbsp;&nbsp;');
    sb.append('<a href="javascript:closeConversation(' + item + ')">');
    sb.append('close</a>');
    return sb.toString();
}

 

 

The displayConversations in turn calls the conversationLink function above to actually create the link. All we are doing here is creating a link so that the user can switch to that conversation and also creating a close link that will end that particular conversation.

/* Close the current conversation. */
function closeConversation(id)
{
    var conv = msgruser.get_conversations().get_item(id);
    convArray.splice(id, 1);
    conv.close();
    if (conv == Conversations)
    {
        removeChildrenFromNode('txtConv');
    }
    displayConversations();
    if (msgruser.get_conversations().get_count() == 0)
        document.getElementById('btnSend').disabled = true;
}

 

 

The closeConversation function takes in the ID of the conversation to close (which is created whenever a conversation starts). We get the actual conversation through the Item(id) method of the Conversation Collection then close that item. If the conversation we are closing is the currently displayed conversation then we clear the conversation from the display area and disable the button to send a conversation item.

We’re nearly there, only another few routines to go through. Whenever a user clicks on a Contact, the createConv function is called :-

function createConv(index)
{
    var newConv = msgruser.get_conversations().create(ContactsList[index].get_Address());
    var convId = msgruser.get_conversations().get_count();
    switchConv(newConv.convId);
}

 

This routine simply creates a conversation object based on the users current address and an id. The id is simply the number of conversations that are currently open, plus 1.  Since the Conversation Collection is zero based, when you get a count of current open conversations, it will always be the index + 1.

function switchConv(id)
{
    var conversation = msgruser.get_conversations().get_item(id);
    if (conversation)
    {
        if (Conversations)
        {
            Conversations.remove_messageReceived(ConversationSink);
        }
        ConversationSink = Delegate.create(null, receiveMessage);
        Conversations = conversation;
        Conversations.add_messageReceived(ConversationSink);
        removeChildrenFromNode('txtConv');
        /* Display all messages from the conversation history. */
        var hist = conversation.get_history();
        var histEnum = hist.getEnumerator();
        while (histEnum.moveNext())
        {
            displayMsg(histEnum.get_current());
        }
        document.getElementById('btnSend').disabled = false;
    }
    displayConversations();
    document.getElementById('txtMessage').focus();
}

 

 

We then call the switchConv function passing in the conversation id as described above. This routine gets the Conversation object from the Conversation Collection, creates a delegate for if a message is received on that particular conversation object and displays all the messages that have been sent and received on this particular conversation thread. We also enable the Send button to allow the user to send a message on this Conversation thread.

/* Called by the Messenger Library when a new message is received. */
function receiveMessage(sender, e)
{
    var message = e.get_message();
    displayMessage(message);
    document.getElementById('msgLastRecv').innerText = 'Last message at: ' + Conversations.get_history().get_lastReceived().toString();
}

 

 

When we receive a message we simply display it.

/* Send a message in the current conversation. */
function sendMessage()
{
    var txtMessage = $get('txtMessage');
    var messageText = txtMessage.value;
    var message = new Microsoft.Live.Messenger.TextMessage(messageText, null);
    if (msgruser)
    {
        Conversations.sendMessage(message, null);
    }
    displayMessage(message);
    txtMessage.value = '';
    txtMessage.focus();
}

 

 

Whenever the user clicks on the “Send” button to send a line of text in the conversation, the above routine gets called.

This simply grabs the line of the text from the input textbox on the page, adds this line of text to the current conversation object and sends the line of text to the recipient. Then we clear the line of the text from the input box ready for the next message to be sent.

That’s all there is to sending and receiving text messages to a users’ contacts. The reason for the brief description above is that I’ve actually covered this in more depth in a previous article that I have written which you can find on this site.

The last thing we want to do is complete our signOutCompleted handler :-

function signOutCompleted(sender, e)
{
    //Fill in later
    if (signincontroldisplayed == false)
    {
        ToggleSignIn()
    }
    Conversations = null;
    ContactsList = null;
    userInfo = null;
    //Blank the user interface.
    removeChildrenFromNode("divConversations");
    removeChildrenFromNode("divContacts");
    removeChildrenFromNode("searchResults");
    $get('divConversations').innerHTML = "";
    $get('txtConv').innerHTML = "";
    $get('btnSend').disabled = true;
}

 

Here we are just tidying things up. We show the sign-in Control and remove all conversations, user contact lists etc. from memory and also from the screen display.

There you have it. A fully functional web messenger control. I will be entire code for the above control soon and will post where I have uploaded it to.

There are numerous improvements we can make to this control, e.g. each conversation actually takes place in a popup window rather than having to switch conversation in a single control etc. and these are things that I will continue to do with this control but the main reason for not going into all that here is that it’s mainly cosmetic changes and I want to start getting into Live FX programming.  I will be revisiting this code in the future however.

If you’ve followed each article in this series then you should have the complete code already. The only thing that you will need to change is the following :-

var privacyUrl = 'http://localhost:44444/WLWebMessenger/WebMessenger/Privacy.htm';
var channelUrl = 'http://localhost:44444/WLWebMessenger/WebMessenger/Channel.htm';

 

 

This is found in the startMessenger function of the MessengerFns.js file. This will need to change to point to your own website url rather than localhost.