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')