total rebase
[anni] / lib / pleroma / web / mastodon_api / views / poll_view.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 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       pleroma: %{
26         non_anonymous: object.data["nonAnonymous"] || false
27       }
28     }
29
30     if params[:for] do
31       # when unauthenticated Mastodon doesn't include `voted` & `own_votes` keys in response
32       {voted, own_votes} = voted_and_own_votes(params, options)
33       Map.merge(poll, %{voted: voted, own_votes: own_votes})
34     else
35       poll
36     end
37   end
38
39   def render("show.json", %{object: object} = params) do
40     case object.data do
41       %{"anyOf" => [_ | _] = options} ->
42         render(__MODULE__, "show.json", Map.merge(params, %{multiple: true, options: options}))
43
44       %{"oneOf" => [_ | _] = options} ->
45         render(__MODULE__, "show.json", Map.merge(params, %{multiple: false, options: options}))
46
47       _ ->
48         nil
49     end
50   end
51
52   defp end_time_and_expired(object) do
53     if object.data["closed"] do
54       end_time = NaiveDateTime.from_iso8601!(object.data["closed"])
55       expired = NaiveDateTime.compare(end_time, NaiveDateTime.utc_now()) == :lt
56
57       {Utils.to_masto_date(end_time), expired}
58     else
59       {nil, false}
60     end
61   end
62
63   defp options_and_votes_count(options) do
64     Enum.map_reduce(options, 0, fn %{"name" => name} = option, count ->
65       current_count = option["replies"]["totalItems"] || 0
66
67       {%{
68          title: name,
69          votes_count: current_count
70        }, current_count + count}
71     end)
72   end
73
74   defp voters_count(%{data: %{"voters" => [_ | _] = voters}}) do
75     length(voters)
76   end
77
78   defp voters_count(_), do: 0
79
80   defp voted_and_own_votes(%{object: object} = params, options) do
81     if params[:for] do
82       existing_votes =
83         Pleroma.Web.ActivityPub.Utils.get_existing_votes(params[:for].ap_id, object)
84
85       voted = existing_votes != [] or params[:for].ap_id == object.data["actor"]
86
87       own_votes =
88         if voted do
89           titles = Enum.map(options, & &1[:title])
90
91           Enum.reduce(existing_votes, [], fn vote, acc ->
92             data = vote |> Map.get(:object) |> Map.get(:data)
93             index = Enum.find_index(titles, &(&1 == data["name"]))
94             [index | acc]
95           end)
96         else
97           []
98         end
99
100       {voted, own_votes}
101     else
102       {false, []}
103     end
104   end
105 end