import Controller from "@ember/controller"; import EmberObject, { action } from "@ember/object"; import { gt, or } from "@ember/object/computed"; import { next } from "@ember/runloop"; import discourseComputed, { observes } from "discourse-common/utils/decorators"; import ModalFunctionality from "discourse/mixins/modal-functionality"; import I18n from "I18n"; export const BAR_CHART_TYPE = "bar"; export const PIE_CHART_TYPE = "pie"; export const REGULAR_POLL_TYPE = "regular"; export const NUMBER_POLL_TYPE = "number"; export const MULTIPLE_POLL_TYPE = "multiple"; const ALWAYS_POLL_RESULT = "always"; const VOTE_POLL_RESULT = "on_vote"; const CLOSED_POLL_RESULT = "on_close"; const STAFF_POLL_RESULT = "staff_only"; export default Controller.extend(ModalFunctionality, { showAdvanced: false, pollType: REGULAR_POLL_TYPE, pollTitle: "", pollOptions: null, pollOptionsText: null, pollMin: 1, pollMax: 2, pollStep: 1, pollGroups: null, pollAutoClose: null, pollResult: ALWAYS_POLL_RESULT, chartType: BAR_CHART_TYPE, publicPoll: null, onShow() { this.setProperties({ showAdvanced: false, pollType: REGULAR_POLL_TYPE, pollTitle: null, pollOptions: [EmberObject.create({ value: "" })], pollOptionsText: "", pollMin: 1, pollMax: 2, pollStep: 1, pollGroups: null, pollAutoClose: null, pollResult: ALWAYS_POLL_RESULT, chartType: BAR_CHART_TYPE, publicPoll: false, }); }, @discourseComputed pollResults() { const options = [ { name: I18n.t("poll.ui_builder.poll_result.always"), value: ALWAYS_POLL_RESULT, }, { name: I18n.t("poll.ui_builder.poll_result.vote"), value: VOTE_POLL_RESULT, }, { name: I18n.t("poll.ui_builder.poll_result.closed"), value: CLOSED_POLL_RESULT, }, ]; if (this.get("currentUser.staff")) { options.push({ name: I18n.t("poll.ui_builder.poll_result.staff"), value: STAFF_POLL_RESULT, }); } return options; }, @discourseComputed("pollType") isRegular(pollType) { return pollType === REGULAR_POLL_TYPE; }, @discourseComputed("pollType") isNumber(pollType) { return pollType === NUMBER_POLL_TYPE; }, @discourseComputed("pollType") isMultiple(pollType) { return pollType === MULTIPLE_POLL_TYPE; }, showNumber: or("showAdvanced", "isNumber"), @discourseComputed("pollOptions.@each.value") pollOptionsCount(pollOptions) { return (pollOptions || []).filter((option) => option.value.length > 0) .length; }, @discourseComputed("site.groups") siteGroups(groups) { // prevents group "everyone" to be listed return groups.filter((g) => g.id !== 0); }, @discourseComputed("chartType", "pollType") isPie(chartType, pollType) { return pollType !== NUMBER_POLL_TYPE && chartType === PIE_CHART_TYPE; }, canRemoveOption: gt("pollOptions.length", 1), @observes("pollType", "pollOptionsCount") _setPollMinMax() { if (this.isMultiple) { if ( this.pollMin <= 0 || this.pollMin >= this.pollMax || this.pollMin >= this.pollOptionsCount ) { this.set("pollMin", this.pollOptionsCount > 0 ? 1 : 0); } if ( this.pollMax <= 0 || this.pollMin >= this.pollMax || this.pollMax > this.pollOptionsCount ) { this.set("pollMax", this.pollOptionsCount); } } else if (this.isNumber) { this.set("pollMax", this.siteSettings.poll_maximum_options); } }, @discourseComputed( "pollType", "pollResult", "publicPoll", "pollTitle", "pollOptions.@each.value", "pollMin", "pollMax", "pollStep", "pollGroups", "pollAutoClose", "chartType" ) pollOutput( pollType, pollResult, publicPoll, pollTitle, pollOptions, pollMin, pollMax, pollStep, pollGroups, pollAutoClose, chartType ) { 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 && pollType !== REGULAR_POLL_TYPE) { pollHeader += ` min=${pollMin}`; } if (pollMax && pollType !== REGULAR_POLL_TYPE) { pollHeader += ` max=${pollMax}`; } if (pollType === NUMBER_POLL_TYPE) { pollHeader += ` step=${step}`; } if (publicPoll) { pollHeader += ` public=true`; } if (chartType && pollType !== NUMBER_POLL_TYPE) { pollHeader += ` chartType=${chartType}`; } if (pollGroups && pollGroups.length > 0) { pollHeader += ` groups=${pollGroups}`; } if (pollAutoClose) { pollHeader += ` close=${pollAutoClose.toISOString()}`; } pollHeader += "]"; output += `${pollHeader}\n`; if (pollTitle) { output += `# ${pollTitle.trim()}\n`; } if (pollOptions.length > 0 && pollType !== NUMBER_POLL_TYPE) { pollOptions.forEach((option) => { if (option.value.length > 0) { output += `* ${option.value.trim()}\n`; } }); } output += "[/poll]\n"; return output; }, @discourseComputed("isNumber", "pollOptionsCount") minNumOfOptionsValidation(isNumber, pollOptionsCount) { let options = { ok: true }; if (!isNumber) { if (pollOptionsCount < 1) { return EmberObject.create({ failed: true, reason: I18n.t("poll.ui_builder.help.options_min_count"), }); } if (pollOptionsCount > this.siteSettings.poll_maximum_options) { return EmberObject.create({ failed: true, reason: I18n.t("poll.ui_builder.help.options_max_count", { count: this.siteSettings.poll_maximum_options, }), }); } } return EmberObject.create(options); }, @discourseComputed("pollOptions.@each.value") showMinNumOfOptionsValidation(pollOptions) { return pollOptions.length !== 1 || pollOptions[0].value !== ""; }, @discourseComputed( "isMultiple", "pollOptionsCount", "isNumber", "pollMin", "pollMax", "pollStep" ) minMaxValueValidation( isMultiple, pollOptionsCount, isNumber, pollMin, pollMax, pollStep ) { pollMin = parseInt(pollMin, 10) || 0; pollMax = parseInt(pollMax, 10) || 0; pollStep = parseInt(pollStep, 10) || 0; if (pollMin < 0) { return EmberObject.create({ failed: true, reason: I18n.t("poll.ui_builder.help.invalid_min_value"), }); } if (pollMax < 0 || (isMultiple && pollMax > pollOptionsCount)) { return EmberObject.create({ failed: true, reason: I18n.t("poll.ui_builder.help.invalid_max_value"), }); } if (pollMin > pollMax) { return EmberObject.create({ failed: true, reason: I18n.t("poll.ui_builder.help.invalid_values"), }); } if (isNumber) { if (pollStep < 1) { return EmberObject.create({ failed: true, reason: I18n.t("poll.ui_builder.help.min_step_value"), }); } const optionsCount = (pollMax - pollMin + 1) / pollStep; if (optionsCount < 1) { return EmberObject.create({ failed: true, reason: I18n.t("poll.ui_builder.help.options_min_count"), }); } if (optionsCount > this.siteSettings.poll_maximum_options) { return EmberObject.create({ failed: true, reason: I18n.t("poll.ui_builder.help.options_max_count", { count: this.siteSettings.poll_maximum_options, }), }); } } return EmberObject.create({ ok: true }); }, @discourseComputed("minMaxValueValidation", "minNumOfOptionsValidation") disableInsert(minMaxValueValidation, minNumOfOptionsValidation) { return !minMaxValueValidation.ok || !minNumOfOptionsValidation.ok; }, _comboboxOptions(startIndex, endIndex) { return [...Array(endIndex - startIndex).keys()].map((number) => ({ value: number + startIndex, name: number + startIndex, })); }, @action onOptionsTextChange(e) { let idx = 0; this.set( "pollOptions", e.target.value .split("\n") .map((value) => EmberObject.create({ idx: idx++, value })) ); }, @action insertPoll() { this.toolbarEvent.addText(this.pollOutput); this.send("closeModal"); }, @action toggleAdvanced() { this.toggleProperty("showAdvanced"); if (this.showAdvanced) { this.set( "pollOptionsText", this.pollOptions.map((x) => x.value).join("\n") ); } }, @action addOption(beforeOption, value, e) { if (value !== "") { const idx = this.pollOptions.indexOf(beforeOption) + 1; const option = EmberObject.create({ value: "" }); this.pollOptions.insertAt(idx, option); let lastOptionIdx = 0; this.pollOptions.forEach((o) => o.set("idx", lastOptionIdx++)); next(() => { const pollOptions = document.getElementsByClassName("poll-options"); if (pollOptions) { const inputs = pollOptions[0].getElementsByTagName("input"); if (option.idx < inputs.length) { inputs[option.idx].focus(); } } }); } if (e) { e.preventDefault(); } }, @action removeOption(option) { this.pollOptions.removeObject(option); }, });