diff --git a/customize.dist/src/less2/include/notifications.less b/customize.dist/src/less2/include/notifications.less index d65da5e49..b0d6b22ed 100644 --- a/customize.dist/src/less2/include/notifications.less +++ b/customize.dist/src/less2/include/notifications.less @@ -15,12 +15,21 @@ .cp-notification-content { flex: 1; min-width: 0; + p { + word-break: break-all; + } + &.cp-clickable { + cursor: pointer; + &:hover { + background-color: rgba(0,0,0,0.1); + } + } } .cp-notification-dismiss { color: black; width: 25px; height: 100%; - display: flex; + display: none; align-items: center; justify-content: center; span { diff --git a/www/common/common-messaging.js b/www/common/common-messaging.js index b0d13ec81..a53f0c3b2 100644 --- a/www/common/common-messaging.js +++ b/www/common/common-messaging.js @@ -7,14 +7,7 @@ define([ '/common/common-realtime.js', ], function (Crypto, Hash, Util, Constants, Messages, Realtime) { - var Msg = { - inputs: [], - }; - - // TODO - // - mute a channel (hide notifications or don't open it?) - var pending = {}; - var pendingRequests = []; + var Msg = {}; var createData = Msg.createData = function (proxy, hash) { return { @@ -23,11 +16,13 @@ define([ profile: proxy.profile && proxy.profile.view, edPublic: proxy.edPublic, curvePublic: proxy.curvePublic, + notifications: Util.find(proxy, ['mailboxes', 'notifications', 'channel']), avatar: proxy.profile && proxy.profile.avatar }; }; - var getFriend = function (proxy, pubkey) { + var getFriend = Msg.getFriend = function (proxy, pubkey) { + if (!pubkey) { return; } if (pubkey === proxy.curvePublic) { var data = createData(proxy); delete data.channel; @@ -56,21 +51,18 @@ define([ return list; }; - // TODO make this internal to the messenger - var channels = Msg.channels = {}; - - Msg.getLatestMessages = function () { - Object.keys(channels).forEach(function (id) { - if (id === 'me') { return; } - var friend = channels[id]; - friend.getMessagesSinceDisconnect(); - friend.refresh(); + Msg.acceptFriendRequest = function (store, data, cb) { + var friend = getFriend(store.proxy, data.curvePublic) || {}; + var myData = createData(store.proxy, friend.channel || data.channel); + store.mailbox.sendTo('ACCEPT_FRIEND_REQUEST', myData, { + channel: data.notifications, + curvePublic: data.curvePublic + }, function (obj) { + cb(obj); + if (obj && obj.error) { return void cb(obj); } }); }; - - // Invitation - // FIXME there are too many functions with this name - var addToFriendList = Msg.addToFriendList = function (cfg, data, cb) { + Msg.addToFriendList = function (cfg, data, cb) { var proxy = cfg.proxy; var friends = getFriendList(proxy); var pubKey = data.curvePublic; // todo validata data @@ -85,135 +77,6 @@ define([ if (res.error) { console.error(res.error); } }); }); - cfg.updateMetadata(); - }; - - /* Used to accept friend requests within apps other than /contacts/ */ - Msg.addDirectMessageHandler = function (cfg, href) { - var network = cfg.network; - var proxy = cfg.proxy; - if (!network) { return void console.error('Network not ready'); } - network.on('message', function (message, sender) { - var msg; - if (sender === network.historyKeeper) { return; } - try { - var parsed = Hash.parsePadUrl(href); - var secret = Hash.getSecrets(parsed.type, parsed.hash); - if (!parsed.hashData) { return; } - var chan = secret.channel; - // Decrypt - var key = secret.keys ? secret.keys.cryptKey : Hash.decodeBase64(secret.key); - var decryptMsg; - try { - decryptMsg = Crypto.decrypt(message, key); - } catch (e) { - // If we can't decrypt, it means it is not a friend request message - } - if (!decryptMsg) { return; } - // Parse - msg = JSON.parse(decryptMsg); - if (msg[1] !== chan) { return; } - var msgData = msg[2]; - var msgStr; - if (msg[0] === "FRIEND_REQ") { - msg = ["FRIEND_REQ_NOK", chan]; - var todo = function (yes) { - if (yes) { - pending[sender] = msgData; - msg = ["FRIEND_REQ_OK", chan, createData(proxy, msgData.channel)]; - } - msgStr = Crypto.encrypt(JSON.stringify(msg), key); - network.sendto(sender, msgStr); - }; - var existing = getFriend(proxy, msgData.curvePublic); - if (existing) { - todo(true); - return; - } - var confirmMsg = Messages._getKey('contacts_request', [ - Util.fixHTML(msgData.displayName) - ]); - cfg.friendRequest(confirmMsg, todo); - return; - } - if (msg[0] === "FRIEND_REQ_OK") { - var idx = pendingRequests.indexOf(sender); - if (idx !== -1) { pendingRequests.splice(idx, 1); } - - // FIXME clarify this function's name - addToFriendList(cfg, msgData, function (err) { - if (err) { - return void cfg.friendComplete({ - logText: Messages.contacts_addError, - netfluxId: sender - }); - } - cfg.friendComplete({ - logText: Messages.contacts_added, - netfluxId: sender, - friend: msgData - }); - var msg = ["FRIEND_REQ_ACK", chan]; - var msgStr = Crypto.encrypt(JSON.stringify(msg), key); - network.sendto(sender, msgStr); - }); - return; - } - if (msg[0] === "FRIEND_REQ_NOK") { - var i = pendingRequests.indexOf(sender); - if (i !== -1) { pendingRequests.splice(i, 1); } - cfg.friendComplete({ - logText: Messages.contacts_rejected, - netfluxId: sender, - }); - cfg.updateMetadata(); - return; - } - if (msg[0] === "FRIEND_REQ_ACK") { - var data = pending[sender]; - if (!data) { return; } - addToFriendList(cfg, data, function (err) { - if (err) { - return void cfg.friendComplete({ - logText: Messages.contacts_addError, - netfluxId: sender - }); - } - cfg.friendComplete({ - logText: Messages.contacts_added, - netfluxId: sender, - friend: data - }); - }); - return; - } - // TODO: timeout ACK: warn the user - } catch (e) { - console.error("Cannot parse direct message", msg || message, "from", sender, e); - } - }); - }; - - Msg.inviteFromUserlist = function (cfg, data, cb) { - var network = cfg.network; - var netfluxId = data.netfluxId; - var parsed = Hash.parsePadUrl(data.href); - var secret = Hash.getSecrets(parsed.type, parsed.hash); - if (!parsed.hashData) { return; } - // Message - var chan = secret.channel; - var myData = createData(cfg.proxy); - var msg = ["FRIEND_REQ", chan, myData]; - // Encryption - var key = secret.keys ? secret.keys.cryptKey : Hash.decodeBase64(secret.key); - var msgStr = Crypto.encrypt(JSON.stringify(msg), key); - // Send encrypted message - if (pendingRequests.indexOf(netfluxId) === -1) { - pendingRequests.push(netfluxId); - cfg.updateMetadata(); // redraws the userlist in pad - } - network.sendto(netfluxId, msgStr); - cb(); }; return Msg; diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 4949b76c0..3ba27f86f 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2607,5 +2607,25 @@ define([ return m; }; + UIElements.displayFriendRequestModal = function (common, data) { + var msg = data.content.msg; + var text = Messages._getKey('contacts_request', [msg.content.displayName]); + UI.confirm(text, function (yes) { + common.getSframeChannel().query("Q_ANSWER_FRIEND_REQUEST", { + data: data, + value: yes + }, function (err, obj) { + var error = err || (obj && obj.error); + if (error) { + return void UI.warn(error); + } + UI.log(Messages.contacts_added); + }); + }, { + ok: 'Accept', // XXX + cancel: 'Ignore the request' // XXX + }, true); + }; + return UIElements; }); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 027b61929..dde1e9d5e 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -604,16 +604,6 @@ define([ }); }; - // Messaging (manage friends from the userlist) - common.inviteFromUserlist = function (netfluxId, cb) { - postMessage("INVITE_FROM_USERLIST", { - netfluxId: netfluxId, - href: window.location.href - }, function (obj) { - if (obj && obj.error) { return void cb(obj.error); } - cb(); - }); - }; // Admin common.adminRpc = function (data, cb) { @@ -625,14 +615,13 @@ define([ common.onNetworkReconnect = Util.mkEvent(); common.onNewVersionReconnect = Util.mkEvent(); - // Messaging + // Messaging (friend requests) var messaging = common.messaging = {}; - messaging.onFriendRequest = Util.mkEvent(); - messaging.onFriendComplete = Util.mkEvent(); - messaging.addHandlers = function (href) { - postMessage("ADD_DIRECT_MESSAGE_HANDLERS", { - href: href - }); + messaging.answerFriendRequest = function (data, cb) { + postMessage("ANSWER_FRIEND_REQUEST", data, cb); + }; + messaging.sendFriendRequest = function (data, cb) { + postMessage("SEND_FRIEND_REQUEST", data, cb); }; // Onlyoffice @@ -1081,9 +1070,6 @@ define([ var localToken = tryParsing(localStorage.getItem(Constants.tokenKey)); if (localToken !== data.token) { requestLogin(); } }, - // Messaging - Q_FRIEND_REQUEST: common.messaging.onFriendRequest.fire, - EV_FRIEND_COMPLETE: common.messaging.onFriendComplete.fire, // Network NETWORK_DISCONNECT: common.onNetworkDisconnect.fire, NETWORK_RECONNECT: function (data) { diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 5b25b0379..ac02e3ce3 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -461,7 +461,8 @@ define([ friends: store.proxy.friends || {}, settings: store.proxy.settings, thumbnails: disableThumbnails === false, - isDriveOwned: Boolean(Util.find(store, ['driveMetadata', 'owners'])) + isDriveOwned: Boolean(Util.find(store, ['driveMetadata', 'owners'])), + pendingFriends: store.proxy.friends_pending || {} } }; cb(JSON.parse(JSON.stringify(metadata))); @@ -902,36 +903,63 @@ define([ // Messaging (manage friends from the userlist) - var getMessagingCfg = function (clientId) { - return { - proxy: store.proxy, - realtime: store.realtime, - network: store.network, - updateMetadata: function () { - postMessage(clientId, "UPDATE_METADATA"); - }, - pinPads: function (data, cb) { Store.pinPads(null, data, cb); }, - friendComplete: function (data) { - if (data.friend && store.messenger && store.messenger.onFriendAdded) { - store.messenger.onFriendAdded(data.friend); - } - postMessage(clientId, "EV_FRIEND_COMPLETE", data); - }, - friendRequest: function (data, cb) { - postMessage(clientId, "Q_FRIEND_REQUEST", data, cb); - }, + Store.answerFriendRequest = function (clientId, obj, cb) { + console.log(obj); + var value = obj.value; + var data = obj.data; + if (data.type !== 'notifications') { return void cb ({error: 'EINVAL'}); } + var hash = data.content.hash; + var msg = data.content.msg; + + var dismiss = function (cb) { + cb = cb || function () {}; + store.mailbox.dismiss({ + hash: hash, + type: 'notifications' + }, cb); }; + + if (value) { + Messaging.acceptFriendRequest(store, msg.content, function (obj) { + if (obj && obj.error) { return void cb(obj); } + Messaging.addToFriendList({ + proxy: store.proxy, + realtime: store.realtime, + pinPads: function (data, cb) { Store.pinPads(null, data, cb); }, + }, msg.content, function (err) { + broadcast([], "UPDATE_METADATA"); + if (err) { return void cb({error: err}); } + dismiss(cb); + }); + }); + return; + } + dismiss(); }; - Store.inviteFromUserlist = function (clientId, data, cb) { - var messagingCfg = getMessagingCfg(clientId); - Messaging.inviteFromUserlist(messagingCfg, data, cb); - }; - Store.addDirectMessageHandlers = function (clientId, data) { - var messagingCfg = getMessagingCfg(clientId); - Messaging.addDirectMessageHandler(messagingCfg, data.href); - }; + Store.sendFriendRequest = function (clientId, data, cb) { + var friend = Messaging.getFriend(store.proxy, data.curvePublic); + if (friend) { return void cb({error: 'ALREADY_FRIEND'}); } + if (!data.notifications || !data.curvePublic) { return void cb({error: 'INVALID_USER'}); } - // Messenger + store.proxy.friends_pending = store.proxy.friends_pending || {}; + + var twoDaysAgo = +new Date(); // (+new Date() - (2 * 24 * 3600 * 1000)); // XXX + if (store.proxy.friends_pending[data.curvePublic] && + store.proxy.friends_pending[data.curvePublic] > twoDaysAgo) { + return void cb({error: 'TIMEOUT'}); + } + + store.proxy.friends_pending[data.curvePublic] = +new Date(); + broadcast([], "UPDATE_METADATA"); + + var myData = Messaging.createData(store.proxy); + store.mailbox.sendTo('FRIEND_REQUEST', myData, { + channel: data.notifications, + curvePublic: data.curvePublic + }, function (obj) { + cb(obj); + }); + }; // Get hashes for the share button Store.getStrongerHash = function (clientId, data, cb) { @@ -946,6 +974,7 @@ define([ cb(); }; + // Messenger Store.messenger = { execCommand: function (clientId, data, cb) { if (!store.messenger) { return void cb({error: 'Messenger is disabled'}); } @@ -1444,7 +1473,13 @@ define([ if (!store.loggedIn || !store.proxy.edPublic) { return; } - store.mailbox = Mailbox.init(store, waitFor, function (ev, data, clients) { + store.mailbox = Mailbox.init({ + store: store, + updateMetadata: function () { + broadcast([], "UPDATE_METADATA"); + }, + pinPads: function (data, cb) { Store.pinPads(null, data, cb); }, + }, waitFor, function (ev, data, clients) { clients.forEach(function (cId) { postMessage(cId, 'MAILBOX_EVENT', { ev: ev, @@ -1606,6 +1641,10 @@ define([ // Trigger userlist update when the friendlist has changed broadcast([], "UPDATE_METADATA"); }); + proxy.on('change', ['friends_pending'], function () { + // Trigger userlist update when the friendlist has changed + broadcast([], "UPDATE_METADATA"); + }); proxy.on('change', ['settings'], function () { broadcast([], "UPDATE_METADATA"); }); diff --git a/www/common/outer/mailbox.js b/www/common/outer/mailbox.js index da7b8bf90..0b4ce7367 100644 --- a/www/common/outer/mailbox.js +++ b/www/common/outer/mailbox.js @@ -2,9 +2,10 @@ define([ '/common/common-util.js', '/common/common-hash.js', '/common/common-realtime.js', + '/common/outer/mailbox-handlers.js', '/bower_components/chainpad-netflux/chainpad-netflux.js', '/bower_components/chainpad-crypto/crypto.js', -], function (Util, Hash, Realtime, CpNetflux, Crypto) { +], function (Util, Hash, Realtime, Handlers, CpNetflux, Crypto) { var Mailbox = {}; var TYPES = [ @@ -14,13 +15,16 @@ define([ var BLOCKING_TYPES = [ ]; - var initializeMailboxes = function (mailboxes) { + var initializeMailboxes = function (ctx, mailboxes) { if (!mailboxes['notifications']) { mailboxes.notifications = { channel: Hash.createChannelId(), lastKnownHash: '', viewed: [] }; + ctx.pinPads([mailboxes.notifications.channel], function (res) { + if (res.error) { console.error(res); } + }); } }; @@ -55,6 +59,108 @@ proxy.mailboxes = { }; }; + // Send a message to someone else + var sendTo = function (ctx, type, msg, user, cb) { + if (!Crypto.Mailbox) { + return void cb({error: "chainpad-crypto is outdated and doesn't support mailboxes."}); + } + var keys = getMyKeys(ctx); + if (!keys) { return void cb({error: "missing asymmetric encryption keys"}); } + if (!user || !user.channel || !user.curvePublic) { return void cb({error: "no notification channel"}); } + + var crypto = Crypto.Mailbox.createEncryptor(keys); + var network = ctx.store.network; + + var ciphertext = crypto.encrypt(JSON.stringify({ + type: type, + content: msg + }), user.curvePublic); + + network.join(user.channel).then(function (wc) { + wc.bcast(ciphertext).then(function () { + cb(); + wc.leave(); + }); + }, function (err) { + cb({error: err}); + }); + }; + + var updateLastKnownHash = function (ctx, type) { + var m = Util.find(ctx, ['store', 'proxy', 'mailboxes', type]); + if (!m) { return; } + var box = ctx.boxes[type]; + if (!box) { return; } + + }; + + // Mark a message as read + var dismiss = function (ctx, data, cId, cb) { + var type = data.type; + var hash = data.hash; + var m = Util.find(ctx, ['store', 'proxy', 'mailboxes', type]); + if (!m) { return void cb({error: 'NOT_FOUND'}); } + var box = ctx.boxes[type]; + if (!box) { return void cb({error: 'NOT_LOADED'}); } + + // If the hash in in our history, get the index from the history: + // - if the index is 0, we can change our lastKnownHash + // - otherwise, just push to view + var idx; + if (box.history.some(function (el, i) { + if (hash === el) { + idx = i; + return true; + } + })) { + if (idx === 0) { + m.lastKnownHash = hash; + box.history.shift(); + delete box.content[hash]; + } else if (m.viewed.indexOf(hash) === -1) { + m.viewed.push(hash); + } + } + + // Clear data in memory if needed + // Check the "viewed" array to see if we're able to bump lastKnownhash more + var sliceIdx; + var lastKnownHash; + box.history.some(function (hash, i) { + var isViewed = m.viewed.indexOf(hash); + if (isViewed !== -1) { + sliceIdx = i + 1; + m.viewed.splice(isViewed, 1); + lastKnownHash = hash; + return false; + } + return true; + }); + + if (sliceIdx) { + box.history = box.history.slice(sliceIdx); + m.lastKnownHash = lastKnownHash; + } + + // Make sure we remove data about dismissed messages + Object.keys(box.content).forEach(function (h) { + if (box.history.indexOf(h) === -1 || m.viewed.indexOf(h) !== -1) { + delete box.content[h]; + } + }); + + Realtime.whenRealtimeSyncs(ctx.store.realtime, function () { + cb(); + ctx.emit('VIEWED', { + type: type, + hash: hash + }, ctx.clients.filter(function (clientId) { + return clientId !== cId; + })); + }); + }; + + var openChannel = function (ctx, type, m, onReady) { var box = ctx.boxes[type] = { queue: [], // Store the messages to send when the channel is ready @@ -126,8 +232,19 @@ proxy.mailboxes = { msg: msg, hash: hash }; - box.content[hash] = msg; - showMessage(ctx, type, message); + Handlers(ctx, box, message, function (toDismiss) { + if (toDismiss) { + dismiss(ctx, { + type: type, + hash: hash + }, '', function () { + console.log('Notification handled automatically'); + }); + return; + } + box.content[hash] = msg; + showMessage(ctx, type, message); + }); } else { // Message has already been viewed by the user if (Object.keys(box.content).length === 0) { @@ -182,105 +299,6 @@ proxy.mailboxes = { CpNetflux.start(cfg); }; - // Send a message to someone else - var sendTo = function (ctx, type, msg, user, cb) { - if (!Crypto.Mailbox) { - return void cb({error: "chainpad-crypto is outdated and doesn't support mailboxes."}); - } - var keys = getMyKeys(ctx); - if (!keys) { return void cb({error: "missing asymmetric encryption keys"}); } - if (!user || !user.channel || !user.curvePublic) { return void cb({error: "no notification channel"}); } - - var crypto = Crypto.Mailbox.createEncryptor(keys); - var network = ctx.store.network; - - var ciphertext = crypto.encrypt(JSON.stringify({ - type: type, - content: msg - }), user.curvePublic); - - network.join(user.channel).then(function (wc) { - wc.bcast(ciphertext).then(function () { - cb(); - }); - }, function (err) { - cb({error: err}); - }); - }; - - var updateLastKnownHash = function (ctx, type) { - var m = Util.find(ctx, ['store', 'proxy', 'mailboxes', type]); - if (!m) { return; } - var box = ctx.boxes[type]; - if (!box) { return; } - - }; - - // Mark a message as read - var dismiss = function (ctx, data, cId, cb) { - var type = data.type; - var hash = data.hash; - var m = Util.find(ctx, ['store', 'proxy', 'mailboxes', type]); - if (!m) { return void cb({error: 'NOT_FOUND'}); } - var box = ctx.boxes[type]; - if (!box) { return void cb({error: 'NOT_LOADED'}); } - - // If the hash in in our history, get the index from the history: - // - if the index is 0, we can change our lastKnownHash - // - otherwise, just push to view - var idx; - if (box.history.some(function (el, i) { - if (hash === el) { - idx = i; - return true; - } - })) { - if (idx === 0) { - m.lastKnownHash = hash; - box.history.shift(); - delete box.content[hash]; - } else if (m.viewed.indexOf(hash) === -1) { - m.viewed.push(hash); - } - } - - // Clear data in memory if needed - // Check the "viewed" array to see if we're able to bump lastKnownhash more - var sliceIdx; - var lastKnownHash; - box.history.some(function (hash, i) { - var isViewed = m.viewed.indexOf(hash); - if (isViewed !== -1) { - sliceIdx = i + 1; - m.viewed.splice(isViewed, 1); - lastKnownHash = hash; - return false; - } - return true; - }); - - if (sliceIdx) { - box.history = box.history.slice(sliceIdx); - m.lastKnownHash = lastKnownHash; - } - - // Make sure we remove data about dismissed messages - Object.keys(box.content).forEach(function (h) { - if (box.history.indexOf(h) === -1 || m.viewed.indexOf(h) !== -1) { - delete box.content[h]; - } - }); - - Realtime.whenRealtimeSyncs(ctx.store.realtime, function () { - cb(); - ctx.emit('VIEWED', { - type: type, - hash: hash - }, ctx.clients.filter(function (clientId) { - return clientId !== cId; - })); - }); - }; var subscribe = function (ctx, data, cId, cb) { // Get existing notifications @@ -306,10 +324,13 @@ proxy.mailboxes = { ctx.clients.splice(idx, 1); }; - Mailbox.init = function (store, waitFor, emit) { + Mailbox.init = function (cfg, waitFor, emit) { var mailbox = {}; + var store = cfg.store; var ctx = { store: store, + pinPads: cfg.pinPads, + updateMetadata: cfg.updateMetadata, emit: emit, clients: [], boxes: {} @@ -317,7 +338,7 @@ proxy.mailboxes = { var mailboxes = store.proxy.mailboxes = store.proxy.mailboxes || {}; - initializeMailboxes(mailboxes); + initializeMailboxes(ctx, mailboxes); Object.keys(mailboxes).forEach(function (key) { if (TYPES.indexOf(key) === -1) { return; } @@ -346,6 +367,10 @@ proxy.mailboxes = { }); }; + mailbox.dismiss = function (data, cb) { + dismiss(ctx, data, '', cb); + }; + mailbox.sendTo = function (type, msg, user, cb) { sendTo(ctx, type, msg, user, cb); }; diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index aed0d5ab4..40c446c48 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -58,8 +58,8 @@ define([ ADD_SHARED_FOLDER: Store.addSharedFolder, LOAD_SHARED_FOLDER: Store.loadSharedFolderAnon, // Messaging - INVITE_FROM_USERLIST: Store.inviteFromUserlist, - ADD_DIRECT_MESSAGE_HANDLERS: Store.addDirectMessageHandlers, + ANSWER_FRIEND_REQUEST: Store.answerFriendRequest, + SEND_FRIEND_REQUEST: Store.sendFriendRequest, // Chat CHAT_COMMAND: Store.messenger.execCommand, // OnlyOffice diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 5c164a3eb..9a5ba5730 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -502,16 +502,11 @@ define([ }); // Messaging - sframeChan.on('Q_SEND_FRIEND_REQUEST', function (netfluxId, cb) { - Cryptpad.inviteFromUserlist(netfluxId, cb); + sframeChan.on('Q_SEND_FRIEND_REQUEST', function (data, cb) { + Cryptpad.messaging.sendFriendRequest(data, cb); }); - Cryptpad.messaging.onFriendRequest.reg(function (confirmText, cb) { - sframeChan.query('Q_INCOMING_FRIEND_REQUEST', confirmText, function (err, data) { - cb(data); - }); - }); - Cryptpad.messaging.onFriendComplete.reg(function (data) { - sframeChan.event('EV_FRIEND_REQUEST', data); + sframeChan.on('Q_ANSWER_FRIEND_REQUEST', function (data, cb) { + Cryptpad.messaging.answerFriendRequest(data, cb); }); // History @@ -954,9 +949,6 @@ define([ readOnly: readOnly, crypto: Crypto.createEncryptor(secret.keys), onConnect: function () { - var href = parsed.getUrl(); - // Add friends requests handlers when we have the final href - Cryptpad.messaging.addHandlers(href); if (window.location.hash && window.location.hash !== '#') { /*window.location = parsed.getUrl({ present: parsed.hashData.present, diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 76dcaa029..7081136c0 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -380,14 +380,24 @@ define([ funcs.mergeAnonDrive = function (cb) { ctx.sframeChan.query('Q_MERGE_ANON_DRIVE', null, cb); }; - // Friends - var pendingFriends = []; + + // Create friend request funcs.getPendingFriends = function () { - return pendingFriends.slice(); + return ctx.metadataMgr.getPrivateData().pendingFriends; + }; + funcs.sendFriendRequest = function (data, cb) { + ctx.sframeChan.query('Q_SEND_FRIEND_REQUEST', data, cb); + }; + // Friend requests received + var friendRequests = {}; + funcs.addFriendRequest = function (data) { + var curve = Util.find(data, ['content', 'msg', 'author']); + console.log(data); + console.log(curve); + friendRequests[curve] = data; }; - funcs.sendFriendRequest = function (netfluxId) { - ctx.sframeChan.query('Q_SEND_FRIEND_REQUEST', netfluxId, $.noop); - pendingFriends.push(netfluxId); + funcs.getFriendRequests = function () { + return JSON.parse(JSON.stringify(friendRequests)); }; // Feedback @@ -524,15 +534,6 @@ define([ UI.addTooltips(); - ctx.sframeChan.on('Q_INCOMING_FRIEND_REQUEST', function (confirmMsg, cb) { - UI.confirm(confirmMsg, cb, null, true); - }); - ctx.sframeChan.on('EV_FRIEND_REQUEST', function (data) { - var i = pendingFriends.indexOf(data.netfluxId); - if (i !== -1) { pendingFriends.splice(i, 1); } - UI.log(data.logText); - }); - ctx.sframeChan.on("EV_PAD_PASSWORD", function () { UIElements.displayPasswordPrompt(funcs); }); diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js index 8eb3b6c2d..d782622e5 100644 --- a/www/common/toolbar3.js +++ b/www/common/toolbar3.js @@ -8,10 +8,11 @@ define([ '/common/common-util.js', '/common/common-feedback.js', '/common/hyperscript.js', + '/common/notifications.js', '/common/messenger-ui.js', '/customize/messages.js', ], function ($, Config, ApiConfig, UIElements, UI, Hash, Util, Feedback, h, -MessengerUI, Messages) { +Notifications, MessengerUI, Messages) { var Common; var Bar = { @@ -232,7 +233,11 @@ MessengerUI, Messages) { // Display the userlist // Editors - var pendingFriends = Common.getPendingFriends(); + var pendingFriends = Common.getPendingFriends(); // Friend requests sent + var friendRequests = Common.getFriendRequests(); // Friend requests received + console.log(friendRequests); + var friendTo = +new Date() - (2 * 24 * 3600 * 1000); + friendTo = +new Date(); // XXX editUsersNames.forEach(function (data) { var name = data.name || Messages.anonymous; var $span = $('', {'class': 'cp-avatar'}); @@ -300,9 +305,21 @@ MessengerUI, Messages) { } } else if (Common.isLoggedIn() && data.curvePublic && !friends[data.curvePublic] && !priv.readOnly) { - if (pendingFriends.indexOf(data.netfluxId) !== -1) { + console.log(pendingFriends); + if (pendingFriends[data.curvePublic] && pendingFriends[data.curvePublic] > friendTo) { $('', {'class': 'cp-toolbar-userlist-friend'}).text(Messages.userlist_pending) .appendTo($rightCol); + } else if (friendRequests[data.curvePublic]) { + $('