Learn Skype Web SDK Day 17 : Sending & Receiving “Is Typing” Notifications
[contemplate-1]
View Demo
One of the surprisingly useful features of Skype for Business that I found when I started using it is the “is typing” messages. When the person you’re in a conversation with is typing a message, you get notification of that in the client. It’s a great way to know that the person is constructing their answer, rather than ignoring you.
If you’ve been using the Skype for Web API and created something which can send and receive messages, you’ll notice the lack of this feature even more. Wouldn’t it be nice if there was an easy way to include this into your Skype for Web API projects? Well there is!
The isTyping
feature consists of two parts: you sending notification that you’re typing, and reacting to receiving messages that the remote party is typing. You don’t need to worry about “not typing anymore” messages: when you send a isTyping
message it lasts 5 seconds, the server will automatically send a “stopped typing” message unless you continue to send isTyping messages.
Stage 1 – sending isTyping
messages. You need some trigger to start sending the messages. In JQuery, a good starting place would be the keyup() on a text input box. Assuming you already have a conversation set up (if not see earlier posts), then sending the isTyping
notification is as simple as:
$('#someinputbox').keyup(function (){ conversation.chatService.sendIsTyping(); })
That’s it! By binding the call to the keyup function you’ll ensure that the message is sent each time a character is pressed, which should be enough to keep the messages realistic.
Stage 2 – receiving isTyping
messages. This is a little more complicated, but only because the messages are hidden in a hard-to-find place. The reason for this is that although in our examples we’re only going to be looking at a conversation between two people, it’s possible to have group conversations and conferences, and the API needs to be able to cope with all scenarios. Therefore, it doesn’t make any sense to have a isTyping
member on the conversation itself, it needs to be on every participant in the conversation.
In a previous post, we saw how to set up a new conversation. Part of that process involved creating a Conversation Participant, which we then added to the conversation. It’s this participant object which contains our isTyping
message. Luckily, there’s a changed()
event here as well, making it fairly easy to react to changes in typing messages. Assuming you’ve already created a participant:
convParticipant.chat.isTyping.changed(function (newState) { //do something with newState });
newState
is a boolean signifiying whether or not the participant is typing. In designing a user interface, a common approach is to show a message (such as ‘Bob is typing…’) if the value is true, and to simply hide the message if it’s false.
Here’s a full example showing this in action
<div class="form-horizontal"> <div class="form-group"> <label for="username" class="col-sm-2 control-label">Username</label> <div class="col-sm-10"> <input type="email" class="form-control" id="username" placeholder="Email"> </div> </div> <div class="form-group"> <label for="password" class="col-sm-2 control-label">Password</label> <div class="col-sm-10"> <input type="password" class="form-control" id="password" placeholder="Password"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button class="btn btn-default" id="btnLogIn">Log in</button> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button class="btn btn-default" id="btnLogOut">Log out</button> </div> </div> </div> <div> <span id="loginStatus"></span> </div> <hr/> <div class="form-horizontal"> <div class="form-group"> <label for="contact" class="col-sm-2 control-label">Start Conversation with:</label> <div class="col-sm-10"> <input type="email" class="form-control" id="contact" placeholder="Contact SIP Address"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button class="btn btn-default" id="btnStartConversation" disabled="disabled">Start</button> </div> </div> </div> <div> Conversation State: <span id="lblConversationState"></span> <ul id='conversationText'></ul> <span id='lblTypingStatus'></span> <hr/> <div class="form-horizontal"> <div class="form-group"> <label for="message" class="col-sm-2 control-label">Send Message:</label> <div class="col-sm-10"> <input type="email" class="form-control" id="message" placeholder="Message to send"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button class="btn btn-default" id="btnSendIM" disabled="disabled">Send IM</button> </div> </div> </div> <div id="footer"></div> <!-- This is not needed for the samples to run, but adds standard headers and footers to the samples, to display title, instructions, about etc. If you're taking this code and using it yourself, you can remove this.--> <script type="text/javascript" src="../../assets/layoutcodesample-min.js"></script> <script type="text/javascript"> <!-- These variables are only needed for laying out the code sample, they are not part of the sample code. --> var pageTitle = 'Sending &amp; Receiving isTyping Messages'; var blogPostLocation = "http://thoughtstuff.co.uk"; var githubLocation = "http://github.com"; var client; $(function () { 'use strict'; Skype.initialize({ apiKey: 'SWX-BUILD-SDK', }, function (api) { client = new api.application(); // whenever client.state changes, display its value client.signInManager.state.changed(function (state) { $('#loginStatus').text("Login State: " + state); }); }, function (err) { alert('Error loading Skype Web SDK: ' + err); }); $('#btnLogIn').click(function () { // start signing in client.signInManager.signIn({ username: $('#username').val(), password: $('#password').val() }).then(function () { //log in worked! alert('Logged in!'); $('#btnStartConversation').prop('disabled', false); }, function (error) { //Something went wrong. alert(error); }); }); $('#btnLogOut').click(function () { // start signing out client.signInManager.signOut() .then(function () { //log out worked! alert('Logged out!'); $('#btnStartConversation').prop('disabled', true); $('#btnSendIM').prop('disabled', true); }, function (error) { //Something went wrong. alert(error); }); }); $('#btnStartConversation').click(function () { StartConversation($('#contact').val()); }); $('#btnSendIM').click(function () { conversation.chatService.sendMessage($('#message').val()); }); function StartConversation (contactSIP) { //first, get the person to start a conversation with. Assume one person. var person; GetContactFromName(contactSIP).then(function (results) { results.forEach(function (result) { person = result.result; }); //create the conversation object conversation = client.conversationsManager.createConversation(); //add the person to the conversation by creating a conversation participant object convParticipant = conversation.createParticipant(person); conversation.participants.add(convParticipant); //add the newly created conversation to the ConversationManager list client.conversationsManager.conversations.add(conversation); //register for the conversation state changing to connected. conversation.chatService.state.changed(function(newState){ $('#lblConversationState').text(newState); if (newState == 'Connected') { $('#btnSendIM').prop('disabled', false); //safe to send IMs now } }); //register for new messages added to the historyService object conversation.historyService.activityItems.added(function (newMsg){ if (newMsg.type() == 'TextMessage') { var direction; if (newMsg.direction() == 'Incoming') direction = "<--"; else { direction = "-->"; } $("#conversationText").append('<li><b>' + direction + '</b>&amp;nbsp; ' + newMsg.sender.person.displayName() + ' : ' + newMsg.text() + '</li>'); } else { alert(newMsg.type()); } }); //register for remote conversation participant typing convParticipant.chat.isTyping.changed(function (newState) { if (newState == true) { $('#lblTypingStatus').text(convParticipant.uri() + ' is typing'); } else { $('#lblTypingStatus').text(''); } }); conversation.chatService.start(); }); //send local participant typing messages $('#message').keyup(function (){ conversation.chatService.sendIsTyping(); }) } function GetContactFromName(contactSIP) { var query = client.personsAndGroupsManager.createPersonSearchQuery(); query.text(contactSIP); query.limit(1); return query.getMore(); } }); </script>
Demo Online
You can try this code out against your own Skype for Business environment by going to the demo page. From here you can also download the code from GitHub if you want to host it locally, or take it and use it in your next project.
[contemplate-2]