import EmberObject from "@ember/object"; import WidgetGlue from "discourse/widgets/glue"; import { getRegister } from "discourse-common/lib/get-owner"; import { bind, observes } from "discourse-common/utils/decorators"; import { withPluginApi } from "discourse/lib/plugin-api"; const PLUGIN_ID = "discourse-poll"; let _glued = []; let _interval = null; function rerender() { _glued.forEach((g) => g.queueRerender()); } function cleanUpPolls() { if (_interval) { clearInterval(_interval); _interval = null; } _glued.forEach((g) => g.cleanUp()); _glued = []; } function initializePolls(api) { const register = getRegister(api), pollGroupableUserFields = api.container.lookup( "service:site-settings" ).poll_groupable_user_fields; cleanUpPolls(); api.modifyClass("controller:topic", { pluginId: PLUGIN_ID, subscribe() { this._super(...arguments); this.messageBus.subscribe(`/polls/${this.model.id}`, this._onPollMessage); }, unsubscribe() { this.messageBus.unsubscribe("/polls/*", this._onPollMessage); this._super(...arguments); }, @bind _onPollMessage(msg) { const post = this.get("model.postStream").findLoadedPost(msg.post_id); post?.set("polls", msg.polls); }, }); api.modifyClass("model:post", { pluginId: PLUGIN_ID, _polls: null, pollsObject: null, // we need a proper ember object so it is bindable @observes("polls") pollsChanged() { const polls = this.polls; if (polls) { this._polls = this._polls || {}; polls.forEach((p) => { const existing = this._polls[p.name]; if (existing) { this._polls[p.name].setProperties(p); } else { this._polls[p.name] = EmberObject.create(p); } }); this.set("pollsObject", this._polls); rerender(); } }, }); function attachPolls(elem, helper) { let pollNodes = [...elem.querySelectorAll(".poll")]; pollNodes = pollNodes.filter( (node) => node.parentNode.tagName !== "BLOCKQUOTE" ); if (!pollNodes.length || !helper) { return; } const post = helper.getModel(); api.preventCloak(post.id); post.pollsChanged(); const polls = post.pollsObject || {}; const votes = post.polls_votes || {}; _interval = _interval || setInterval(rerender, 30000); pollNodes.forEach((pollNode) => { const pollName = pollNode.dataset.pollName; let poll = polls[pollName]; let pollPost = post; let vote = votes[pollName] || []; const quotedId = pollNode.closest(".expanded-quote")?.dataset.postId; if (quotedId && post.quoted[quotedId]) { pollPost = post.quoted[quotedId]; pollPost = EmberObject.create(pollPost); poll = EmberObject.create(pollPost.polls.findBy("name", pollName)); vote = pollPost.polls_votes || {}; vote = vote[pollName] || []; } if (poll) { const titleElement = pollNode.querySelector(".poll-title"); const attrs = { id: `${pollName}-${pollPost.id}`, post: pollPost, poll, vote, hasSavedVote: vote.length > 0, titleHTML: titleElement?.outerHTML, groupableUserFields: (pollGroupableUserFields || "") .split("|") .filter(Boolean), }; const glue = new WidgetGlue("discourse-poll", register, attrs); glue.appendTo(pollNode); _glued.push(glue); } }); } api.includePostAttributes("polls", "polls_votes"); api.decorateCookedElement(attachPolls, { onlyStream: true, id: "discourse-poll", }); api.cleanupStream(cleanUpPolls); const siteSettings = api.container.lookup("site-settings:main"); if (siteSettings.poll_enabled) { api.addSearchSuggestion("in:polls"); } } export default { name: "extend-for-poll", initialize() { withPluginApi("0.8.7", initializePolls); }, };