From 378f6e0a8bfe029290a7af96edd0665ee53354e4 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 11 Sep 2013 20:59:50 -0700 Subject: [PATCH] Disconnected overlay, active contact selection, unread count badges, login screen --- clientapp/helpers/xmppEventHandlers.js | 14 +- clientapp/models/contact.js | 10 +- clientapp/models/me.js | 21 ++- clientapp/pages/base.js | 8 +- clientapp/templates.js | 4 +- clientapp/templates/body.jade | 6 + .../templates/includes/contactListItem.jade | 1 + clientapp/views/contactListItem.js | 7 +- clientapp/views/main.js | 3 + public/logo.png | Bin 0 -> 1745 bytes public/style.css | 129 +++++++++++++++++- server.js | 9 +- views/layout.jade | 4 +- 13 files changed, 195 insertions(+), 21 deletions(-) create mode 100644 public/logo.png diff --git a/clientapp/helpers/xmppEventHandlers.js b/clientapp/helpers/xmppEventHandlers.js index db03672..a8274f8 100644 --- a/clientapp/helpers/xmppEventHandlers.js +++ b/clientapp/helpers/xmppEventHandlers.js @@ -78,7 +78,7 @@ module.exports = function (client, app) { }); client.on('disconnected', function () { - me.connectionStatus = 'disconnected'; + me.connected = false; }); client.on('auth:failed', function () { @@ -89,7 +89,7 @@ module.exports = function (client, app) { client.on('session:started', function (jid) { me.jid = jid; - me.connectionStatus = 'connected'; + me.connected = true; client.getRoster(function (err, resp) { resp = resp.toJSON(); @@ -186,6 +186,13 @@ module.exports = function (client, app) { if (info.chatState === 'gone') { contact.lockedResource = undefined; } + } else if (me.isMe(info.from)) { + if (info.chatState === 'active' || info.chatState === 'composing') { + contact = me.getContact(info.to); + if (contact) { + contact.unreadCount = 0; + } + } } }); @@ -206,6 +213,9 @@ module.exports = function (client, app) { // }); //} + if (!contact.activeContact) { + contact.unreadCount++; + } contact.messages.add(message); if (!contact.lockedResource) { contact.lockedResource = msg.from.full; diff --git a/clientapp/models/contact.js b/clientapp/models/contact.js index c5543a0..c55f967 100644 --- a/clientapp/models/contact.js +++ b/clientapp/models/contact.js @@ -66,6 +66,12 @@ module.exports = HumanModel.define({ return ''; } } + }, + hasUnread: { + deps: ['unreadCount'], + fn: function () { + return this.unreadCount > 0; + } } }, session: { @@ -77,7 +83,9 @@ module.exports = HumanModel.define({ chatState: ['string', true, 'gone'], lockedResource: 'string', lastSentMessage: 'object', - timezoneOffset: ['number', false, 0] + timezoneOffset: ['number', false, 0], + activeContact: ['bool', true, false], + unreadCount: ['number', true, 0] }, collections: { resources: Resources, diff --git a/clientapp/models/me.js b/clientapp/models/me.js index 768a981..daeb6bf 100644 --- a/clientapp/models/me.js +++ b/clientapp/models/me.js @@ -1,4 +1,4 @@ -/*global app*/ +/*global app, client*/ "use strict"; var HumanModel = require('human-model'); @@ -14,12 +14,29 @@ module.exports = HumanModel.define({ session: { jid: ['object', true], status: ['string', true, ''], - avatar: ['string', true, ''] + avatar: ['string', true, ''], + connected: ['bool', true, false], + _activeContact: ['string', true, ''] }, collections: { contacts: Contacts }, + setActiveContact: function (jid) { + var prev = this.getContact(this._activeContact); + if (prev) { + prev.activeContact = false; + } + var curr = this.getContact(jid); + if (curr) { + curr.activeContact = true; + curr.unreadCount = 0; + } + this._activeContact = jid; + }, getContact: function (jid, alt) { + if (typeof jid === 'string') jid = new client.JID(jid); + if (typeof alt === 'string') alt = new client.JID(alt); + if (this.isMe(jid)) { jid = alt || jid; } diff --git a/clientapp/pages/base.js b/clientapp/pages/base.js index d4cb047..70de378 100644 --- a/clientapp/pages/base.js +++ b/clientapp/pages/base.js @@ -1,4 +1,4 @@ -/*global $, app*/ +/*global $, app, me*/ "use strict"; var _ = require('underscore'); @@ -29,6 +29,10 @@ module.exports = HumanView.extend({ this.trigger('pageloaded'); + if (this.model.jid) { + me.setActiveContact(this.model.jid); + } + return this; }, hide: function () { @@ -45,6 +49,8 @@ module.exports = HumanView.extend({ this.animateRemove(); } + me.setActiveContact(''); + return this; } }); diff --git a/clientapp/templates.js b/clientapp/templates.js index d72a45d..0b56865 100644 --- a/clientapp/templates.js +++ b/clientapp/templates.js @@ -12,7 +12,7 @@ exports.pages = {}; exports.body = function anonymous(locals) { var buf = []; with (locals || {}) { - buf.push('

'); + buf.push('

'); } return buf.join(""); }; @@ -35,7 +35,7 @@ exports.includes.contactListItem = function anonymous(locals) { "class": "avatar" }, { src: true - }) + '/>
' + jade.escape(null == (jade.interp = contact.displayName) ? "" : jade.interp) + '
' + jade.escape(null == (jade.interp = contact.status) ? "" : jade.interp) + "
"); + }) + '/>
' + jade.escape(null == (jade.interp = contact.displayName) ? "" : jade.interp) + '
' + jade.escape(null == (jade.interp = contact.unreadCount) ? "" : jade.interp) + '
' + jade.escape(null == (jade.interp = contact.status) ? "" : jade.interp) + "
"); } return buf.join(""); }; diff --git a/clientapp/templates/body.jade b/clientapp/templates/body.jade index 2f995ac..e778788 100644 --- a/clientapp/templates/body.jade +++ b/clientapp/templates/body.jade @@ -1,8 +1,14 @@ body .wrap + #reconnect header#me img.avatar p.status + nav.main + li + a(href="/logout") Logout + li + a(href="/") Home nav#contactList section#pages footer diff --git a/clientapp/templates/includes/contactListItem.jade b/clientapp/templates/includes/contactListItem.jade index 15a475b..e00e587 100644 --- a/clientapp/templates/includes/contactListItem.jade +++ b/clientapp/templates/includes/contactListItem.jade @@ -1,4 +1,5 @@ li.contact img.avatar(src=contact.avatar) .name=contact.displayName + .unread=contact.unreadCount .status=contact.status diff --git a/clientapp/views/contactListItem.js b/clientapp/views/contactListItem.js index 7a97259..018c31a 100644 --- a/clientapp/views/contactListItem.js +++ b/clientapp/views/contactListItem.js @@ -11,11 +11,14 @@ module.exports = HumanView.extend({ classBindings: { show: '', subscription: '', - chatState: '' + chatState: '', + activeContact: '', + hasUnread: '' }, textBindings: { displayName: '.name', - status: '.status' + status: '.status', + unreadCount: '.unread' }, srcBindings: { avatar: '.avatar' diff --git a/clientapp/views/main.js b/clientapp/views/main.js index 5ba7d0b..9c33516 100644 --- a/clientapp/views/main.js +++ b/clientapp/views/main.js @@ -11,6 +11,9 @@ module.exports = HumanView.extend({ events: { 'click a[href]': 'handleLinkClick' }, + classBindings: { + connected: '#reconnect' + }, render: function () { $('head').append(templates.head()); this.renderAndBind(); diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ce2f15915c4d3eb6e777bb10b745d0812935529c GIT binary patch literal 1745 zcmV;?1}^!DP)|Nb);z$r4Q@lgf1z9KFl=d*Iu9Nm12L*4i1FT?P{Jw9A~&23w_ zZ(HSi_nqh(rw8WyOTh94_f-mE`Y`}1PwZb zD`=0@(0|z(f+vFK*1j^y2RR73?o!Q^X5>e-iNv4*9>vmdT)GNZ6k_jlv=#zkL5(wa zV~W@oB+yW$N+v6=$HDo2P5rzf#_sd-Gz0|SAj%9g>4he>O;i>%zW@h>9SR0LVZ^|N z)D$s=Fy@kkGvOR&=o{q$u|istP?wa8iG`X9=M+(~IamKXZ4qc@WkK@+I4KRmfyOrx zE&*tYpb)%tST~ai1YU{zLrMhdz%#%I3;OP_0W?`3XDTo$K~aqW_XPrfVnTVNP7#^b zSEifSAZS9HpveM<(>5Cre%X}z)KZ@|633fSL!b!+rql>D@4z85#rZaivXh{_s@4A# zI8A}(**0j>J`NX=9HbkR0$MU^^R7Wgd&CQ$G{jiL?Dq-)Rq^4swt*%M9M1k)V+{I8 zpy5tBe+qL+bw*dj?RTBiiVEHULc? z-L?ogQmsAa&aQL7;WW-sT^W&Cs$v7TYxJNiR+B_kb<(k6&?I{`1LX)QGTn^rrv!Od zqv;qm%_sxVoJcbrsd>Z?NhI18LNgnhM9zJo2JkG0n;Z};EAS!{H4$`u+l%9}y{Skn zXfcpZbk!}Ed-V6D<*z*sfJZ?Vbh z3B&EN#jOAigolb5$qD$6E*G6&;x~aC=d@w^4wSGuqDL$IC=gg^X(F5*ty*YY6crU2 zw1qV{Nb^i-NRTpHc{HKCmN=^77&fD$F=fnDD9Cq24Kn&rG$E$=?0XZSi85w27P=iv zZXxsx1-33)=1HLAHrsXIjI_JS6H<|*%p4r!pLvL>sSa!6yvZ$K8R5vJ1;5))L^ z9Nt+n6@wzlFjDPpN+iYtJ|6@;4J8^U8So?k&v!ICT%9mF)KtJFIW4gf9Mscl!@P(9 z1)H+lT;1@}BpD{$Ol%dA$&zTuhrA;`{Y)5q1VN)_m`MgjN0<6T|cwxB5J5uUlNpj$SU{Jj%jzz&Ex3Ql?^827;^xOUnE~cxrwoK; z;7~?a=i59HlRwdCQxI(kFe3xgqyx_<1Ji)oZGP#$u%PCFhK%^#(9|SjHhNa7p%DGe zWZnEwe@!XRlJ!6K6j4N%A|6TtdK6_OH7Y6<^5Q+)lDQc!hccvRXP>Xs&ttrCf$@C$ nm7kf