total rebase
[anni] / lib / pleroma / web / mastodon_api / views / notification_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.NotificationView do
6   use Pleroma.Web, :view
7
8   alias Pleroma.Activity
9   alias Pleroma.Chat.MessageReference
10   alias Pleroma.Notification
11   alias Pleroma.Object
12   alias Pleroma.User
13   alias Pleroma.UserRelationship
14   alias Pleroma.Web.AdminAPI.Report
15   alias Pleroma.Web.AdminAPI.ReportView
16   alias Pleroma.Web.CommonAPI
17   alias Pleroma.Web.MastodonAPI.AccountView
18   alias Pleroma.Web.MastodonAPI.NotificationView
19   alias Pleroma.Web.MastodonAPI.StatusView
20   alias Pleroma.Web.MediaProxy
21   alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
22
23   defp object_id_for(%{data: %{"object" => %{"id" => id}}}) when is_binary(id), do: id
24
25   defp object_id_for(%{data: %{"object" => id}}) when is_binary(id), do: id
26
27   @parent_types ~w{Like Announce EmojiReact Update}
28
29   def render("index.json", %{notifications: notifications, for: reading_user} = opts) do
30     activities = Enum.map(notifications, & &1.activity)
31
32     parent_activities =
33       activities
34       |> Enum.filter(fn
35         %{data: %{"type" => type}} ->
36           type in @parent_types
37       end)
38       |> Enum.map(&object_id_for/1)
39       |> Activity.create_by_object_ap_id()
40       |> Activity.with_preloaded_object(:left)
41       |> Pleroma.Repo.all()
42
43     relationships_opt =
44       cond do
45         Map.has_key?(opts, :relationships) ->
46           opts[:relationships]
47
48         is_nil(reading_user) ->
49           UserRelationship.view_relationships_option(nil, [])
50
51         true ->
52           move_activities_targets =
53             activities
54             |> Enum.filter(&(&1.data["type"] == "Move"))
55             |> Enum.map(&User.get_cached_by_ap_id(&1.data["target"]))
56             |> Enum.filter(& &1)
57
58           actors =
59             activities
60             |> Enum.map(fn a -> User.get_cached_by_ap_id(a.data["actor"]) end)
61             |> Enum.filter(& &1)
62             |> Kernel.++(move_activities_targets)
63
64           UserRelationship.view_relationships_option(reading_user, actors, subset: :source_mutes)
65       end
66
67     opts =
68       opts
69       |> Map.put(:parent_activities, parent_activities)
70       |> Map.put(:relationships, relationships_opt)
71
72     safe_render_many(notifications, NotificationView, "show.json", opts)
73   end
74
75   def render(
76         "show.json",
77         %{
78           notification: %Notification{activity: activity} = notification,
79           for: reading_user
80         } = opts
81       ) do
82     actor = User.get_cached_by_ap_id(activity.data["actor"])
83
84     parent_activity_fn = fn ->
85       if opts[:parent_activities] do
86         Activity.Queries.find_by_object_ap_id(opts[:parent_activities], object_id_for(activity))
87       else
88         Activity.get_create_by_object_ap_id(object_id_for(activity))
89       end
90     end
91
92     # Note: :relationships contain user mutes (needed for :muted flag in :status)
93     status_render_opts = %{relationships: opts[:relationships]}
94     account = AccountView.render("show.json", %{user: actor, for: reading_user})
95
96     response = %{
97       id: to_string(notification.id),
98       type: notification.type,
99       created_at: CommonAPI.Utils.to_masto_date(notification.inserted_at),
100       account: account,
101       pleroma: %{
102         is_muted: User.mutes?(reading_user, actor),
103         is_seen: notification.seen
104       }
105     }
106
107     case notification.type do
108       "mention" ->
109         put_status(response, activity, reading_user, status_render_opts)
110
111       "favourite" ->
112         put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
113
114       "reblog" ->
115         put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
116
117       "update" ->
118         put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
119
120       "move" ->
121         put_target(response, activity, reading_user, %{})
122
123       "poll" ->
124         put_status(response, activity, reading_user, status_render_opts)
125
126       "pleroma:emoji_reaction" ->
127         response
128         |> put_status(parent_activity_fn.(), reading_user, status_render_opts)
129         |> put_emoji(activity)
130
131       "pleroma:chat_mention" ->
132         put_chat_message(response, activity, reading_user, status_render_opts)
133
134       "pleroma:report" ->
135         put_report(response, activity)
136
137       type when type in ["follow", "follow_request"] ->
138         response
139     end
140   end
141
142   defp put_report(response, activity) do
143     report_render = ReportView.render("show.json", Report.extract_report_info(activity))
144
145     Map.put(response, :report, report_render)
146   end
147
148   defp put_emoji(response, activity) do
149     response
150     |> Map.put(:emoji, activity.data["content"])
151     |> Map.put(:emoji_url, MediaProxy.url(Pleroma.Emoji.emoji_url(activity.data)))
152   end
153
154   defp put_chat_message(response, activity, reading_user, opts) do
155     object = Object.normalize(activity, fetch: false)
156     author = User.get_cached_by_ap_id(object.data["actor"])
157     chat = Pleroma.Chat.get(reading_user.id, author.ap_id)
158     cm_ref = MessageReference.for_chat_and_object(chat, object)
159     render_opts = Map.merge(opts, %{for: reading_user, chat_message_reference: cm_ref})
160     chat_message_render = MessageReferenceView.render("show.json", render_opts)
161
162     Map.put(response, :chat_message, chat_message_render)
163   end
164
165   defp put_status(response, activity, reading_user, opts) do
166     status_render_opts = Map.merge(opts, %{activity: activity, for: reading_user})
167     status_render = StatusView.render("show.json", status_render_opts)
168
169     Map.put(response, :status, status_render)
170   end
171
172   defp put_target(response, activity, reading_user, opts) do
173     target_user = User.get_cached_by_ap_id(activity.data["target"])
174     target_render_opts = Map.merge(opts, %{user: target_user, for: reading_user})
175     target_render = AccountView.render("show.json", target_render_opts)
176
177     Map.put(response, :target, target_render)
178   end
179 end