diff --git a/www/common/common-messaging.js b/www/common/common-messaging.js index f34100601..af7cdb1a3 100644 --- a/www/common/common-messaging.js +++ b/www/common/common-messaging.js @@ -8,7 +8,9 @@ define([ var Types = { message: 'MSG', update: 'UPDATE', - unfriend: 'UNFRIEND' + unfriend: 'UNFRIEND', + mapId: 'MAP_ID', + mapIdAck: 'MAP_ID_ACK' }; // TODO @@ -107,6 +109,7 @@ define([ $friend.append($rightCol); }); } + $('', {'class': 'status'}).appendTo($friend); }; Msg.getFriendListUI = function (common, open, remove) { var proxy = common.getProxy(); @@ -204,8 +207,37 @@ define([ return ready.length; }; + // Id message allows us to map a netfluxId with a public curve key + var onIdMessage = function (common, msg, sender) { + var channel; + var isId = Object.keys(channels).some(function (chanId) { + if (channels[chanId].userList.indexOf(sender) !== -1) { + channel = channels[chanId]; + return true; + } + }); + + if (!isId) { return; } + + var decryptedMsg = channel.encryptor.decrypt(msg); + var parsed = JSON.parse(decryptedMsg); + if (parsed[0] !== Types.mapId && parsed[0] !== Types.mapidAck) { return; } + if (parsed[2] !== sender || !parsed[1]) { return; } + channel.mapId[sender] = parsed[1]; + + channel.updateStatus(); + + if (parsed[0] !== Types.mapId) { return; } // Don't send your key if it's already an ACK + // Answer with your own key + var proxy = common.getProxy(); + var network = common.getNetwork(); + var rMsg = [Types.mapIdAck, proxy.curvePublic, channel.wc.myID]; + var rMsgStr = JSON.stringify(rMsg); + var cryptMsg = channel.encryptor.encrypt(rMsgStr); + network.sendto(sender, cryptMsg); + }; var onDirectMessage = function (common, msg, sender) { - if (sender !== Msg.hk) { return; } + if (sender !== Msg.hk) { return void onIdMessage(common, msg, sender); } var parsed = JSON.parse(msg); if ((parsed.validateKey || parsed.owners) && parsed.channel) { return; @@ -232,7 +264,10 @@ define([ if (!channels[chan.id]) { return; } var isMessage = pushMsg(common, channels[chan.id], msg); if (isMessage) { - channels[chan.id].notify(); + // Don't notify for your own messages + if (channels[chan.id].wc.myID !== sender) { + channels[chan.id].notify(); + } channels[chan.id].refresh(); } }; @@ -457,6 +492,18 @@ define([ } } }; + var updateStatus = function (curvePublic) { + var data = getFriend(common, curvePublic); + var chan = channels[data.channel]; + var $friend = $listContainer.find('.friend').filter(function (idx, el) { + return $(el).data('key') === curvePublic; + }); + var status = chan.userList.some(function (nId) { + return chan.mapId[nId] === curvePublic; + }); + var statusText = status ? 'online' : 'offline'; + $friend.find('.status').attr('class', 'status '+statusText); + }; // Open the channels var openFriendChannel = function (f) { @@ -465,7 +512,7 @@ define([ var keys = Curve.deriveKeys(data.curvePublic, proxy.curvePrivate); var encryptor = Curve.createEncryptor(keys); network.join(data.channel).then(function (chan) { - channels[data.channel] = { + var channel = channels[data.channel] = { friendEd: f, keys: keys, encryptor: encryptor, @@ -475,11 +522,34 @@ define([ unnotify: function () { unnotify(data.curvePublic); }, removeUI: function () { removeUI(data.curvePublic); }, updateUI: function (types) { updateUI(data.curvePublic, types); }, - wc: chan + updateStatus: function () { updateStatus(data.curvePublic); }, + wc: chan, + userList: [], + mapId: {} }; chan.on('message', function (msg, sender) { onMessage(common, msg, sender, chan); }); + var onJoining = function (peer) { + if (peer === Msg.hk) { return; } + if (channel.userList.indexOf(peer) !== -1) { return; } + channel.userList.push(peer); + var msg = [Types.mapId, proxy.curvePublic, chan.myID]; + var msgStr = JSON.stringify(msg); + var cryptMsg = channel.encryptor.encrypt(msgStr); + network.sendto(peer, cryptMsg); + channel.updateStatus(); + }; + chan.members.forEach(onJoining); + chan.on('join', onJoining); + chan.on('leave', function (peer) { + var i = channel.userList.indexOf(peer); + while (i !== -1) { + channel.userList.splice(i, 1); + i = channel.userList.indexOf(peer); + } + channel.updateStatus(); + }); var cfg = { validateKey: keys.validateKey, owners: [proxy.edPublic, data.edPublic], @@ -548,7 +618,13 @@ define([ var keyStr = parsed.hashData.key; var cryptor = Crypto.createEditCryptor(keyStr); var key = cryptor.cryptKey; - var decryptMsg = Crypto.decrypt(message, key); + var decryptMsg + try { + decryptMsg = Crypto.decrypt(message, key); + } catch () { + // If we can't decrypt, it means it is not a friend request message + } + if (!decrypMsg) { return; } // Parse msg = JSON.parse(decryptMsg); if (msg[1] !== parsed.hashData.channel) { return; } diff --git a/www/contacts/main.less b/www/contacts/main.less index 80c7f6f6a..e726c34ba 100644 --- a/www/contacts/main.less +++ b/www/contacts/main.less @@ -81,9 +81,28 @@ body { media-tag, .default { margin-right: 5px; } + .status { + width: 5px; + display: inline-block; + position: absolute; + right: 0; + top: 0; + bottom: 0; + opacity: 0.7; + background-color: #777; + &.online { + background-color: green; + } + &.offline { + background-color: red; + } + } } #friendList { + .friend { + position: relative; + } .remove { cursor: pointer; width: 20px;