From 11ddb96422d7ce6b675046a88a84eafcbfec80b7 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 10 Dec 2019 13:07:57 +0100 Subject: [PATCH] Mute friends: dismiss notifications and fix UI issues --- www/common/common-interface.js | 1 + www/common/common-messaging.js | 1 + www/common/common-ui-elements.js | 37 +++++++++++++++++-- www/common/messenger-ui.js | 53 +++++++++++++++++----------- www/common/outer/async-store.js | 4 +++ www/common/outer/mailbox-handlers.js | 34 ++++++++++++++++++ www/common/outer/messenger.js | 40 +++++++++++++++------ 7 files changed, 136 insertions(+), 34 deletions(-) diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 8efc7397a..3c758a88b 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -580,6 +580,7 @@ define([ }]; var modal = dialog.customModal(content, {buttons: buttons}); UI.openCustomModal(modal); + return modal; }; UI.log = function (msg) { diff --git a/www/common/common-messaging.js b/www/common/common-messaging.js index 91322f526..feb3d79d3 100644 --- a/www/common/common-messaging.js +++ b/www/common/common-messaging.js @@ -84,6 +84,7 @@ define([ var myData = createData(store.proxy, false); if (store.proxy.friends) { store.proxy.friends.me = myData; + delete store.proxy.friends.me.channel; } if (store.modules['team']) { store.modules['team'].updateMyData(myData); diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index eb8bb21a2..992428899 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -3803,10 +3803,20 @@ define([ }); }; + var modal; + var mute = UIElements.createMuteButton(common, msg.content, function () { + // Mute = auto-reject friend request + var $modal = modal && $(modal) && $(modal).closest('div.alertify'); + if ($modal && $modal.length && $modal[0].closeModal) { + $modal[0].closeModal(function () {}); + } + return void todo(false); // XXX false is reject. We can also "dismiss"... + }); var content = h('div.cp-share-modal', [ - setHTML(h('p'), text) + setHTML(h('p'), text), + h('p', mute) ]); - UI.proposal(content, todo); + modal = UI.proposal(content, todo); }; UIElements.displayAddOwnerModal = function (common, data) { @@ -4148,5 +4158,28 @@ define([ UI.proposal(div, todo); }; + UIElements.createMuteButton = function (common, data, cb) { + cb = cb || function () {}; + var button = h('i.fa.fa-bell-slash-o', { + title: Messages.notifications_muteUserTitle + }); + var module = common.makeUniversal('messenger'); + $(button).click(function () { + UI.confirm(Messages.notifications_muteUserConfirm, function (yes) { + if (!yes) { return; } + module.execCommand('MUTE_USER', { + curvePublic: data.curvePublic, + name: data.displayName || data.name, + avatar: data.avatar + }, function (e) { + cb(e); + if (e) { return void UI.warn(Messages.error); } + UI.log(Messages.success); + }); + }); + }); + return button; + }; + return UIElements; }); diff --git a/www/common/messenger-ui.js b/www/common/messenger-ui.js index 7c437969b..9ce795f4e 100644 --- a/www/common/messenger-ui.js +++ b/www/common/messenger-ui.js @@ -186,7 +186,7 @@ define([ markup.message = function (msg) { if (msg.type !== 'MSG') { return; } var curvePublic = msg.author; - var name = typeof msg.name !== "undefined" ? + var name = (typeof msg.name !== "undefined" || !contactsData[msg.author]) ? (msg.name || Messages.anonymous) : contactsData[msg.author].displayName; var d = msg.time ? new Date(msg.time) : undefined; @@ -548,7 +548,7 @@ define([ UI.confirm(content, function (yes) { if (!yes) { return; } var mute = Util.isChecked($(content).find('#cp-contacts-mute')); - muteUser(friend); + if (mute) { muteUser(friend); } removeFriend(curvePublic); // TODO remove friend from userlist ui // FIXME seems to trigger EJOINED from netflux-websocket (from server); @@ -806,37 +806,27 @@ define([ // var onJoinRoom // var onLeaveRoom - - var ready = false; - var onMessengerReady = function () { - if (isApp) { return; } - if (ready) { return; } - ready = true; - - execCommand('GET_ROOMS', null, function (err, rooms) { - if (err) { return void console.error(err); } - - debug('rooms: ' + JSON.stringify(rooms)); - rooms.forEach(initializeRoom); - }); - + var updateMutedList = function () { execCommand('GET_MUTED_USERS', null, function (err, muted) { if (err) { return void console.error(err); } - if (!muted || Object.keys(muted).length === 0) { return; } - var $button = $userlist.find('.cp-app-contacts-muted-button'); + + if (!muted || Object.keys(muted).length === 0) { + $button.hide(); + return; + } + var rows = Object.keys(muted).map(function (curve) { var data = muted[curve]; var avatar = h('div.cp-avatar'); var button = h('td', h('i.fa.fa-times', {title: Messages.contacts_unmute})); UIElements.displayAvatar(common, $(avatar), data.avatar, data.name); $(button).click(function () { - execCommand('UNMUTE_USER', { - curvePublic: curve, - }, function (e /*, removed */) { + execCommand('UNMUTE_USER', curve, function (e, data) { if (e) { return void console.error(e); } $(button).closest('tr').remove(); + if (!data) { $button.hide(); } }); }); return h('tr', [ @@ -849,10 +839,27 @@ define([ h('p', Messages.contacts_mutedUsers), h('table', rows) ]); + $button.off('click'); $button.click(function () { UI.alert(content); }).show(); }); + }; + + var ready = false; + var onMessengerReady = function () { + if (isApp) { return; } + if (ready) { return; } + ready = true; + + execCommand('GET_ROOMS', null, function (err, rooms) { + if (err) { return void console.error(err); } + + debug('rooms: ' + JSON.stringify(rooms)); + rooms.forEach(initializeRoom); + }); + + updateMutedList(); $container.removeClass('cp-app-contacts-initializing'); }; @@ -930,6 +937,10 @@ define([ onUpdateData(data); return; } + if (cmd === 'UPDATE_MUTED') { + updateMutedList(); + return; + } if (cmd === 'MESSAGE') { onMessage(data); return; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 0cf362e5d..0a44dfc76 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1253,6 +1253,10 @@ define([ if (friend) { return void cb({error: 'ALREADY_FRIEND'}); } if (!data.notifications || !data.curvePublic) { return void cb({error: 'INVALID_USER'}); } + // Unmute this user when we send them a friend request + var muted = store.proxy.mutedUsers || {}; + delete muted[data.curvePublic]; + store.proxy.friends_pending = store.proxy.friends_pending || {}; var twoDaysAgo = +new Date() - (2 * 24 * 3600 * 1000); diff --git a/www/common/outer/mailbox-handlers.js b/www/common/outer/mailbox-handlers.js index 00f990eb3..6e10cd5a2 100644 --- a/www/common/outer/mailbox-handlers.js +++ b/www/common/outer/mailbox-handlers.js @@ -12,6 +12,13 @@ define([ var handlers = {}; var removeHandlers = {}; + var isMuted = function (ctx, data) { + var muted = ctx.store.proxy.mutedUsers || {}; + var curvePublic = Util.find(data, ['msg', 'author']); + if (!curvePublic) { return false; } + return Boolean(muted[curvePublic]); + }; + // Store the friend request displayed to avoid duplicates var friendRequest = {}; handlers['FRIEND_REQUEST'] = function (ctx, box, data, cb) { @@ -21,6 +28,8 @@ define([ return void cb(true); } + if (isMuted(ctx, data)) { return void cb(true); } + // Don't show duplicate friend request: if we already have a friend request // in memory from the same user, dismiss the new one if (friendRequest[data.msg.author]) { return void cb(true); } @@ -30,10 +39,22 @@ define([ // If the user is already in our friend list, automatically accept the request if (Messaging.getFriend(ctx.store.proxy, data.msg.author) || ctx.store.proxy.friends_pending[data.msg.author]) { + delete ctx.store.proxy.friends_pending[data.msg.author]; Messaging.acceptFriendRequest(ctx.store, data.msg.content, function (obj) { if (obj && obj.error) { return void cb(); } + Messaging.addToFriendList({ + proxy: ctx.store.proxy, + realtime: ctx.store.realtime, + pinPads: ctx.pinPads + }, data.msg.content, function (err) { + if (err) { console.error(err); } + if (ctx.store.messenger) { + ctx.store.messenger.onFriendAdded(data.msg.content); + } + }); + ctx.updateMetadata(); cb(true); }); return; @@ -170,6 +191,8 @@ define([ var content = msg.content; // content.name, content.title, content.href, content.password + if (isMuted(ctx, data)) { return void cb(true); } + var channel = Hash.hrefToHexChannelId(content.href, content.password); var parsed = Hash.parsePadUrl(content.href); var mode = parsed.hashData && parsed.hashData.mode || 'n/a'; @@ -212,6 +235,9 @@ define([ supportMessage = true; cb(); }; + removeHandlers['SUPPORT_MESSAGE'] = function () { + supportMessage = false; + }; // Incoming edit rights request: add data before sending it to inner handlers['REQUEST_PAD_ACCESS'] = function (ctx, box, data, cb) { @@ -220,6 +246,8 @@ define([ if (msg.author !== content.user.curvePublic) { return void cb(true); } + if (isMuted(ctx, data)) { return void cb(true); } + var channel = content.channel; var res = ctx.store.manager.findChannel(channel); @@ -270,6 +298,9 @@ define([ var content = msg.content; if (msg.author !== content.user.curvePublic) { return void cb(true); } + + if (isMuted(ctx, data)) { return void cb(true); } + if (!content.teamChannel && !(content.href && content.title && content.channel)) { console.log('Remove invalid notification'); return void cb(true); @@ -327,6 +358,9 @@ define([ var content = msg.content; if (msg.author !== content.user.curvePublic) { return void cb(true); } + + if (isMuted(ctx, data)) { return void cb(true); } + if (!content.team) { console.log('Remove invalid notification'); return void cb(true); diff --git a/www/common/outer/messenger.js b/www/common/outer/messenger.js index 90884198e..5d4c67135 100644 --- a/www/common/outer/messenger.js +++ b/www/common/outer/messenger.js @@ -458,12 +458,22 @@ define([ } }; + var getAllClients = function (ctx) { + var all = []; + Array.prototype.push.apply(all, ctx.friendsClients); + Object.keys(ctx.channels).forEach(function (id) { + Array.prototype.push.apply(all, ctx.channels[id].clients); + }); + return Util.deduplicateString(all); + }; + var muteUser = function (ctx, data, _cb) { var cb = Util.once(Util.mkAsync(_cb)); var proxy = ctx.store.proxy; var muted = proxy.mutedUsers = proxy.mutedUsers || {}; if (muted[data.curvePublic]) { return void cb(); } muted[data.curvePublic] = data; + ctx.emit('UPDATE_MUTED', null, getAllClients(ctx)); cb(); }; var unmuteUser = function (ctx, curvePublic, _cb) { @@ -471,7 +481,8 @@ define([ var proxy = ctx.store.proxy; var muted = proxy.mutedUsers = proxy.mutedUsers || {}; delete muted[curvePublic]; - cb(); + ctx.emit('UPDATE_MUTED', null, getAllClients(ctx)); + cb(Object.keys(muted).length); }; var getMutedUsers = function (ctx, cb) { var proxy = ctx.store.proxy; @@ -687,7 +698,14 @@ define([ nThen(function (waitFor) { // Load or get all friends channels Object.keys(friends).forEach(function (key) { - if (key === 'me') { return; } + if (key === 'me') { + // At some point a bug inserted a friend's channel into our "me" data. + // This led to displaying our name instead of our friend's name in the + // contacts app. The following line is here to prevent this issue to happen + // again. + delete friends.me.channel; + return; + } var friend = clone(friends[key]); if (typeof(friend) !== 'object') { return; } if (!friend.channel) { return; } @@ -910,15 +928,6 @@ define([ }); }; - var getAllClients = function (ctx) { - var all = []; - Array.prototype.push.apply(all, ctx.friendsClients); - Object.keys(ctx.channels).forEach(function (id) { - Array.prototype.push.apply(all, ctx.channels[id].clients); - }); - return Util.deduplicateString(all); - }; - Msg.init = function (cfg, waitFor, emit) { var messenger = {}; var store = cfg.store; @@ -934,6 +943,9 @@ define([ range_requests: {} }; + store.proxy.on('change', ['mutedUsers'], function () { + ctx.emit('UPDATE_MUTED', null, getAllClients(ctx)); + }); ctx.store.network.on('message', function(msg, sender) { onDirectMessage(ctx, msg, sender); @@ -965,6 +977,12 @@ define([ var channel = friend.channel; if (!channel) { return; } + // Already friend? don't load the channel a second time + var chanId = friend.channel; + var chan = ctx.channels[chanId]; + if (chan) { return; } + + // Load the channel and add the friend to the contacts app loadFriend(ctx, null, friend, function () { emit('FRIEND', { curvePublic: friend.curvePublic,