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 0000000..ce2f159 Binary files /dev/null and b/public/logo.png differ diff --git a/public/style.css b/public/style.css index a65b344..fde5521 100644 --- a/public/style.css +++ b/public/style.css @@ -4,6 +4,7 @@ html, body { padding: 0px; margin: 0px; background-color: #ecf0f2; + color: #2e2d2d; } #pages { @@ -14,11 +15,62 @@ html, body { padding: 5px; } +#reconnect { + z-index: 5000; + position: absolute; + left: 0px; + top: 0px; + height: 100%; + width: 100%; + background-color: rgba(0, 0, 0, .5); +} + +#reconnect.connected { + display: none; +} + +#me { + display: none; +} + +nav.main { + background-color: #1C232D; + width: 176px; + border-right: 1px solid black; + padding: 5px; + margin: 0px; + text-align: center; + box-sizing: border-box; + position: fixed; + bottom: 0px; + left: 0px; +} + +nav.main li { + display: inline-block; + margin: 5px; +} + +nav.main a { + display: block; + text-decoration: none; + text-align: center; + border-radius: 3px; + border: none; + height: 20px; + padding: 0 1em; + color: white; + background-color: #333; + line-height: 20px; + font-size: 12px; + cursor: pointer; + box-sizing: border-box; +} + #contactList { top: 0px; - left: 0px; + bottom: 40px; width: 175px; - height: 100%; background-color: #1C232D; position: fixed; overflow-y: auto; @@ -122,6 +174,12 @@ html, body { color: #777; } +.contact.activeContact { + background-color: #35c8ff; + background-image: -moz-linear-gradient(top, #35c8ff, #00aeef); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #35c8ff), color-stop(1, #00aeef)); +} + .chatView { position: relative; } @@ -226,8 +284,18 @@ html, body { font-size: 14px; } -#me { +.contact .unread { display: none; + color: white; + width: 20px; + border-radius: 10px; + position: relative; + text-align: center; + background-color: red; +} + +.contact.hasUnread .unread { + display: block; } #chatInput { @@ -270,9 +338,58 @@ html, body { background-color: yellow; } -#loginForm { - margin: 50px; +#loginbox { + position: relative; + margin: auto; + margin-top: 5%; + padding: 20px; + background-color: white; + width: 75%; } -#loginForm label { +#loginbox label { display: block; + margin-bottom: 5px; + font-size: 13px; + font-weight: bold; + color: #777; +} +#loginbox input { + width: 100%; + display: block; + height: 35px; + font-size: 14px; + padding: 10px 0.5em; + margin-bottom: 15px; + background-color: #f9fafa; + border: 1px solid #eeeeee; + color: #2e2d2d; + box-sizing: border-box; +} +#loginbox input:focus { + border: 1px solid #a7d9eb; + outline: 0px; +} +#loginbox button { + text-decoration: none; + text-align: center; + border-radius: 3px; + border: none; + height: 35px; + padding: 0 1em; + color: white; + background-color: #7ec6e2; + line-height: 35px; + font-size: 16px; + cursor: pointer; +} +#loginbox h2 { + padding-bottom: 10px; + border-bottom: 1px solid #f8f8f8 +} +.aux header { + margin-top: 10%; + text-align: center; +} +#logo { + margin: auto; } diff --git a/server.js b/server.js index 92df127..c3d74b5 100644 --- a/server.js +++ b/server.js @@ -51,8 +51,9 @@ app.get('/logout', function (req, res) { app.get('*', clientApp.html()); -https.createServer({ - key: fs.readFileSync(config.http.key), - cert: fs.readFileSync(config.http.cert) -}, app).listen(config.http.port); +//https.createServer({ +// key: fs.readFileSync(config.http.key), +// cert: fs.readFileSync(config.http.cert) +//}, app).listen(config.http.port); +app.listen(config.http.port); console.log('demo.stanza.io running at: ' + config.http.baseUrl); diff --git a/views/layout.jade b/views/layout.jade index 508bbcf..c419189 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -7,7 +7,9 @@ html link(rel="stylesheet", href="/styles.css") block head - body + body.aux + header + img#logo(src="/logo.png", alt="OTalk") block content script(src='/zepto.js')