notification: preload child objects
[akkoma] / lib / pleroma / notification.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Notification do
6 use Ecto.Schema
7
8 alias Pleroma.Activity
9 alias Pleroma.Notification
10 alias Pleroma.Object
11 alias Pleroma.Pagination
12 alias Pleroma.Repo
13 alias Pleroma.User
14 alias Pleroma.Web.CommonAPI
15 alias Pleroma.Web.CommonAPI.Utils
16
17 import Ecto.Query
18 import Ecto.Changeset
19
20 schema "notifications" do
21 field(:seen, :boolean, default: false)
22 belongs_to(:user, User, type: Pleroma.FlakeId)
23 belongs_to(:activity, Activity, type: Pleroma.FlakeId)
24
25 timestamps()
26 end
27
28 def changeset(%Notification{} = notification, attrs) do
29 notification
30 |> cast(attrs, [:seen])
31 end
32
33 def for_user_query(user) do
34 Notification
35 |> where(user_id: ^user.id)
36 |> join(:inner, [n], activity in assoc(n, :activity))
37 |> join(:left, [n, a], object in Object,
38 on: fragment("(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)", object.data, a.data)
39 )
40 |> preload([n, a, o], activity: {a, object: o})
41 end
42
43 def for_user(user, opts \\ %{}) do
44 user
45 |> for_user_query()
46 |> Pagination.fetch_paginated(opts)
47 end
48
49 def set_read_up_to(%{id: user_id} = _user, id) do
50 query =
51 from(
52 n in Notification,
53 where: n.user_id == ^user_id,
54 where: n.id <= ^id,
55 update: [
56 set: [seen: true]
57 ]
58 )
59
60 Repo.update_all(query, [])
61 end
62
63 def read_one(%User{} = user, notification_id) do
64 with {:ok, %Notification{} = notification} <- get(user, notification_id) do
65 notification
66 |> changeset(%{seen: true})
67 |> Repo.update()
68 end
69 end
70
71 def get(%{id: user_id} = _user, id) do
72 query =
73 from(
74 n in Notification,
75 where: n.id == ^id,
76 join: activity in assoc(n, :activity),
77 preload: [activity: activity]
78 )
79
80 notification = Repo.one(query)
81
82 case notification do
83 %{user_id: ^user_id} ->
84 {:ok, notification}
85
86 _ ->
87 {:error, "Cannot get notification"}
88 end
89 end
90
91 def clear(user) do
92 from(n in Notification, where: n.user_id == ^user.id)
93 |> Repo.delete_all()
94 end
95
96 def dismiss(%{id: user_id} = _user, id) do
97 notification = Repo.get(Notification, id)
98
99 case notification do
100 %{user_id: ^user_id} ->
101 Repo.delete(notification)
102
103 _ ->
104 {:error, "Cannot dismiss notification"}
105 end
106 end
107
108 def create_notifications(%Activity{data: %{"to" => _, "type" => type}} = activity)
109 when type in ["Create", "Like", "Announce", "Follow"] do
110 users = get_notified_from_activity(activity)
111
112 notifications = Enum.map(users, fn user -> create_notification(activity, user) end)
113 {:ok, notifications}
114 end
115
116 def create_notifications(_), do: {:ok, []}
117
118 # TODO move to sql, too.
119 def create_notification(%Activity{} = activity, %User{} = user) do
120 unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or
121 CommonAPI.thread_muted?(user, activity) or user.ap_id == activity.data["actor"] or
122 (activity.data["type"] == "Follow" and
123 Enum.any?(Notification.for_user(user), fn notif ->
124 notif.activity.data["type"] == "Follow" and
125 notif.activity.data["actor"] == activity.data["actor"]
126 end)) do
127 notification = %Notification{user_id: user.id, activity: activity}
128 {:ok, notification} = Repo.insert(notification)
129 Pleroma.Web.Streamer.stream("user", notification)
130 Pleroma.Web.Push.send(notification)
131 notification
132 end
133 end
134
135 def get_notified_from_activity(activity, local_only \\ true)
136
137 def get_notified_from_activity(
138 %Activity{data: %{"to" => _, "type" => type} = _data} = activity,
139 local_only
140 )
141 when type in ["Create", "Like", "Announce", "Follow"] do
142 recipients =
143 []
144 |> Utils.maybe_notify_to_recipients(activity)
145 |> Utils.maybe_notify_mentioned_recipients(activity)
146 |> Enum.uniq()
147
148 User.get_users_from_set(recipients, local_only)
149 end
150
151 def get_notified_from_activity(_, _local_only), do: []
152 end