From 9cb1a059f240e5f1ffc1b995d9dc762d1a0da131 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 22 May 2019 18:03:52 +0200 Subject: [PATCH] Improve friend request process and UI --- customize.dist/src/less2/include/toolbar.less | 21 +++++++ www/common/common-messaging.js | 1 - www/common/common-messenger.js | 59 +++++++------------ www/common/common-ui-elements.js | 31 ++++++++-- www/common/notifications.js | 50 ++++++++++++++++ www/common/outer/async-store.js | 47 +++++++++++++-- www/common/outer/mailbox-handlers.js | 52 ++++++++++++++++ www/common/outer/mailbox.js | 4 +- www/common/sframe-common-mailbox.js | 7 ++- www/common/sframe-common.js | 11 +++- www/common/toolbar3.js | 40 +++++++++---- 11 files changed, 257 insertions(+), 66 deletions(-) create mode 100644 www/common/notifications.js create mode 100644 www/common/outer/mailbox-handlers.js diff --git a/customize.dist/src/less2/include/toolbar.less b/customize.dist/src/less2/include/toolbar.less index ec0e3aa60..0cecd179f 100644 --- a/customize.dist/src/less2/include/toolbar.less +++ b/customize.dist/src/less2/include/toolbar.less @@ -898,6 +898,27 @@ } .cp-toolbar-notifications { margin-left: 10px; + .cp-notifications-empty { + color: black; + padding: 5px; + } + button { + position: relative; + &.fa-bell-o { + cursor: default; + } + .cp-dropdown-button-title { + position: absolute; + bottom: 0; + right: 0; + font-size: 14px; + border: 1px solid; + border-radius: 50%; + width: 20px; + height: 20px; + line-height: 16px; + } + } } .cp-toolbar-link { display: inline-flex; diff --git a/www/common/common-messaging.js b/www/common/common-messaging.js index a53f0c3b2..d8cf76c43 100644 --- a/www/common/common-messaging.js +++ b/www/common/common-messaging.js @@ -59,7 +59,6 @@ define([ curvePublic: data.curvePublic }, function (obj) { cb(obj); - if (obj && obj.error) { return void cb(obj); } }); }; Msg.addToFriendList = function (cfg, data, cb) { diff --git a/www/common/common-messenger.js b/www/common/common-messenger.js index c333112d9..f34c177ca 100644 --- a/www/common/common-messenger.js +++ b/www/common/common-messenger.js @@ -68,7 +68,7 @@ define([ }); }; - Msg.messenger = function (store) { + Msg.messenger = function (store, updateMetadata) { var messenger = { handlers: { event: [] @@ -97,6 +97,7 @@ define([ stack.push(f); }; + var allowFriendsChannels = false; var channels = messenger.channels = {}; var joining = {}; @@ -301,7 +302,10 @@ define([ if (!proxy.friends) { return; } var friends = proxy.friends; delete friends[curvePublic]; - Realtime.whenRealtimeSyncs(realtime, cb); + Realtime.whenRealtimeSyncs(realtime, function () { + updateMetadata(); + cb(); + }); }; var pushMsg = function (channel, cryptMsg) { @@ -771,45 +775,9 @@ define([ openChannel(data); }; - // Detect friends changes made in another worker - proxy.on('change', ['friends'], function (o, n, p) { - var curvePublic; - if (o === undefined) { - // new friend added - curvePublic = p.slice(-1)[0]; - - // Load channel - var friend = friends[curvePublic]; - if (typeof(friend) !== 'object') { return; } - var channel = friend.channel; - if (!channel) { return; } - loadFriend(friend, function () { - emit('FRIEND', { - curvePublic: curvePublic, - }); - }); - return; - } - - if (typeof(n) === 'undefined') { - // Handled by .on('remove') - return; - } - }).on('remove', ['friends'], function (o, p) { - var curvePublic = p[1]; - if (!curvePublic) { return; } - if (p[2] !== 'channel') { return; } - var channel = channels[o]; - channel.wc.leave(Types.unfriend); - delete channels[channel.id]; - emit('UNFRIEND', { - curvePublic: curvePublic, - fromMe: true - }); - }); - // Friend added in our contacts in the current worker messenger.onFriendAdded = function (friendData) { + if (!allowFriendsChannels) { return; } var friend = friends[friendData.curvePublic]; if (typeof(friend) !== 'object') { return; } var channel = friend.channel; @@ -820,10 +788,23 @@ define([ }); }); }; + messenger.onFriendRemoved = function (curvePublic, chanId) { + var channel = channels[chanId]; + if (!channel) { return; } + if (channel.wc) { + channel.wc.leave(Types.unfriend); + } + delete channels[channel.id]; + emit('UNFRIEND', { + curvePublic: curvePublic, + fromMe: true + }); + }; var ready = false; var initialized = false; var init = function () { + allowFriendsChannels = true; if (initialized) { return; } initialized = true; var friends = getFriendList(proxy); diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 3ba27f86f..aead1bcfd 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2610,7 +2610,8 @@ define([ UIElements.displayFriendRequestModal = function (common, data) { var msg = data.content.msg; var text = Messages._getKey('contacts_request', [msg.content.displayName]); - UI.confirm(text, function (yes) { + + var todo = function (yes) { common.getSframeChannel().query("Q_ANSWER_FRIEND_REQUEST", { data: data, value: yes @@ -2621,10 +2622,32 @@ define([ } UI.log(Messages.contacts_added); }); + }; + + var content = h('div.cp-share-modal', [ + setHTML(h('p'), text) + ]); + var buttons = [{ + name: Messages.cancel, // XXX "later"? + onClick: function () {}, + keys: [27] + }, { + className: 'primary', + name: "Accept (Enter)", // XXX + onClick: function () { + todo(true); + }, + keys: [13] }, { - ok: 'Accept', // XXX - cancel: 'Ignore the request' // XXX - }, true); + className: 'primary', + name: "Ignore the request", // XXX + onClick: function () { + todo(false); + }, + keys: [[13, 'ctrl']] + }]; + var modal = UI.dialog.customModal(content, {buttons: buttons}); + UI.openCustomModal(modal); }; return UIElements; diff --git a/www/common/notifications.js b/www/common/notifications.js new file mode 100644 index 000000000..570795efc --- /dev/null +++ b/www/common/notifications.js @@ -0,0 +1,50 @@ +define([ + 'jquery', + '/common/hyperscript.js', + '/common/common-ui-elements.js' +], function ($, h, UIElements) { + + var handlers = {}; + + handlers['FRIEND_REQUEST'] = function (common, data, el) { + var content = data.content; + var msg = content.msg; + + // Check authenticity + if (msg.author !== msg.content.curvePublic) { return; } + + common.addFriendRequest(data); + + // Display the notification + $(el).find('.cp-notification-dismiss').attr('title', 'IGNORE').css('display', 'flex'); // XXX + $(el).find('.cp-notification-content').addClass("cp-clickable"); + $(el).find('.cp-notification-content p') + .html('New friend request: '+msg.content.displayName+'') // XXX + .click(function () { + UIElements.displayFriendRequestModal(common, data); + }); + }; + + handlers['ACCEPT_FRIEND_REQUEST'] = function (common, data, el) { + var content = data.content; + var msg = content.msg; + $(el).find('.cp-notification-content p') + .html('Friend request accepted: '+msg.content.displayName+''); + $(el).find('.cp-notification-dismiss').css('display', 'flex'); + }; + + return { + add: function (common, data, el) { + var type = data.content.msg.type; + + if (handlers[type]) { + handlers[type](common, data, el); + } else { + $(el).find('.cp-notification-dismiss').css('display', 'flex'); + } + }, + remove: function (common, data) { + common.removeFriendRequest(data.hash); + }, + }; +}); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index bcf3685ec..0e0d6fe68 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -639,7 +639,7 @@ define([ /*store.mailbox.post('notifications', 'NAME_CHANGED', { old: store.proxy[Constants.displayNameKey], new: value - });*/ + }); Object.keys(store.proxy.friends).forEach(function (curve) { var f = store.proxy.friends[curve]; if (!f.notifications) { return; } @@ -653,7 +653,7 @@ define([ if (obj && obj.error) { return void console.error(obj.error); } console.log('notif sent to '+f); }); - }); + });*/ } store.proxy[Constants.displayNameKey] = value; broadcast([clientId], "UPDATE_METADATA"); @@ -911,7 +911,6 @@ define([ // Messaging (manage friends from the userlist) 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'}); } @@ -926,6 +925,7 @@ define([ }, cb); }; + // If we accept the request, add the friend to the list if (value) { Messaging.acceptFriendRequest(store, msg.content, function (obj) { if (obj && obj.error) { return void cb(obj); } @@ -934,6 +934,9 @@ define([ realtime: store.realtime, pinPads: function (data, cb) { Store.pinPads(null, data, cb); }, }, msg.content, function (err) { + if (store.messenger) { + store.messenger.onFriendAdded(msg.content); + } broadcast([], "UPDATE_METADATA"); if (err) { return void cb({error: err}); } dismiss(cb); @@ -941,6 +944,7 @@ define([ }); return; } + // Otherwise, just remove the notification dismiss(); }; Store.sendFriendRequest = function (clientId, data, cb) { @@ -1445,7 +1449,9 @@ define([ }; var loadMessenger = function () { if (AppConfig.availablePadTypes.indexOf('contacts') === -1) { return; } - var messenger = store.messenger = Messenger.messenger(store); + var messenger = store.messenger = Messenger.messenger(store, function () { + broadcast([], "UPDATE_METADATA"); + }); messenger.on('event', function (ev, data) { sendMessengerEvent('CHAT_EVENT', { ev: ev, @@ -1496,6 +1502,18 @@ define([ }); }; + var cleanFriendRequests = function () { + try { + if (!store.proxy.friends_pending) { return; } + var twoDaysAgo = +new Date() - (2 * 24 * 3600 * 1000); // XXX + Object.keys(store.proxy.friends_pending).forEach(function (curve) { + if (store.proxy.friends_pending[curve] < twoDaysAgo) { + delete store.proxy.friends_pending[curve]; + } + }); + } catch (e) {} + }; + ////////////////////////////////////////////////////////////////// /////////////////////// Init ///////////////////////////////////// ////////////////////////////////////////////////////////////////// @@ -1591,6 +1609,7 @@ define([ loadCursor(); loadOnlyOffice(); loadMailbox(waitFor); + cleanFriendRequests(); }).nThen(function () { var requestLogin = function () { broadcast([], "REQUEST_LOGIN"); @@ -1644,14 +1663,32 @@ define([ // Trigger userlist update when the avatar has changed broadcast([], "UPDATE_METADATA"); }); - proxy.on('change', ['friends'], function () { + proxy.on('change', ['friends'], function (o, n, p) { // Trigger userlist update when the friendlist has changed broadcast([], "UPDATE_METADATA"); + + if (!store.messenger) { return; } + if (o !== undefined) { return; } + var curvePublic = p.slice(-1)[0]; + var friend = proxy.friends && proxy.friends[curvePublic]; + store.messenger.onFriendAdded(friend); + }); + proxy.on('remove', ['friends'], function (o, p) { + broadcast([], "UPDATE_METADATA"); + + if (!store.messenger) { return; } + var curvePublic = p[1]; + if (!curvePublic) { return; } + if (p[2] !== 'channel') { return; } + store.messenger.onFriendRemoved(curvePublic, o); }); proxy.on('change', ['friends_pending'], function () { // Trigger userlist update when the friendlist has changed broadcast([], "UPDATE_METADATA"); }); + proxy.on('remove', ['friends_pending'], function () { + broadcast([], "UPDATE_METADATA"); + }); proxy.on('change', ['settings'], function () { broadcast([], "UPDATE_METADATA"); }); diff --git a/www/common/outer/mailbox-handlers.js b/www/common/outer/mailbox-handlers.js new file mode 100644 index 000000000..c0b48f847 --- /dev/null +++ b/www/common/outer/mailbox-handlers.js @@ -0,0 +1,52 @@ +define([ + '/common/common-messaging.js', +], function (Messaging) { + + var handlers = {}; + + handlers['FRIEND_REQUEST'] = function (ctx, data, cb) { + if (data.msg.author === data.msg.content.curvePublic && + Messaging.getFriend(ctx.store.proxy, data.msg.author)) { + Messaging.acceptFriendRequest(ctx.store, data.msg.content, function (obj) { + if (obj && obj.error) { return void cb(); } + cb(true); + }); + return; + } + cb(); + }; + handlers['ACCEPT_FRIEND_REQUEST'] = function (ctx, box, data, cb) { + // Our friend request was accepted. + // Make sure we really sent it + if (!ctx.store.proxy.friends_pending[data.msg.author]) { return void cb(); } + // And add the friend + Messaging.addToFriendList({ + proxy: ctx.store.proxy, + realtime: ctx.store.realtime, + pinPads: ctx.pinPads + }, data.msg.content, function (err) { + if (err) { console.error(err); } + delete ctx.store.proxy.friends_pending[data.msg.author]; + if (ctx.store.messenger) { + ctx.store.messenger.onFriendAdded(data.msg.content); + } + ctx.updateMetadata(); + }); + cb(); + }; + + return function (ctx, box, data, cb) { + var type = data.msg.type; + + if (handlers[type]) { + try { + handlers[type](ctx, box, data, cb); + } catch (e) { + cb(); + } + } else { + cb(); + } + }; +}); + diff --git a/www/common/outer/mailbox.js b/www/common/outer/mailbox.js index 0b4ce7367..f273ca5a5 100644 --- a/www/common/outer/mailbox.js +++ b/www/common/outer/mailbox.js @@ -347,11 +347,11 @@ proxy.mailboxes = { if (BLOCKING_TYPES.indexOf(key) === -1) { openChannel(ctx, key, m, function () { updateLastKnownHash(ctx, key); - console.log(key + ' mailbox is ready'); + //console.log(key + ' mailbox is ready'); }); } else { openChannel(ctx, key, m, waitFor(function () { - console.log(key + ' mailbox is ready'); + //console.log(key + ' mailbox is ready'); })); } }); diff --git a/www/common/sframe-common-mailbox.js b/www/common/sframe-common-mailbox.js index 4aca98e56..bf0cee5cc 100644 --- a/www/common/sframe-common-mailbox.js +++ b/www/common/sframe-common-mailbox.js @@ -3,9 +3,10 @@ define([ '/common/common-util.js', '/common/common-interface.js', '/common/common-ui-elements.js', + '/common/notifications.js', '/common/hyperscript.js', '/customize/messages.js' -], function ($, Util, UI, UIElements, h, Messages) { +], function ($, Util, UI, UIElements, Notifications, h, Messages) { var Mailbox = {}; Messages = Messages; // XXX @@ -87,6 +88,7 @@ define([ var todo = function (f) { try { var el = createElement(data); + Notifications.add(Common, data, el); f(data, el); } catch (e) { console.error(e); @@ -103,6 +105,7 @@ define([ onViewedHandlers.forEach(function (f) { try { f(data); + Notifications.remove(Common, data); } catch (e) { console.error(e); } @@ -165,7 +168,7 @@ define([ }); execCommand('SUBSCRIBE', null, function () { - console.log('subscribed'); + //console.log('subscribed'); }); return mailbox; diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 7081136c0..38ea38f27 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -392,10 +392,17 @@ define([ var friendRequests = {}; funcs.addFriendRequest = function (data) { var curve = Util.find(data, ['content', 'msg', 'author']); - console.log(data); - console.log(curve); friendRequests[curve] = data; }; + funcs.removeFriendRequest = function (hash) { + Object.keys(friendRequests).some(function (curve) { + var h = Util.find(friendRequests[curve], ['content', 'hash']); + if (h === hash) { + delete friendRequests[curve]; + return true; + } + }); + }; funcs.getFriendRequests = function () { return JSON.parse(JSON.stringify(friendRequests)); }; diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js index d782622e5..d21b3f556 100644 --- a/www/common/toolbar3.js +++ b/www/common/toolbar3.js @@ -8,11 +8,10 @@ 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, -Notifications, MessengerUI, Messages) { +MessengerUI, Messages) { var Common; var Bar = { @@ -235,9 +234,8 @@ Notifications, MessengerUI, Messages) { // Editors 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 + //friendTo = +new Date(); // XXX editUsersNames.forEach(function (data) { var name = data.name || Messages.anonymous; var $span = $('', {'class': 'cp-avatar'}); @@ -305,7 +303,6 @@ Notifications, MessengerUI, Messages) { } } else if (Common.isLoggedIn() && data.curvePublic && !friends[data.curvePublic] && !priv.readOnly) { - console.log(pendingFriends); if (pendingFriends[data.curvePublic] && pendingFriends[data.curvePublic] > friendTo) { $('', {'class': 'cp-toolbar-userlist-friend'}).text(Messages.userlist_pending) .appendTo($rightCol); @@ -950,10 +947,11 @@ Notifications, MessengerUI, Messages) { return $userAdmin; }; - var createNotifications = function (toolbar) { - console.log(Common.mailbox); + var createNotifications = function (toolbar, config) { var $notif = toolbar.$top.find('.'+NOTIFICATIONS_CLS).show(); - var div = h('div.cp-notifications-container'); + var div = h('div.cp-notifications-container', [ + h('div.cp-notifications-empty', "Nothing new here") // XXX + ]); var pads_options = [div]; var dropdownConfig = { text: '', // Button initial text @@ -963,15 +961,35 @@ Notifications, MessengerUI, Messages) { common: Common }; var $newPadBlock = UIElements.createDropdown(dropdownConfig); - $newPadBlock.find('button').attr('title', Messages.mailbox_title); // XXX - $newPadBlock.find('button').addClass('fa fa-bell-o'); + var $button = $newPadBlock.find('button'); + $button.attr('title', Messages.mailbox_title); // XXX + $button.addClass('fa fa-bell-o'); + var $n = $button.find('.cp-dropdown-button-title').hide(); + var $empty = $(div).find('.cp-notifications-empty'); + + var refresh = function () { + updateUserList(toolbar, config); + var n = $(div).find('.cp-notification').length; + $button.removeClass('fa-bell-o').removeClass('fa-bell'); + if (n === 0) { + $empty.show(); + $n.hide(); + return void $button.addClass('fa-bell-o'); + } + $empty.hide(); + $n.text(n).show(); + $button.addClass('fa-bell'); + }; Common.mailbox.subscribe({ onMessage: function (data, el) { if (el) { - Notifications(Common, data, el); div.appendChild(el); } + refresh(); + }, + onViewed: function (data) { + refresh(); } });