discourse/plugins/poll/assets/javascripts/controllers/poll-ui-builder.js.es6
Jarek Radosz babbebfb35
FEATURE: Add the title attribute to polls (#10759)
Adds an optional title attribute to polls. The rationale for this addition is that polls themselves didn't contain context/question and relied on post body to explain them. That context wasn't always obvious (e.g. when there are multiple polls in a single post) or available (e.g. when you display the poll breakdown - you see the answers, but not the question)

As a side note, here's a word on how the poll plugin works:

> We have a markdown poll renderer, which we use in the builder UI and the composer preview, but… when you submit a post, raw markdown is cooked into html (twice), then we extract data from the generated html and save it to the database. When it's render time, we first display the cooked html poll, and then extract some data from that html, get the data from the post's JSON (and identify that poll using the extracted html stuff) to then render the poll using widgets and the JSON data.
2020-10-02 09:21:24 +02:00

406 lines
9.0 KiB
JavaScript

import I18n from "I18n";
import Controller from "@ember/controller";
import EmberObject from "@ember/object";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
export const BAR_CHART_TYPE = "bar";
export const PIE_CHART_TYPE = "pie";
export default Controller.extend({
regularPollType: "regular",
numberPollType: "number",
multiplePollType: "multiple",
alwaysPollResult: "always",
votePollResult: "on_vote",
closedPollResult: "on_close",
staffPollResult: "staff_only",
pollChartTypes: [
{
name: I18n.t("poll.ui_builder.poll_chart_type.bar"),
value: BAR_CHART_TYPE,
},
{
name: I18n.t("poll.ui_builder.poll_chart_type.pie"),
value: PIE_CHART_TYPE,
},
],
pollType: null,
pollResult: null,
pollTitle: null,
init() {
this._super(...arguments);
this._setupPoll();
},
@discourseComputed("regularPollType", "numberPollType", "multiplePollType")
pollTypes(regularPollType, numberPollType, multiplePollType) {
return [
{
name: I18n.t("poll.ui_builder.poll_type.regular"),
value: regularPollType,
},
{
name: I18n.t("poll.ui_builder.poll_type.number"),
value: numberPollType,
},
{
name: I18n.t("poll.ui_builder.poll_type.multiple"),
value: multiplePollType,
},
];
},
@discourseComputed("chartType", "pollType", "numberPollType")
isPie(chartType, pollType, numberPollType) {
return pollType !== numberPollType && chartType === PIE_CHART_TYPE;
},
@discourseComputed(
"alwaysPollResult",
"votePollResult",
"closedPollResult",
"staffPollResult"
)
pollResults(
alwaysPollResult,
votePollResult,
closedPollResult,
staffPollResult
) {
let options = [
{
name: I18n.t("poll.ui_builder.poll_result.always"),
value: alwaysPollResult,
},
{
name: I18n.t("poll.ui_builder.poll_result.vote"),
value: votePollResult,
},
{
name: I18n.t("poll.ui_builder.poll_result.closed"),
value: closedPollResult,
},
];
if (this.get("currentUser.staff")) {
options.push({
name: I18n.t("poll.ui_builder.poll_result.staff"),
value: staffPollResult,
});
}
return options;
},
@discourseComputed("site.groups")
siteGroups(groups) {
return groups
.map((g) => {
// prevents group "everyone" to be listed
if (g.id !== 0) {
return { name: g.name };
}
})
.filter(Boolean);
},
@discourseComputed("pollType", "regularPollType")
isRegular(pollType, regularPollType) {
return pollType === regularPollType;
},
@discourseComputed("pollType", "pollOptionsCount", "multiplePollType")
isMultiple(pollType, count, multiplePollType) {
return pollType === multiplePollType && count > 0;
},
@discourseComputed("pollType", "numberPollType")
isNumber(pollType, numberPollType) {
return pollType === numberPollType;
},
@discourseComputed("isRegular")
showMinMax(isRegular) {
return !isRegular;
},
@discourseComputed("pollOptions")
pollOptionsCount(pollOptions) {
if (pollOptions.length === 0) {
return 0;
}
let length = 0;
pollOptions.split("\n").forEach((option) => {
if (option.length !== 0) {
length += 1;
}
});
return length;
},
@observes("pollType", "pollOptionsCount")
_setPollMax() {
const isMultiple = this.isMultiple;
const isNumber = this.isNumber;
if (!isMultiple && !isNumber) {
return;
}
if (isMultiple) {
this.set("pollMax", this.pollOptionsCount);
} else if (isNumber) {
this.set("pollMax", this.siteSettings.poll_maximum_options);
}
},
@discourseComputed("isRegular", "isMultiple", "isNumber", "pollOptionsCount")
pollMinOptions(isRegular, isMultiple, isNumber, count) {
if (isRegular) {
return;
}
if (isMultiple) {
return this._comboboxOptions(1, count + 1);
} else if (isNumber) {
return this._comboboxOptions(
1,
this.siteSettings.poll_maximum_options + 1
);
}
},
@discourseComputed(
"isRegular",
"isMultiple",
"isNumber",
"pollOptionsCount",
"pollMin",
"pollStep"
)
pollMaxOptions(isRegular, isMultiple, isNumber, count, pollMin, pollStep) {
if (isRegular) {
return;
}
const pollMinInt = parseInt(pollMin, 10) || 1;
if (isMultiple) {
return this._comboboxOptions(pollMinInt + 1, count + 1);
} else if (isNumber) {
let pollStepInt = parseInt(pollStep, 10);
if (pollStepInt < 1) {
pollStepInt = 1;
}
return this._comboboxOptions(
pollMinInt + 1,
pollMinInt + this.siteSettings.poll_maximum_options * pollStepInt
);
}
},
@discourseComputed("isNumber", "pollMax")
pollStepOptions(isNumber, pollMax) {
if (!isNumber) {
return;
}
return this._comboboxOptions(1, (parseInt(pollMax, 10) || 1) + 1);
},
@discourseComputed(
"isNumber",
"showMinMax",
"pollType",
"pollResult",
"publicPoll",
"pollTitle",
"pollOptions",
"pollMin",
"pollMax",
"pollStep",
"pollGroups",
"autoClose",
"chartType",
"date",
"time"
)
pollOutput(
isNumber,
showMinMax,
pollType,
pollResult,
publicPoll,
pollTitle,
pollOptions,
pollMin,
pollMax,
pollStep,
pollGroups,
autoClose,
chartType,
date,
time
) {
let pollHeader = "[poll";
let output = "";
const match = this.toolbarEvent
.getText()
.match(/\[poll(\s+name=[^\s\]]+)*.*\]/gim);
if (match) {
pollHeader += ` name=poll${match.length + 1}`;
}
let step = pollStep;
if (step < 1) {
step = 1;
}
if (pollType) {
pollHeader += ` type=${pollType}`;
}
if (pollResult) {
pollHeader += ` results=${pollResult}`;
}
if (pollMin && showMinMax) {
pollHeader += ` min=${pollMin}`;
}
if (pollMax) {
pollHeader += ` max=${pollMax}`;
}
if (isNumber) {
pollHeader += ` step=${step}`;
}
if (publicPoll) {
pollHeader += ` public=true`;
}
if (chartType && pollType !== "number") {
pollHeader += ` chartType=${chartType}`;
}
if (pollGroups && pollGroups.length > 0) {
pollHeader += ` groups=${pollGroups}`;
}
if (autoClose) {
let closeDate = moment(
date + " " + time,
"YYYY-MM-DD HH:mm"
).toISOString();
if (closeDate) {
pollHeader += ` close=${closeDate}`;
}
}
pollHeader += "]";
output += `${pollHeader}\n`;
if (pollTitle) {
output += `# ${pollTitle.trim()}\n`;
}
if (pollOptions.length > 0 && !isNumber) {
pollOptions.split("\n").forEach((option) => {
if (option.length !== 0) {
output += `* ${option}\n`;
}
});
}
output += "[/poll]\n";
return output;
},
@discourseComputed(
"pollOptionsCount",
"isRegular",
"isMultiple",
"isNumber",
"pollMin",
"pollMax"
)
disableInsert(count, isRegular, isMultiple, isNumber, pollMin, pollMax) {
return (
(isRegular && count < 1) ||
(isMultiple && count < pollMin && pollMin >= pollMax) ||
(isNumber ? false : count < 1)
);
},
@discourseComputed("pollMin", "pollMax")
minMaxValueValidation(pollMin, pollMax) {
let options = { ok: true };
if (pollMin >= pollMax) {
options = {
failed: true,
reason: I18n.t("poll.ui_builder.help.invalid_values"),
};
}
return EmberObject.create(options);
},
@discourseComputed("pollStep")
minStepValueValidation(pollStep) {
let options = { ok: true };
if (pollStep < 1) {
options = {
failed: true,
reason: I18n.t("poll.ui_builder.help.min_step_value"),
};
}
return EmberObject.create(options);
},
@discourseComputed("disableInsert")
minNumOfOptionsValidation(disableInsert) {
let options = { ok: true };
if (disableInsert) {
options = {
failed: true,
reason: I18n.t("poll.ui_builder.help.options_count"),
};
}
return EmberObject.create(options);
},
_comboboxOptions(startIndex, endIndex) {
return [...Array(endIndex - startIndex).keys()].map((number) => ({
value: number + startIndex,
name: number + startIndex,
}));
},
_setupPoll() {
this.setProperties({
pollType: this.get("pollTypes.firstObject.value"),
publicPoll: false,
pollOptions: "",
pollMin: 1,
pollMax: null,
pollStep: 1,
autoClose: false,
chartType: BAR_CHART_TYPE,
pollResult: this.alwaysPollResult,
pollGroups: null,
pollTitle: null,
date: moment().add(1, "day").format("YYYY-MM-DD"),
time: moment().add(1, "hour").format("HH:mm"),
});
},
actions: {
insertPoll() {
this.toolbarEvent.addText(this.pollOutput);
this.send("closeModal");
this._setupPoll();
},
},
});