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