summaryrefslogtreecommitdiff
path: root/lib/pleroma/web/mastodon_api/views/poll_view.ex
blob: 34e23873e1ea79f1782e10623859ba3acdfc4478 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.MastodonAPI.PollView do
  use Pleroma.Web, :view

  alias Pleroma.Web.CommonAPI.Utils

  def render("show.json", %{object: object, multiple: multiple, options: options} = params) do
    {end_time, expired} = end_time_and_expired(object)
    {options, votes_count} = options_and_votes_count(options)

    poll = %{
      # Mastodon uses separate ids for polls, but an object can't have
      # more than one poll embedded so object id is fine
      id: to_string(object.id),
      expires_at: end_time,
      expired: expired,
      multiple: multiple,
      votes_count: votes_count,
      voters_count: voters_count(object),
      options: options,
      emojis: Pleroma.Web.MastodonAPI.StatusView.build_emojis(object.data["emoji"])
    }

    if params[:for] do
      # when unauthenticated Mastodon doesn't include `voted` & `own_votes` keys in response
      {voted, own_votes} = voted_and_own_votes(params, options)
      Map.merge(poll, %{voted: voted, own_votes: own_votes})
    else
      poll
    end
  end

  def render("show.json", %{object: object} = params) do
    case object.data do
      %{"anyOf" => [_ | _] = options} ->
        render(__MODULE__, "show.json", Map.merge(params, %{multiple: true, options: options}))

      %{"oneOf" => [_ | _] = options} ->
        render(__MODULE__, "show.json", Map.merge(params, %{multiple: false, options: options}))

      _ ->
        nil
    end
  end

  defp end_time_and_expired(object) do
    if object.data["closed"] do
      end_time = NaiveDateTime.from_iso8601!(object.data["closed"])
      expired = NaiveDateTime.compare(end_time, NaiveDateTime.utc_now()) == :lt

      {Utils.to_masto_date(end_time), expired}
    else
      {nil, false}
    end
  end

  defp options_and_votes_count(options) do
    Enum.map_reduce(options, 0, fn %{"name" => name} = option, count ->
      current_count = option["replies"]["totalItems"] || 0

      {%{
         title: name,
         votes_count: current_count
       }, current_count + count}
    end)
  end

  defp voters_count(%{data: %{"voters" => [_ | _] = voters}}) do
    length(voters)
  end

  defp voters_count(_), do: 0

  defp voted_and_own_votes(%{object: object} = params, options) do
    if params[:for] do
      existing_votes =
        Pleroma.Web.ActivityPub.Utils.get_existing_votes(params[:for].ap_id, object)

      voted = existing_votes != [] or params[:for].ap_id == object.data["actor"]

      own_votes =
        if voted do
          titles = Enum.map(options, & &1[:title])

          Enum.reduce(existing_votes, [], fn vote, acc ->
            data = vote |> Map.get(:object) |> Map.get(:data)
            index = Enum.find_index(titles, &(&1 == data["name"]))
            [index | acc]
          end)
        else
          []
        end

      {voted, own_votes}
    else
      {false, []}
    end
  end
end