I'm playing around with Coldfusion 10's websockets and made a simple chat for testing. I've seen several chats where they have the "user is typing..." text that shows up when the other user is typing. Does anyone know how to implement this efficiently?
2 Answers
Create one more channel 'notification' and publish into it 'User is typing' when user does a 'keyDown' and 'emptystring' when 'keyUp'.
Subscribe to this channel along with you chat. the target of this channel on the receiver side shall be a whose inner html can be populated with 'User is Typing' message.
PseudoCode:
<cfwebsocket name="notificationSocket"
onmessage="notifyHandler"
subscribeto="notificationChannel" >
<cfwebsocket name="ChatSocket"
onmessage="chatHandler"
subscribeto="chatChannel" >
<!-- Conversation displayer -->
<div id="messageBoard"></div>
<!-- your text message input area -->
<textarea onKeyDown="sayTyping()" onKeyUp="sayStopped()" id="chatInput"></textarea>
<div id="notifyArea"></div>
<input name="submit" onClick="publishMessage()"></textarea>
<script>
/*chat Functions*/
var publishMessage = function(){
var msg = document.getElementById('chatInput').value;
mycfwebsocketobject.publish("chatChannel", msg );
};
var chatHandler = function(msgObj){
document.getElementById('messageBoard').innerHTML += ColdFusion.JSON.encode(msgObj);
};
/*notifying Functions*/
var notifyHandler = function(noteObj){
document.getElementById('notifyArea').innerHTML = ColdFusion.JSON.encode(noteObj);
};
var sayTyping = function(){
mycfwebsocketobject.publish("notificationchannel","User is Typing..." );
};
var sayStopped = function(){
mycfwebsocketobject.publish("notificationchannel","" );
};
</script>
Another enhancement will be to have a div already with the text 'user is typing' and your channel broadcasts the text as 'show' and 'noshow'. This is basically the class name given to the to show and hide it. Less traffic.
Approach 2: Using the same channel
<cfwebsocket name="ChatSocket"
onmessage="chatHandler"
subscribeto="chatChannel" >
<!-- Conversation displayer -->
<div id="messageBoard"></div>
<!-- your text message input area -->
<textarea onKeyDown="sayTyping()" onKeyUp="sayStopped()" id="chatInput"></textarea>
<div id="notifyArea" class="no">User is typing...</div>
<input name="submit" onClick="publishMessage()"></textarea>
<script>
/*chat Functions*/
var publishMessage = function(){
var msg = document.getElementById('chatInput').value;
mycfwebsocketobject.publish("chatChannel", msg );
};
var chatHandler = function(msgObj){
var msg = ColdFusion.JSON.encode(msgObj);
if (msg == '@userTyping@-yes'){
notifyHandler('yes');
}
else if (msg == '@userTyping@-no'){
notifyHandler('no');
}
else {
document.getElementById('messageBoard').innerHTML += msg;
}
};
/*notifying Functions*/
var notifyHandler = function(action){
document.getElementById('notifyArea').className = action;
/*call the notify handler with off to stop showing the user is typing message
after a certain interval of time. This is to avoid someone believing that
partner is still typing even when the connection is lost*/
if(action == 'on'){
setTimeout(function(){notifyHandler('no')},250);
}
};
var sayTyping = function(){
mycfwebsocketobject.publish("chatChannel","@userTyping@-yes" );
};
var sayStopped = function(){
mycfwebsocketobject.publish("chatChannel","@userTyping@-no" );
};
</script>
<style>
.yes { display:block;}
.no { display:none;}
</style>
One can always trick this code by typing the message as '@userTyping@-yes' or '@userTyping@-no'. But as i said, this is just a POC. Also, for the timeout you have mentioned, the keyUp would take care of it anyway. But you can also call the notifyHandler() by a setTimeout() as shown above.

- 1,838
- 1
- 16
- 28
-
Thanks for the example. The only problem is that I'm using the websocket for a Live Help Chat so the conversations will be one on one and not broadcasted to everyone in the channel. Would I need to create a unique subchannel for the notificationSocket for each chat initiated? Also, should there be a timeout so it doesn't keep updating every key that's pressed? – Guest May 30 '13 at 15:54
-
In that case you can use the same channel and adapt your logic to differentiate if the incoming message is a 'notification' or a chat message. – Sanjeev May 31 '13 at 02:28
-
So the best way of having a one on one chat, like a live help chat is by having one channel and filtering by id's? – Guest May 31 '13 at 23:10
-
You can do that or the best way is to use the users session id's for filtering. read this fantastic article from BenNadel blog - http://www.bennadel.com/blog/2351-ColdFusion-10-Using-WebSockets-To-Push-A-Message-To-A-Target-User.htm .It is more like a step-by-step guide. – Sanjeev Jun 01 '13 at 05:15
Inside the Comments is a question on how to filter for one on one messaging. Here is some sample code for achieving this.
Instead of using the subscribeTo
attribute, use the js function to subscribe the user and pass in some header values. These headers can then be used as filters on the publish call using selector
Example:
<cfwebsocket name="ChatSocket" onOpen="openHandler" onMessage="msgHandler" onError="errHandler">
<script>
function openHandler(){
//Subscribe to the channel, pass in headers for filtering later
ChatSocket.subscribe('chatChannel',{name: '#Session.Auth.FirstName#', UserID: '#Session.Auth.UserID#', AccountID: '#Session.Auth.AccountID#' });
}
function publish(txt, userID){
var msg = {
AccountID: "#Session.Auth.AccountID#",
publisher: '#Session.Auth.UserID#',
id: userID,
message: converthtml(txt)
};
//When including headers, the "selector" is where you will filter who it goes to.
var headers = {
AccountID: "#Session.Auth.AccountID#",
publisher: '#Session.Auth.UserID#',
id: userID,
selector: "UserID eq '"+userID+"' and AccountID eq '#Session.Auth.AccountID#'"
};
ChatSocket.publish('chatChannel',msg, headers);
}
function msgHandler(message){
console.log(message);
}
function errHandler(err){
console.log(err);
}
</script>

- 11
- 2