diff --git a/www/common/outer/support.js b/www/common/outer/support.js index 97839ed65..f0b3f4d91 100644 --- a/www/common/outer/support.js +++ b/www/common/outer/support.js @@ -7,12 +7,13 @@ define([ '/common/common-util.js', '/common/common-hash.js', '/common/common-realtime.js', + '/common/pinpad.js', '/components/nthen/index.js', '/components/chainpad-crypto/crypto.js', 'chainpad-listmap', '/components/chainpad/chainpad.dist.js', 'chainpad-netflux' -], function (ApiConfig, Util, Hash, Realtime, nThen, Crypto, Listmap, ChainPad, CpNetflux) { +], function (ApiConfig, Util, Hash, Realtime, Pinpad, nThen, Crypto, Listmap, ChainPad, CpNetflux) { var Support = {}; // UTILS @@ -396,6 +397,7 @@ define([ cb(false); }); notifyClient(ctx, true, 'NEW_TICKET', data.channel); + if (ctx.adminRpc) { ctx.adminRpc.pin([data.channel], () => {}); } }); }); }; @@ -442,6 +444,43 @@ define([ // INITIALIZE ADMIN + var getPinList = function (ctx) { + if (!ctx.adminDoc || !ctx.adminRpc) { return; } + let adminChan = ctx.adminDoc.metadata && ctx.adminDoc.metadata.channel; + let doc = ctx.adminDoc.proxy; + let t = doc.tickets; + let list = [ + adminChan, + ...Object.keys(t.active), + ...Object.keys(t.pending), + ...Object.keys(t.closed) + ]; + return Util.deduplicateString(list).sort(); + }; + var initAdminRpc = function (ctx, _cb) { + let cb = Util.mkAsync(_cb); + let Nacl = Crypto.Nacl; + let proxy = ctx.store.proxy; + let curvePrivate = Util.find(proxy, ['mailboxes', 'support2', 'keys', 'curvePrivate']); + if (!curvePrivate) { return void cb('EFORBIDDEN'); } + let edPrivate, edPublic + try { + let pair = Nacl.sign.keyPair.fromSeed(Nacl.util.decodeBase64(curvePrivate)); + edPrivate = Nacl.util.encodeBase64(pair.secretKey); + edPublic = Nacl.util.encodeBase64(pair.publicKey); + } catch (e) { + return void cb(e); + } + Pinpad.create(ctx.store.network, { + edPublic: edPublic, + edPrivate: edPrivate + }, (e, call) => { + if (e) { return void cb(e); } + console.log("Support RPC ready, public key is ", edPublic); + ctx.adminRpc = call; + cb(); + }); + }; var loadAdminDoc = function (ctx, hash, cb) { var secret = Hash.getSecrets('support', hash); var listmapConfig = { @@ -467,6 +506,19 @@ define([ doc.tickets.pending = doc.tickets.pending || {}; ctx.adminRdyEvt.fire(); cb(); + + if (!ctx.adminRpc) { return; } + // Check pin list + let list = getPinList(ctx); + let local = Hash.hashChannelList(list); + ctx.adminRpc.getServerHash(function (e, hash) { + if (e) { return void console.warn(e); } + if (hash !== local) { + ctx.adminRpc.reset(list, function (e, hash) { + if (e) { console.warn(e); } + }); + } + }); }); }; @@ -487,6 +539,10 @@ define([ return void waitFor.abort(); } })); + }).nThen((waitFor) => { + initAdminRpc(ctx, waitFor((err) => { + if (err) { console.error('Support RPC not ready', err); } + })); }).nThen((waitFor) => { let seed = privateKey.slice(0,24); // XXX better way to get seed? let hash = Hash.getEditHashFromKeys({ diff --git a/www/moderation/inner.js b/www/moderation/inner.js index b4b1bf5b4..379ceda80 100644 --- a/www/moderation/inner.js +++ b/www/moderation/inner.js @@ -53,7 +53,10 @@ define([ Messages.support_pendingListHint = "List of tickets that may not be updated for a while but should not be closed"; Messages.support_privacyTitle = "Answer anonymously"; - Messages.support_privacyHint = "Check this option to reply as 'The Support Team' instead of your own usenrame"; + Messages.support_privacyHint = "Check this option to reply as 'The Support Team' instead of your own username"; + + Messages.support_notificationsTitle = "Disable notifications"; + Messages.support_notificationsHint = "Check this option to disable notifications on new or updated ticket"; var andThen = function (common, $container, linkedTicket) { const sidebar = Sidebar.create(common, 'support', $container); @@ -193,11 +196,13 @@ define([ let activeContainer, pendingContainer, closedContainer; var refreshAll = function () { - console.error('refresh'); refresh($(activeContainer), 'active'); refresh($(pendingContainer), 'pending'); refresh($(closedContainer), 'closed'); }; + let _refresh = Util.throttle(refreshAll, 500); + events.NEW_TICKET.reg(_refresh); + events.UPDATE_TICKET.reg(_refresh); // Make sidebar layout const categories = { @@ -215,9 +220,15 @@ define([ 'closed-list' ] }, + 'settings': { + icon: undefined, + content: [ + 'notifications' + ] + }, 'refresh': { icon: undefined, - action: refreshAll + onClick: refreshAll } }; @@ -242,13 +253,20 @@ define([ let div = closedContainer = h('div.cp-support-container'); // XXX block cb(div); }, { noTitle: true, noHint: true }); - - let _refresh = Util.throttle(refreshAll, 500); - events.NEW_TICKET.reg(_refresh); - events.UPDATE_TICKET.reg(_refresh); - refreshAll(); + sidebar.addCheckboxItem({ + key: 'notifications', + getState: () => APP.disableSupportNotif, + query: (val, setState) => { + common.setAttribute(['general', 'disableSupportNotif'], val, function (err) { + if (err) { val = APP.disableSupportNotif; } + APP.disableSupportNotif = val; + setState(val); + }); + } + }); + sidebar.makeLeftside(categories); }; @@ -273,6 +291,10 @@ define([ APP.$toolbar = $('#cp-toolbar'); sframeChan = common.getSframeChannel(); sframeChan.onReady(waitFor()); + }).nThen(function (waitFor) { + common.getAttribute(['general', 'disableSupportNotif'], waitFor(function (err, value) { + APP.disableSupportNotif = !!value; + })); }).nThen(function (/*waitFor*/) { createToolbar(); var metadataMgr = common.getMetadataMgr(); diff --git a/www/support/ui.js b/www/support/ui.js index aa5e37643..44459c7cf 100644 --- a/www/support/ui.js +++ b/www/support/ui.js @@ -171,7 +171,8 @@ define([ $(button).click(cb); } - var cancel = title ? h('button.btn.btn-secondary', Messages.cancel) : undefined; + var cancel = title ? h('button.btn.btn-secondary.cp-support-reply-cancel', Messages.cancel) + : undefined; var category = h('input.cp-support-form-category', { type: 'hidden', @@ -318,6 +319,7 @@ define([ var adminActions; var adminClasses = ''; var adminOpen; + var ticket; if (ctx.isAdmin) { // Admin custom style adminClasses = `.cp-not-loaded`; @@ -345,6 +347,7 @@ define([ $ticket.toggleClass('cp-not-loaded', true); return onHide(ticket, id, content, function () { visible = false; + $(ticket).find('.cp-support-reply-cancel').click(); $show.text(Messages.admin_support_open); $show.prop('disabled', ''); }); @@ -362,7 +365,7 @@ define([ let isPremium = content.premium ? '.cp-support-ispremium' : ''; var name = Util.fixHTML(content.author) || Messages.anonymous; - var ticket = h(`div.cp-support-list-ticket${adminClasses}`, { + ticket = h(`div.cp-support-list-ticket${adminClasses}`, { 'data-id': id }, [ h('div.cp-support-ticket-header', [