From 07c90b6a94edea51430cbab4ee4a87d9924e13ac Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 21 May 2021 13:39:33 +0200 Subject: [PATCH] View responses --- .../src/less2/include/colortheme-dark.less | 4 + .../src/less2/include/colortheme.less | 5 + www/common/cryptpad-common.js | 4 +- www/form/app-form.less | 66 ++++++++- www/form/inner.js | 133 ++++++++++++++++-- www/form/main.js | 31 ++-- 6 files changed, 218 insertions(+), 25 deletions(-) diff --git a/customize.dist/src/less2/include/colortheme-dark.less b/customize.dist/src/less2/include/colortheme-dark.less index 3443aaeb0..875ad004b 100644 --- a/customize.dist/src/less2/include/colortheme-dark.less +++ b/customize.dist/src/less2/include/colortheme-dark.less @@ -427,3 +427,7 @@ @cp_calendar-now: @cryptpad_color_brand_300; @cp_calendar-now-fg: @cryptpad_color_grey_800; +// Forms +@cp_forms-bg1: @cryptpad_color_grey_800; +@cp_forms-bg2: @cryptpad_color_grey_900; +@cp_forms-border: @cryptpad_color_grey_800; diff --git a/customize.dist/src/less2/include/colortheme.less b/customize.dist/src/less2/include/colortheme.less index e56676213..012a66239 100644 --- a/customize.dist/src/less2/include/colortheme.less +++ b/customize.dist/src/less2/include/colortheme.less @@ -426,3 +426,8 @@ @cp_calendar-border: @cryptpad_color_grey_300; @cp_calendar-now: @cryptpad_color_brand; @cp_calendar-now-fg: @cryptpad_color_grey_200; + +// Forms +@cp_forms-bg1: @cryptpad_color_grey_200; +@cp_forms-bg2: @cryptpad_color_grey_100; +@cp_forms-border: @cryptpad_color_grey_200; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 173aa9c9f..8419db9ba 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -69,7 +69,7 @@ define([ }, cb); }; - common.getAccessKeys = function (cb) { + common.getAccessKeys = function (cb, opts) { var keys = []; Nthen(function (waitFor) { // Push account keys @@ -84,6 +84,7 @@ define([ }); } catch (e) { console.error(e); } })); + // Push teams keys postMessage("GET", { key: ['teams'], @@ -92,6 +93,7 @@ define([ Object.keys(obj || {}).forEach(function (id) { var t = obj[id]; var _keys = t.keys.drive || {}; + _keys.id = id; if (!_keys.edPrivate) { return; } keys.push(t.keys.drive); }); diff --git a/www/form/app-form.less b/www/form/app-form.less index 996de4ad7..ee62e2c89 100644 --- a/www/form/app-form.less +++ b/www/form/app-form.less @@ -20,6 +20,17 @@ overflow: hidden; } + &.cp-app-form-results { + div.cp-form-creator-content, .cp-app-form-button-results { + display: none !important; + } + } + &:not(.cp-app-form-results) { + div.cp-form-creator-results, .cp-app-form-button-creator { + display: none !important; + } + } + #cp-app-form-container { display: flex; flex: 1; @@ -34,8 +45,13 @@ display: flex; flex-flow: column; width: 300px; + .cp-form-creator-types { + margin-top: 20px; + display: flex; + flex-flow: column; + } } - div.cp-form-creator-content { + div.cp-form-creator-content, div.cp-form-creator-results { padding: 10px; display: flex; flex-flow: column; @@ -86,6 +102,54 @@ } } } + div.cp-form-creator-results { + display: flex; + flex-flow: column; + position: relative; + & > div { + background: @cp_forms-bg1; + padding: 10px; + &:not(:last-child) { + margin-bottom: 20px; + } + } + .cp-form-block-question { + margin-bottom: 5px; + } + .cp-form-block-type { + float: right; + padding: 5px; + margin-top: -10px; + margin-right: -10px; + i { margin-right: 5px; } + background: @cp_forms-bg2; + } + .cp-form-results-type-text { + max-height: 300px; + overflow: auto; + .cp-form-results-type-text-data { + padding: 5px 10px; + background: @cp_forms-bg2; + &:not(:last-child) { margin-bottom: 1px; } + } + } + .cp-form-results-type-radio { + display: table; + .cp-form-results-type-radio-data { + display: table-row; + border: 1px solid @cp_forms-border; + & > span { + border: 1px solid @cp_forms-border; + display: table-cell; + padding: 5px 10px; + background: @cp_forms-bg2; + &.cp-value { + min-width: 200px; + } + } + } + } + } } } diff --git a/www/form/inner.js b/www/form/inner.js index b62c4e0da..246d8149f 100644 --- a/www/form/inner.js +++ b/www/form/inner.js @@ -59,21 +59,17 @@ define([ Messages.form_duplicates = "Duplicate entries have been removed"; + Messages.form_submit = "Submit"; + Messages.form_update = "Update"; Messages.form_reset = "Reset"; Messages.form_sent = "Sent"; Messages.form_cantFindAnswers = "Unable to retrieve your existing answers for this form."; - // XXX to update our own answers, we need to store the server hash of the message - // and we'll be able to use getHistoryRange to fetch this message when we come back + Messages.form_viewResults = "Go to responses"; + Messages.form_viewCreator = "Go to form creator"; - - var makeFormSettings = function (framework) { - // XXX - // Button to set results as public - // Checkbox to allow anonymous answers - // Button to clear all answers? - }; + Messages.form_notAnswered = "And {0} empty answers"; var editOptions = function (v, cb) { var add = h('button.btn.btn-secondary', [ @@ -128,6 +124,12 @@ define([ ]; }; + var getEmpty = function (empty) { + if (empty) { + return UI.setHTML(h('div.cp-form-results-type-text-empty'), Messages._getKey('form_notAnswered', [empty])); + } + }; + var TYPES = { input: { get: function () { @@ -140,6 +142,19 @@ define([ reset: function () { $tag.val(''); } }; }, + printResults: function (answers, uid) { + var results = []; + var empty = 0; + Object.keys(answers).forEach(function (author) { + var obj = answers[author]; + var answer = obj.msg[uid]; + if (!answer || !answer.trim()) { return empty++; } + results.push(h('div.cp-form-results-type-text-data', answer)); + }); + results.push(getEmpty(empty)); + + return h('div.cp-form-results-type-text', results); + }, icon: h('i.fa.fa-font') }, radio: { @@ -185,12 +200,33 @@ define([ }; }, + printResults: function (answers, uid) { + var results = []; + var empty = 0; + var count = {}; + Object.keys(answers).forEach(function (author) { + var obj = answers[author]; + var answer = obj.msg[uid]; + if (!answer || !answer.trim()) { return empty++; } + count[answer] = count[answer] || 0; + count[answer]++; + }); + Object.keys(count).forEach(function (value) { + results.push(h('div.cp-form-results-type-radio-data', [ + h('span.cp-value', value), + h('span.cp-count', count[value]) + ])); + }); + results.push(getEmpty(empty)); + + return h('div.cp-form-results-type-radio', results); + }, icon: h('i.fa.fa-list-ul') } }; - var makeFormControls = function (framework, content) { - var send = h('button.btn.btn-primary', Messages.poll_commit); + var makeFormControls = function (framework, content, update) { + var send = h('button.btn.btn-primary', update ? Messages.form_update : Messages.form_submit); var reset = h('button.btn.btn-danger-alt', Messages.form_reset); $(reset).click(function () { if (!Array.isArray(APP.formBlocks)) { return; } @@ -211,12 +247,13 @@ define([ mailbox: content.answers, results: results }, function (err, data) { - console.error(data); + $send.attr('disabled', 'disabled'); if (err || (data && data.error)) { console.error(err || data.error); return void UI.warn(Messages.error); } UI.alert(Messages.form_sent); + $send.text(Messages.form_update); }); }); return h('div.cp-form-send-container', [send, reset]); @@ -321,7 +358,30 @@ define([ }); $container.empty().append(elements); - $container.append(makeFormControls(framework, content)); + $container.append(makeFormControls(framework, content, Boolean(answers))); + }; + + var renderResults = function (content, answers) { + var $container = $('div.cp-form-creator-results').empty(); + var form = content.form; + var elements = Object.keys(form).map(function (uid) { + var block = form[uid]; + var type = block.type; + var model = TYPES[type]; + if (!model || !model.printResults) { return; } + var print = model.printResults(answers, uid); + + var q = h('div.cp-form-block-question', block.q || Messages.form_default); + return h('div.cp-form-block', [ + h('div.cp-form-block-type', [ + TYPES[type].icon.cloneNode(), + h('span', Messages['form_type_'+type]) + ]), + q, + h('div.cp-form-block-content', print), + ]); + }); + $container.append(elements); }; var andThen = function (framework) { @@ -333,6 +393,39 @@ define([ var priv = metadataMgr.getPrivateData(); APP.isEditor = Boolean(priv.form_public); + var $body = $('body'); + + var makeFormSettings = function () { + var viewResults = h('button.btn.btn-primary', [ + h('span.cp-app-form-button-results', Messages.form_viewResults), + h('span.cp-app-form-button-creator', Messages.form_viewCreator), + ]); + var $v = $(viewResults).click(function () { + if ($body.hasClass('cp-app-form-results')) { + $body.removeClass('cp-app-form-results'); + return; + } + $v.attr('disabled', 'disabled'); + sframeChan.query("Q_FORM_FETCH_ANSWERS", { + channel: content.answers.channel, + validateKey: content.answers.validateKey, + publicKey: content.answers.publicKey + }, function (err, answers) { + $v.removeAttr('disabled'); + $body.addClass('cp-app-form-results'); + renderResults(content, answers); + }); + + }); + return [ + viewResults + ]; + + // XXX + // Button to set results as public + // Checkbox to allow anonymous answers + // Button to clear all answers? + }; var makeFormCreator = function () { @@ -356,13 +449,21 @@ define([ }); return btn; }); - controlContainer = h('div.cp-form-creator-control', controls); + + var settings = makeFormSettings(); + + controlContainer = h('div.cp-form-creator-control', [ + h('div.cp-form-creator-settings', settings), + h('div.cp-form-creator-types', controls) + ]); } var contentContainer = h('div.cp-form-creator-content'); + var resultsContainer = h('div.cp-form-creator-results'); var div = h('div.cp-form-creator-container', [ controlContainer, contentContainer, + resultsContainer ]); return div; }; @@ -397,8 +498,10 @@ define([ if (APP.isEditor) { sframeChan.query("Q_FORM_FETCH_ANSWERS", { channel: content.answers.channel, + validateKey: content.answers.validateKey, publicKey: content.answers.publicKey - }, function () { + }, function (err, obj) { + if (obj) { APP.answers = obj; } updateForm(framework, content, true); }); diff --git a/www/form/main.js b/www/form/main.js index 089c3433e..95f162864 100644 --- a/www/form/main.js +++ b/www/form/main.js @@ -30,7 +30,7 @@ define([ }; var addRpc = function (sframeChan, Cryptpad, Utils) { sframeChan.on('Q_FORM_FETCH_ANSWERS', function (data, cb) { - var keys; + var myKeys; var CPNetflux; var network; nThen(function (w) { @@ -40,7 +40,15 @@ define([ CPNetflux = _CPNetflux; })); Cryptpad.getAccessKeys(w(function (_keys) { - keys = _keys; + if (!Array.isArray(_keys)) { return; } + + _keys.some(function (_k) { + if ((!Cryptpad.initialTeam && !_k.id) || Cryptpad.initialTeam === _k.id) { + myKeys = _k; + return true; + } + }); + console.error(myKeys); })); Cryptpad.makeNetwork(w(function (err, nw) { network = nw; @@ -52,24 +60,31 @@ define([ var crypto = Utils.Crypto.Mailbox.createEncryptor({ curvePrivate: privateKey, - curvePublic: publicKey || data.publicKey + curvePublic: publicKey || data.publicKey, + validateKey: data.validateKey }); var config = { network: network, channel: data.channel, noChainPad: true, validateKey: keys.secondaryValidateKey, - owners: [], // XXX add pad owner + owners: [myKeys.edPublic], // XXX add pad owner crypto: crypto, // XXX Cache }; + var results = {}; config.onReady = function () { - cb(); - // XXX + cb(results); network.disconnect(); }; - config.onMessage = function () { - // XXX + config.onMessage = function (msg, peer, vKey, isCp, hash, senderCurve, cfg) { + var parsed = Utils.Util.tryParse(msg); + if (!parsed) { return; } + results[senderCurve] = { + msg: parsed, + hash: hash, + time: cfg.time + }; }; CPNetflux.start(config); });