diff --git a/client-admin/src/components/conversation-admin/conversation-config.js b/client-admin/src/components/conversation-admin/conversation-config.js index ae8d18cbd..2b73c2f8d 100644 --- a/client-admin/src/components/conversation-admin/conversation-config.js +++ b/client-admin/src/components/conversation-admin/conversation-config.js @@ -144,6 +144,10 @@ class ConversationConfig extends React.Component { Customize the user interface + + [EXPERIMENTAL FEATURE] Participants can see the "This comment is important" checkbox + + Participants can see the visualization diff --git a/client-participation/js/main.js b/client-participation/js/main.js index ad47f2d77..3f1264bab 100644 --- a/client-participation/js/main.js +++ b/client-participation/js/main.js @@ -48,6 +48,7 @@ var IconFaAngleRight = require("./templates/icon_fa_angle_right.handlebars"); var IconFaAsterisk = require("./templates/icon_fa_asterisk.handlebars"); var IconFaBan = require("./templates/icon_fa_ban.handlebars"); var IconFaCircleCheckPartial = require("./templates/icon_fa_check_circle.handlebars"); +var IconFaCircleQuestion = require("./templates/icon_fa_question_circle.handlebars"); var iconFaFacebookSquare16 = require("./templates/icon_fa_facebook_square_16.handlebars"); var iconFaFacebookSquare25 = require("./templates/icon_fa_facebook_square_25.handlebars"); var IconFaLightBulb = require("./templates/icon_fa_lightbulb_o.handlebars"); @@ -320,6 +321,7 @@ Handlebars.registerPartial("linkTos", LinkTosPartial); Handlebars.registerPartial("linkPrivacy", LinkPrivacyPartial); Handlebars.registerPartial("linkAddPolis", LinkAddPolisPartial); Handlebars.registerPartial("iconFaCircleCheck", IconFaCircleCheckPartial); +Handlebars.registerPartial("iconFaCircleQuestion", IconFaCircleQuestion); Handlebars.registerPartial("iconFaBan", IconFaBan); Handlebars.registerPartial("iconFaLightBulb", IconFaLightBulb); Handlebars.registerPartial("iconFaAsterisk", IconFaAsterisk); diff --git a/client-participation/js/models/vote.js b/client-participation/js/models/vote.js index 3b04fa835..5060301e0 100644 --- a/client-participation/js/models/vote.js +++ b/client-participation/js/models/vote.js @@ -11,6 +11,7 @@ module.exports = Model.extend({ pid: undefined, // PPPParticipant id -- this is a unique id every participant has in every convo that starts at 0 conversation_id: undefined, // converSation id votes: undefined, // agree = -1, pass = 0, disagree = 1 - participantStarred: false + participantStarred: false, + high_priority: false, } }); diff --git a/client-participation/js/stores/polis.js b/client-participation/js/stores/polis.js index ad1e6e108..cc3a19b6c 100644 --- a/client-participation/js/stores/polis.js +++ b/client-participation/js/stores/polis.js @@ -349,10 +349,10 @@ module.exports = function(params) { } } - function disagree(commentId, starred, weight) { + function disagree(commentId, starred, high_priority) { clearComment(commentId, "push"); var o = { - weight: weight, + high_priority: high_priority, vote: polisTypes.reactions.push, tid: commentId }; @@ -415,10 +415,10 @@ module.exports = function(params) { return promise; } - function agree(commentId, starred, weight) { + function agree(commentId, starred, high_priority) { clearComment(commentId); var o = { - weight: weight, + high_priority: high_priority, vote: polisTypes.reactions.pull, tid: commentId }; @@ -428,10 +428,10 @@ module.exports = function(params) { return react(o); } - function pass(tid, starred, weight) { + function pass(tid, starred, high_priority) { clearComment(tid); var o = { - weight: weight, + high_priority: high_priority, vote: polisTypes.reactions.pass, tid: tid }; diff --git a/client-participation/js/strings/en_us.js b/client-participation/js/strings/en_us.js index c8923317f..561a6c8b5 100644 --- a/client-participation/js/strings/en_us.js +++ b/client-participation/js/strings/en_us.js @@ -6,8 +6,10 @@ s.agree = "Agree"; s.disagree = "Disagree"; s.pass = "Pass / Unsure"; -s.importantCheckbox = "This comment is important"; -s.howImportantPrompt = "How important is this statement?"; +s.importantCheckbox = "Important/Significant"; +s.importantCheckboxDesc = + "Check this box if you believe this statement is especially important to you or is highly relevant to the conversation, irrespective of your vote. It will give this statement higher priority compared to your other votes in the conversation analysis." + s.howImportantPrompt = "How important is this statement?"; s.howImportantLow = "Low"; s.howImportantMedium = "Medium"; s.howImportantHigh = "High"; diff --git a/client-participation/js/templates/icon_fa_question_circle.handlebars b/client-participation/js/templates/icon_fa_question_circle.handlebars new file mode 100644 index 000000000..a0fea6be1 --- /dev/null +++ b/client-participation/js/templates/icon_fa_question_circle.handlebars @@ -0,0 +1,10 @@ +{{! Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . }} + + \ No newline at end of file diff --git a/client-participation/js/templates/vote-view.handlebars b/client-participation/js/templates/vote-view.handlebars index a76464bde..24d433d02 100644 --- a/client-participation/js/templates/vote-view.handlebars +++ b/client-participation/js/templates/vote-view.handlebars @@ -272,6 +272,29 @@ {{else}} {{!-- !shouldMod --}} + {{#if showImportantCheckbox}} +
+ + +
+ {{/if}}
- -
- {{/if}} + {{/if}} {{!-- /shouldMod --}} diff --git a/client-participation/js/views/vote-view.js b/client-participation/js/views/vote-view.js index 7427f0033..b42eadb76 100644 --- a/client-participation/js/views/vote-view.js +++ b/client-participation/js/views/vote-view.js @@ -111,6 +111,7 @@ module.exports = Handlebones.ModelView.extend({ } } + ctx.showImportantCheckbox = preload.conversation.importance_enabled; ctx.social = socialCtx; ctx.noModSet = !ctx.spamOn && !ctx.otOn && !ctx.importantOn; ctx.canSubscribe = !!preload.firstPtpt || this.votesByMe.size() > 0; @@ -484,13 +485,13 @@ module.exports = Handlebones.ModelView.extend({ }); return false; }; - this.getWeight = function() { - if ($("#weight_low").prop("checked")) { - return -1; - } else if ($("#weight_high").prop("checked")) { - return 1; + + // note: instead of -1/1/0, weight is now the boolean high_priority + this.highPriority = function () { + if ($("#weight_high").prop("checked")) { + return true; } - return 0; + return false; }; this.participantAgreed = function(e) { this.mostRecentVoteType = "agree"; @@ -502,12 +503,12 @@ module.exports = Handlebones.ModelView.extend({ this.wipVote = { vote: -1, conversation_id: conversation_id, - weight: this.getWeight(), + high_priority: this.highPriority(), tid: tid }; serverClient.addToVotesByMe(this.wipVote); this.onButtonClicked(); - serverClient.agree(tid, starred, this.wipVote.weight) + serverClient.agree(tid, starred, this.wipVote.high_priority) .then(onVote.bind(this), onFail.bind(this)); }; this.participantDisagreed = function() { @@ -517,12 +518,12 @@ module.exports = Handlebones.ModelView.extend({ this.wipVote = { vote: 1, conversation_id: conversation_id, - weight: this.getWeight(), + high_priority: this.highPriority(), tid: tid }; serverClient.addToVotesByMe(this.wipVote); this.onButtonClicked(); - serverClient.disagree(tid, starred, this.wipVote.weight) + serverClient.disagree(tid, starred, this.wipVote.high_priority) .then(onVote.bind(this), onFail.bind(this)); }; this.participantPassed = function() { @@ -532,12 +533,12 @@ module.exports = Handlebones.ModelView.extend({ this.wipVote = { vote: 0, conversation_id: conversation_id, - weight: this.getWeight(), + high_priority: this.highPriority(), // TODO: specify in help text that this is for "important but unsure" tid: tid }; serverClient.addToVotesByMe(this.wipVote); this.onButtonClicked(); - serverClient.pass(tid, starred, this.wipVote.weight) + serverClient.pass(tid, starred, this.wipVote.high_priority) .then(onVote.bind(this), onFail.bind(this)); }; @@ -577,7 +578,7 @@ module.exports = Handlebones.ModelView.extend({ return; } var starred = this.model.get("starred"); - var weight = 0; + var high_priority = this.wipVote.high_priority; var tid = this.wipVote.tid; @@ -589,13 +590,13 @@ module.exports = Handlebones.ModelView.extend({ } if (this.wipVote.vote === -1) { - serverClient.agree(tid, starred, weight) + serverClient.agree(tid, starred, high_priority) .then(reloadPage, onFailAfterAuth); } else if (this.wipVote.vote === 0) { - serverClient.pass(tid, starred, weight) + serverClient.pass(tid, starred, high_priority) .then(reloadPage, onFailAfterAuth); } else if (this.wipVote.vote === 1) { - serverClient.disagree(tid, starred, weight) + serverClient.disagree(tid, starred, high_priority) .then(reloadPage, onFailAfterAuth); } else { alert(3); diff --git a/server/app.ts b/server/app.ts index e02ab2ce7..637d9833b 100644 --- a/server/app.ts +++ b/server/app.ts @@ -835,7 +835,7 @@ helpersInitialized.then( ), need("vote", getIntInRange(-1, 1), assignToP), want("starred", getBool, assignToP), - want("weight", getNumberInRange(-1, 1), assignToP, 0), + want("high_priority", getBool, assignToP, false), resolve_pidThing("pid", assignToP, "post:votes"), want("xid", getStringLimitLength(1, 999), assignToP), want("lang", getStringLimitLength(1, 10), assignToP), // language of the next comment to be returned @@ -1004,6 +1004,7 @@ helpersInitialized.then( want("strict_moderation", getBool, assignToP), want("topic", getOptionalStringLimitLength(1000), assignToP), want("description", getOptionalStringLimitLength(50000), assignToP), + want("importance_enabled", getBool, assignToP), want("vis_type", getInt, assignToP), want("help_type", getInt, assignToP), want("write_type", getInt, assignToP), diff --git a/server/postgres/migrations/000007_add_comment_priority.sql b/server/postgres/migrations/000007_add_comment_priority.sql new file mode 100644 index 000000000..1973bf3a6 --- /dev/null +++ b/server/postgres/migrations/000007_add_comment_priority.sql @@ -0,0 +1,7 @@ +ALTER TABLE conversations + ADD importance_enabled BOOLEAN NOT NULL + DEFAULT (false); + +ALTER TABLE votes + ADD high_priority BOOLEAN NOT NULL + DEFAULT (false); diff --git a/server/src/d.ts b/server/src/d.ts index f991e9841..6224263a4 100644 --- a/server/src/d.ts +++ b/server/src/d.ts @@ -140,6 +140,7 @@ export type ConversationType = { help_bgcolor?: any; style_btn?: any; write_type?: any; + importance_enabled?: any; owner_sees_participation_stats?: any; link_url?: any; course_invite?: any; @@ -207,4 +208,5 @@ export type Vote = { weight: any; starred: any; parent_url: any; + high_priority: any; }; diff --git a/server/src/db/sql.ts b/server/src/db/sql.ts index 9dd11f368..25e612514 100644 --- a/server/src/db/sql.ts +++ b/server/src/db/sql.ts @@ -27,6 +27,7 @@ const sql_conversations: any = sql.define({ "parent_url", "vis_type", "write_type", + "importance_enabled", "help_type", "socialbtn_type", "subscribe_type", @@ -74,7 +75,7 @@ const sql_comments = sql.define({ const sql_votes_latest_unique = sql.define({ name: "votes_latest_unique", - columns: ["zid", "tid", "pid", "modified", "vote"], + columns: ["zid", "tid", "pid", "modified", "vote", "weight", "high_priority"], }); const sql_participant_metadata_answers = sql.define({ diff --git a/server/src/server.ts b/server/src/server.ts index d7a0c50b1..8d6b84062 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -611,6 +611,7 @@ function initializePolisHelpers() { tid?: any, voteType?: any, weight?: number, + high_priority?: boolean, ) { let zid = conv?.zid; weight = weight || 0; @@ -620,8 +621,8 @@ function initializePolisHelpers() { reject: (arg0: string) => void ) { let query = - "INSERT INTO votes (pid, zid, tid, vote, weight_x_32767, created) VALUES ($1, $2, $3, $4, $5, default) RETURNING *;"; - let params = [pid, zid, tid, voteType, weight_x_32767]; + "INSERT INTO votes (pid, zid, tid, vote, weight_x_32767, high_priority, created) VALUES ($1, $2, $3, $4, $5, $6, default) RETURNING *;"; + let params = [pid, zid, tid, voteType, weight_x_32767, high_priority]; pgQuery(query, params, function (err: any, result: { rows: any[] }) { if (err) { if (isDuplicateKey(err)) { @@ -650,6 +651,7 @@ function initializePolisHelpers() { tid?: any, voteType?: any, weight?: number, + high_priority?: boolean, ) { return ( pgQueryP_readOnly("select * from conversations where zid = ($1);", [zid]) @@ -702,6 +704,7 @@ function initializePolisHelpers() { tid, voteType, weight, + high_priority, ); }) ); @@ -7322,7 +7325,7 @@ Email verified! You can close this tab or hit the back button. let createdTime = comment.created; let votePromise = _.isUndefined(vote) ? Promise.resolve() - : votesPost(uid, pid, zid, tid, vote, 0); + : votesPost(uid, pid, zid, tid, vote, 0, false); return ( votePromise @@ -7947,6 +7950,7 @@ Email verified! You can close this tab or hit the back button. req.p.tid, req.p.vote, req.p.weight, + req.p.high_priority, ); }) .then(function (o: { vote: any }) { @@ -8546,6 +8550,7 @@ Email verified! You can close this tab or hit the back button. help_bgcolor: string; style_btn: any; write_type: any; + importance_enabled: any; owner_sees_participation_stats: any; launch_presentation_return_url_hex: any; link_url: any; @@ -8637,6 +8642,9 @@ Email verified! You can close this tab or hit the back button. if (!_.isUndefined(req.p.write_type)) { fields.write_type = req.p.write_type; } + if (!_.isUndefined(req.p.importance_enabled)) { + fields.importance_enabled = req.p.importance_enabled; + } ifDefinedSet("auth_needed_to_vote", req.p, fields); ifDefinedSet("auth_needed_to_write", req.p, fields); ifDefinedSet("auth_opt_fb", req.p, fields); @@ -13058,6 +13066,7 @@ Thanks for using Polis! parent_url: conv.parent_url, vis_type: conv.vis_type, write_type: conv.write_type, + importance_enabled: conv.importance_enabled, help_type: conv.help_type, socialbtn_type: conv.socialbtn_type, bgcolor: conv.bgcolor,