Skip to content
This repository has been archived by the owner on Dec 1, 2017. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
Socket & UI following the bot revamp.
  • Loading branch information
simonwex committed Jun 13, 2012
1 parent 4857dc6 commit b1cee7b
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 68 deletions.
62 changes: 44 additions & 18 deletions app/http/socket.js
Expand Up @@ -34,6 +34,18 @@ function sendMessageToUser(userId, message){
}
}

function broadcast(message){
if (typeof(message) != 'string'){
message = JSON.stringify(message);
}
for (var userId in connections){
for (var i in connections[userId]){
var conn = connections[userId][i];
conn.sendUTF(message);
}
}
}

/*
* Story Stuff: This should really get rolled up into a model.
*/
Expand All @@ -48,12 +60,11 @@ function sendStoryToUser(userId, story){

function subscribeForStories(){
pubsubRedis.on("ready", function(){
pubsubRedis.subscribe(["feeds.storyForUser", "user.signout", 'contacts.update']);
pubsubRedis.subscribe(["feeds.storyForUser", "user.signout", 'contacts.userStatusUpdate', 'contacts.userOffline', 'contacts.reset']);

pubsubRedis.on("message", function(channel, message){
switch(channel){
case "feeds.storyForUser":
console.log(message);
var data = JSON.parse(message);
sendStoryToUser(data.userId, data.story);

Expand All @@ -74,12 +85,14 @@ function subscribeForStories(){
delete connections[userId];
}
break;
case "contacts.update":
// TODO: This is a nasty performance issue. We're updating all users' contact lists, this needs to be done selectively
for (var userId in connections){
sendContactListToUser(userId);
}

case "contacts.reset":
broadcast(message);
break;
case "contacts.userStatusUpdate":
broadcast(message);
break;
case "contacts.userOffline":
broadcast(message);
break;
default:
logger.error("Redis message received in socket.js on unexpected channel: " + channel);
Expand All @@ -92,6 +105,7 @@ function sendContactListToUser(userId){
logger.debug("Sending contact list to user. (id: " + userId + ")");
mysql.query(
"SELECT DISTINCT \
users.id as id, \
users.real_name as realName, \
users.nick, \
CONCAT('http://www.gravatar.com/avatar/', MD5(users.email)) as gravatar, \
Expand All @@ -112,17 +126,19 @@ function sendContactListToUser(userId){
logger.error(err);

if (rows && rows.length){
var contacts = [];

for (var i in rows){
contacts.push({
realName: (rows[i].realName || rows[i].nick),
gravatar: rows[i].gravatar,
nick: rows[i].nick,
status: rows[i].status || 'available'
});
sendMessageToUser(userId, JSON.stringify({
topic: 'contacts.userStatusUpdate',
data: {
id: rows[i].id,
realName: (rows[i].realName || rows[i].nick),
gravatar: rows[i].gravatar,
nick: rows[i].nick,
status: rows[i].status
}
}));
}

sendMessageToUser(userId, JSON.stringify({topic: 'contacts.list', data: contacts}));
}
}
);
Expand Down Expand Up @@ -169,7 +185,7 @@ module.exports = {
});

subscribeForStories();

// This is during the socket upgrade request.
// We reject for a few reasons mostly around auth
socket.on('request', function(request){
Expand Down Expand Up @@ -206,6 +222,16 @@ module.exports = {
connection.email = user.email;
connection.sessionID = sessionID;

connection.on("message", function(message){
message = JSON.parse(message.utf8Data)
if (message.topic == 'sidebar.refresh'){
userConnected(user);
}
else{
logger.error("Unhandled message from worker: " + JSON.stringify(message));
}
});

if (!connections[user.id]){
connections[user.id] = [];
}
Expand Down
91 changes: 71 additions & 20 deletions app/http/views/social/sidebar.ejs
Expand Up @@ -15,14 +15,14 @@

<link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css" >
<link href="/stylesheets/sidebar.css" rel="stylesheet" type="text/css" media="screen">
<script src="http://code.jquery.com/jquery-1.7.1.min.js" type="text/javascript"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script src="/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script src="https://browserid.org/include.js" type="text/javascript"></script>
<script src="/javascripts/application.js" type="text/javascript"></script>

<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<script src="https://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->

<!-- Le fav and touch icons
Expand All @@ -35,16 +35,21 @@
var storyTemplate = null;
var contactTemplate = null;
var port = null;
var resetContactListOnNextUpdate = true;
var my = {
'id': <%= user.id%>,
'nick': "<%= user.nick.replace('\"', '\\"')%>" //' <- that bit is to keep the syntax highlighter happy
};
var handlers = {
'user.signout': function(){
window.location.reload();
},
'feed.story': function(data){
dump(data + "\n\n");
var li = storyTemplate.clone();
li.attr('id', data.id);
dump(data.id + "\n");
if (data['image']){
var img = li.find('img.thumbnail');
img.attr('src', data.image.url);
Expand All @@ -64,27 +69,72 @@
$('.feed ul li:last-child').remove();
}
},
'contacts.list': function(data){
$('.contacts ul').empty();
for (var i in data){
var li = contactTemplate.clone();
var contact = data[i];
li.find('a').text(contact.realName).attr('href', 'https://mozillians.org/en-US/search?q=' + contact.nick);
li.find('img').attr('src', contact.gravatar + "?s=30");
if (contact.status != 'available'){
li.find('svg').remove();
li.find('.status').text(contact.status);
}
$('.contacts ul').append(li);
li.show();
'contacts.reset': function(){
resetContactListOnNextUpdate = true;
},
'contacts.userStatusUpdate': function(contact){
// TODO: worry about networks.
dump("Contact:\n\t" + JSON.stringify(contact) + "\n");
dump("Received contact update: "+ JSON.stringify(contact) + "\n");
// TODO: add my own status in the UI somewhere.
if (contact.id == my.id)
return;
if (resetContactListOnNextUpdate){
resetContactListOnNextUpdate = false;
$('.contacts ul').empty();
}
var li = $('#contact_' + contact.id);
if (li.length == 0){
li = contactTemplate.clone();
li.attr('id', 'contact_' + contact.id);
$('.contacts ul').append(li);
}
li.find('a').text(contact.realName).attr('href', 'https://mozillians.org/en-US/search?q=' + contact.nick);
li.find('img').attr('src', contact.gravatar + "?s=30");
if (contact.status){
li.find('svg').hide();
var button = li.find('.status button');
var btnClasses = statusClasses[contact.status];
if (!btnClasses){
btnClasses = statusClasses['default'];
}
button.attr('class', btnClasses);
button.text(contact.status);
button.show();
}
else{
li.find('svg').show();
li.find('.status button').hide();
}
li.show();
},
'contacts.userOffline': function(contact){
$('#contact_' + contact.id).remove();
}
};
var statusClasses = {
afk: 'btn btn-mini btn-info',
away: 'btn btn-mini btn-info',
zzz: 'btn btn-mini btn-inverse',
offline: 'btn btn-mini btn-inverse',
meeting: 'btn btn-mini btn-warning',
mtg: 'btn btn-mini btn-warning',
dnd: 'btn btn-mini btn-error',
busy: 'btn btn-mini btn-error',
'default': 'btn btn-mini'
}
function init(){
dump('Initializing Story UI\n');
storyTemplate = $('#story_template');
Expand Down Expand Up @@ -150,7 +200,7 @@
alt="placeholder alt"
height="30"
width="30"
src="https://secure.gravatar.com/avatar/xx?s=30&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png" />
src="https://secure.gravatar.com/avatar/xx?s=30&amp;d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png" />
<p>bronson commented on issue 531 on twitter/bootstrap</p>
</li>
</ul>
Expand All @@ -170,6 +220,7 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="15" cy="15" r="5" stroke="black" stroke-width="0" fill="green" />
</svg>
<button class="btn btn-mini" data-toggle="dropdown"></button>
</span>
</li>
</ul>
Expand Down
76 changes: 46 additions & 30 deletions app/http/views/social/worker.js.ejs
Expand Up @@ -17,6 +17,9 @@
// Make sure we have the right WebSocket class.
WebSocket = WebSocket || MozWebSocket;

var motown;
var reconnectInterval;

function log(message, angry){
if (typeof(message.join) == 'function'){
message = message.join("\n");
Expand Down Expand Up @@ -50,17 +53,44 @@ function log(message, angry){
dump("\n\n\n");
}

function rawSidebarMessage(message){
sidebarPort.postMessage(message);
}

function initWebSocket(){
clearInterval(reconnectInterval)
motown = new WebSocket("<%= wsUrl%>");

motown.onmessage = function(event){
var message = JSON.parse(event.data);

var handler = handlers[message['topic']];

if (handler == null){
log("No handler found for " + message.topic + " from socket.", true);
}
else{
// log(["Message from motown: ", JSON.stringify(message)]);
handler(message);
}
};
motown.onopen = function(){
log("Connected to MoTown!");
};
motown.onclose = function(e){
//TODO: Put sidebar in "disconnected" state
motown = undefined;
log({title: "WebSocket to MoTown closed", code: e.code, reason: e.reason});
reconnectInterval = setInterval(initWebSocket, 200);
};
}

var handlers = {
'user.signout': function(){
sidebarPort.postMessage({topic: 'user.signout'});
},
'feed.story': function(story){
// dump("Sending message to sidebar. (port: " + sidebarPort + ")\n\tStory to send: " + JSON.stringify(story) + "\n\n");
sidebarPort.postMessage({topic: 'feed.story', data: story});
},
'contacts.list': function(contactList){
sidebarPort.postMessage({topic: 'contacts.list', data: contactList});
},
'user.signout': rawSidebarMessage,
'feed.story': rawSidebarMessage,
'contacts.userOffline': rawSidebarMessage,
'contacts.reset': rawSidebarMessage,
'contacts.userStatusUpdate': rawSidebarMessage,
'social.port-closing': function(data, port){
if (apiPort == port){
apiPort.close();
Expand All @@ -75,28 +105,14 @@ var handlers = {
'sidebar.registration': function(data, port){
sidebarPort = port;
dump('sidebar.registration completed, connecting to MoTown\n');
var motown = new WebSocket("<%= wsUrl%>");

motown.onmessage = function(event){
var message = JSON.parse(event.data);

var handler = handlers[message['topic']];

if (handler == null){
dump("No handler found for " + message.topic + " from socket.\n");
}
else{
// log(["Message from motown: ", JSON.stringify(message)]);
handler(message.data);
}
};
motown.onopen = function(){
log("Connected to MoTown!", true);
};
motown.onclose = function(e){
//TODO: Put sidebar in "disconnected" state and try to reconnect
log({title: "WebSocket to MoTown closed", code: e.code, reason: e.reason});
};
if (typeof(motown) == 'undefined'){
initWebSocket();
}
else {
motown.send(JSON.stringify({topic: 'sidebar.refresh'}));
}
},
'social.cookie-changed': function(data){},
};
Expand Down

0 comments on commit b1cee7b

Please sign in to comment.