fix count of poll voters
[akkoma] / lib / pleroma / web / mastodon_api / views / poll_view.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.PollView do
6 use Pleroma.Web, :view
7
8 alias Pleroma.Web.CommonAPI.Utils
9
10 def render("show.json", %{object: object, multiple: multiple, options: options} = params) do
11 {end_time, expired} = end_time_and_expired(object)
12 {options, votes_count} = options_and_votes_count(options)
13
14 poll = %{
15 # Mastodon uses separate ids for polls, but an object can't have
16 # more than one poll embedded so object id is fine
17 id: to_string(object.id),
18 expires_at: end_time,
19 expired: expired,
20 multiple: multiple,
21 votes_count: votes_count,
22 voters_count: voters_count(object),
23 options: options,
24 emojis: Pleroma.Web.MastodonAPI.StatusView.build_emojis(object.data["emoji"])
25 }
26
27 if params[:for] do
28 # when unauthenticated Mastodon doesn't include `voted` & `own_votes` keys in response
29 {voted, own_votes} = voted_and_own_votes(params, options)
30 Map.merge(poll, %{voted: voted, own_votes: own_votes})
31 else
32 poll
33 end
34 end
35
36 def render("show.json", %{object: object} = params) do
37 case object.data do
38 %{"anyOf" => [_ | _] = options} ->
39 render(__MODULE__, "show.json", Map.merge(params, %{multiple: true, options: options}))
40
41 %{"oneOf" => [_ | _] = options} ->
42 render(__MODULE__, "show.json", Map.merge(params, %{multiple: false, options: options}))
43
44 _ ->
45 nil
46 end
47 end
48
49 defp end_time_and_expired(object) do
50 if object.data["closed"] do
51 end_time = NaiveDateTime.from_iso8601!(object.data["closed"])
52 expired = NaiveDateTime.compare(end_time, NaiveDateTime.utc_now()) == :lt
53
54 {Utils.to_masto_date(end_time), expired}
55 else
56 {nil, false}
57 end
58 end
59
60 defp options_and_votes_count(options) do
61 Enum.map_reduce(options, 0, fn %{"name" => name} = option, count ->
62 current_count = option["replies"]["totalItems"] || 0
63
64 {%{
65 title: name,
66 votes_count: current_count
67 }, current_count + count}
68 end)
69 end
70
71 defp voters_count(%{data: %{"voters" => voters}}) when is_list(voters) do
72 length(voters)
73 end
74
75 defp voters_count(_), do: 0
76
77 defp voted_and_own_votes(%{object: object} = params, options) do
78 if params[:for] do
79 existing_votes =
80 Pleroma.Web.ActivityPub.Utils.get_existing_votes(params[:for].ap_id, object)
81
82 voted = existing_votes != [] or params[:for].ap_id == object.data["actor"]
83
84 own_votes =
85 if voted do
86 titles = Enum.map(options, & &1[:title])
87
88 Enum.reduce(existing_votes, [], fn vote, acc ->
89 data = vote |> Map.get(:object) |> Map.get(:data)
90 index = Enum.find_index(titles, &(&1 == data["name"]))
91 [index | acc]
92 end)
93 else
94 []
95 end
96
97 {voted, own_votes}
98 else
99 {false, []}
100 end
101 end
102 end