From d0f53463a2b5965c0e70a1881e7f3d7757045faa Mon Sep 17 00:00:00 2001 From: "byte[]" Date: Mon, 18 Nov 2019 13:09:59 -0500 Subject: [PATCH] render polls --- lib/philomena/polls/poll.ex | 4 +- lib/philomena/posts.ex | 2 +- lib/philomena/posts/post.ex | 2 +- .../controllers/topic_controller.ex | 2 +- .../templates/topic/poll/_display.html.slime | 13 ++++++ .../templates/topic/poll/_results.html.slime | 44 +++++++++++++++++++ .../templates/topic/show.html.slime | 3 +- lib/philomena_web/views/topic/poll_view.ex | 29 ++++++++++++ 8 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 lib/philomena_web/templates/topic/poll/_display.html.slime create mode 100644 lib/philomena_web/templates/topic/poll/_results.html.slime create mode 100644 lib/philomena_web/views/topic/poll_view.ex diff --git a/lib/philomena/polls/poll.ex b/lib/philomena/polls/poll.ex index 2881524d..d5b7f39d 100644 --- a/lib/philomena/polls/poll.ex +++ b/lib/philomena/polls/poll.ex @@ -4,14 +4,16 @@ defmodule Philomena.Polls.Poll do alias Philomena.Topics.Topic alias Philomena.Users.User + alias Philomena.PollOptions.PollOption schema "polls" do belongs_to :topic, Topic belongs_to :deleted_by, User + has_many :options, PollOption field :title, :string field :vote_method, :string - field :active_until, :naive_datetime + field :active_until, :utc_datetime field :total_votes, :integer, default: 0 field :hidden_from_users, :boolean, default: false field :deletion_reason, :string, default: "" diff --git a/lib/philomena/posts.ex b/lib/philomena/posts.ex index d078385d..fe6e9568 100644 --- a/lib/philomena/posts.ex +++ b/lib/philomena/posts.ex @@ -61,7 +61,7 @@ defmodule Philomena.Posts do |> repo.one() Ecto.build_assoc(topic, :posts, [topic_position: (last_position || -1) + 1] ++ attributes) - |> Post.creation_changeset(user, params) + |> Post.creation_changeset(params, user) |> repo.insert() end) |> Multi.run(:update_topic, fn repo, %{post: %{id: post_id}} -> diff --git a/lib/philomena/posts/post.ex b/lib/philomena/posts/post.ex index 7413ac9c..afcaa307 100644 --- a/lib/philomena/posts/post.ex +++ b/lib/philomena/posts/post.ex @@ -40,7 +40,7 @@ defmodule Philomena.Posts.Post do end @doc false - def creation_changeset(post, user, attrs) do + def creation_changeset(post, attrs, user) do post |> cast(attrs, [:body, :anonymous]) |> set_name_at_post_time(user) diff --git a/lib/philomena_web/controllers/topic_controller.ex b/lib/philomena_web/controllers/topic_controller.ex index caf39418..c304ec4b 100644 --- a/lib/philomena_web/controllers/topic_controller.ex +++ b/lib/philomena_web/controllers/topic_controller.ex @@ -12,7 +12,7 @@ defmodule PhilomenaWeb.TopicController do topic = Topic |> where(forum_id: ^forum.id, slug: ^slug, hidden_from_users: false) - |> preload(:user) + |> preload([:user, poll: :option]) |> Repo.one() conn = conn |> assign(:topic, topic) diff --git a/lib/philomena_web/templates/topic/poll/_display.html.slime b/lib/philomena_web/templates/topic/poll/_display.html.slime new file mode 100644 index 00000000..52bd99bc --- /dev/null +++ b/lib/philomena_web/templates/topic/poll/_display.html.slime @@ -0,0 +1,13 @@ += if @poll.hidden_from_users do + .walloftext + .block.block--fixed.block--warning + h1 This poll has been deleted + p + ' Reason: + strong + = @poll.deletion_reason || "Unknown (likely deleted in error). Please contact a moderator." + +- else + .poll + .poll-area + = render PhilomenaWeb.TopicView, "_poll_results.html", poll: @poll, conn: @conn \ No newline at end of file diff --git a/lib/philomena_web/templates/topic/poll/_results.html.slime b/lib/philomena_web/templates/topic/poll/_results.html.slime new file mode 100644 index 00000000..df0e2560 --- /dev/null +++ b/lib/philomena_web/templates/topic/poll/_results.html.slime @@ -0,0 +1,44 @@ +.poll-results + h4.poll__header + ' Poll results: + = @poll.title + + .poll-option-list + elixir: + winning = winning_option(@poll) + winners? = @poll.total_votes > 0 + + = for option <- ranked_options(@poll) do + div class=option_class(option, winning, winners?) + .poll-option__text + span.poll-option__label = option.label + + .poll-option__counts + span + => percent_of_total(option, @poll) + => option.vote_count + => pluralize("vote", "votes", option.vote_count) + + .poll-bar + svg.poll-bar__image width=percent_of_total(option, @poll) height="100%" viewBox="0 0 1 1" preserveAspectRatio="none" + rect class=poll_bar_class(option, winning, winners?) width="1" height="1" + + = if active?(@poll) do + ' Poll ends + = pretty_time(@poll.active_until) + ' . + + = if @poll.total_votes > 0 do + => @poll.total_votes + => pluralize("vote", "votes", @poll.total_votes) + - else + ' No votes have been + ' cast so far. + + - else + ' Poll ended + => pretty_time(@poll.active_until) + ' with + => @poll.total_votes + = pluralize("vote", "votes", @poll.total_votes) + ' . \ No newline at end of file diff --git a/lib/philomena_web/templates/topic/show.html.slime b/lib/philomena_web/templates/topic/show.html.slime index 9990b50a..4939aed7 100644 --- a/lib/philomena_web/templates/topic/show.html.slime +++ b/lib/philomena_web/templates/topic/show.html.slime @@ -29,7 +29,8 @@ h1 = @topic.title = render PhilomenaWeb.Topic.SubscriptionView, "_subscription.html", forum: @forum, topic: @topic, watching: @watching, conn: @conn / Display the poll, if any -/= render partial: 'polls/display', locals: { poll: @topic.poll } += if @topic.poll do + = render PhilomenaWeb.Topic.PollView, "_display.html.slime", poll: @poll, conn: @conn / The actual posts .posts-area diff --git a/lib/philomena_web/views/topic/poll_view.ex b/lib/philomena_web/views/topic/poll_view.ex new file mode 100644 index 00000000..29a8dc2f --- /dev/null +++ b/lib/philomena_web/views/topic/poll_view.ex @@ -0,0 +1,29 @@ +defmodule PhilomenaWeb.Topic.PollView do + use PhilomenaWeb, :view + + def ranked_options(poll) do + poll.options + |> Enum.sort_by(&{-&1.vote_count, &1.id}) + end + + def winning_option(poll) do + poll + |> ranked_options() + |> hd() + end + + def active?(poll) do + not poll.hidden_from_users and poll.active_until > DateTime.utc_now() + end + + def percent_of_total(_option, %{total_votes: 0}), do: 0 + def percent_of_total(%{vote_count: vote_count}, %{total_votes: total_votes}) do + :io_lib.format("~.2f%", [(vote_count / total_votes * 100)]) + end + + def option_class(%{id: option_id}, %{id: option_id}, true), do: "poll-option-top" + def option_class(_option, _top_option, _winners?), do: nil + + def poll_bar_class(%{id: option_id}, %{id: option_id}, true), do: "poll-bar__fill poll-bar__fill--top" + def poll_bar_class(_option, _top_option, _winners?), do: "poll-bar__fill" +end