Add poll votes
[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 |> where(
37 [n, a],
38 fragment(
39 "? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
40 a.actor
41 )
42 )
43 |> join(:inner, [n], activity in assoc(n, :activity))
44 |> join(:left, [n, a], object in Object,
45 on:
46 fragment(
47 "(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
48 object.data,
49 a.data
50 )
51 )
52 |> preload([n, a, o], activity: {a, object: o})
53 end
54
55 def for_user(user, opts \\ %{}) do
56 user
57 |> for_user_query()
58 |> Pagination.fetch_paginated(opts)
59 end
60
61 def set_read_up_to(%{id: user_id} = _user, id) do
62 query =
63 from(
64 n in Notification,
65 where: n.user_id == ^user_id,
66 where: n.id <= ^id,
67 update: [
68 set: [seen: true]
69 ]
70 )
71
72 Repo.update_all(query, [])
73 end
74
75 def read_one(%User{} = user, notification_id) do
76 with {:ok, %Notification{} = notification} <- get(user, notification_id) do
77 notification
78 |> changeset(%{seen: true})
79 |> Repo.update()
80 end
81 end
82
83 def get(%{id: user_id} = _user, id) do
84 query =
85 from(
86 n in Notification,
87 where: n.id == ^id,
88 join: activity in assoc(n, :activity),
89 preload: [activity: activity]
90 )
91
92 notification = Repo.one(query)
93
94 case notification do
95 %{user_id: ^user_id} ->
96 {:ok, notification}
97
98 _ ->
99 {:error, "Cannot get notification"}
100 end
101 end
102
103 def clear(user) do
104 from(n in Notification, where: n.user_id == ^user.id)
105 |> Repo.delete_all()
106 end
107
108 def destroy_multiple(%{id: user_id} = _user, ids) do
109 from(n in Notification,
110 where: n.id in ^ids,
111 where: n.user_id == ^user_id
112 )
113 |> Repo.delete_all()
114 end
115
116 def dismiss(%{id: user_id} = _user, id) do
117 notification = Repo.get(Notification, id)
118
119 case notification do
120 %{user_id: ^user_id} ->
121 Repo.delete(notification)
122
123 _ ->
124 {:error, "Cannot dismiss notification"}
125 end
126 end
127
128 def create_notifications(%Activity{data: %{"to" => _, "type" => type}} = activity)
129 when type in ["Create", "Like", "Announce", "Follow"] do
130 object = Object.normalize(activity)
131
132 unless object && object.data["type"] == "Answer" do
133 users = get_notified_from_activity(activity)
134 notifications = Enum.map(users, fn user -> create_notification(activity, user) end)
135 {:ok, notifications}
136 else
137 {:ok, []}
138 end
139 end
140
141 def create_notifications(_), do: {:ok, []}
142
143 # TODO move to sql, too.
144 def create_notification(%Activity{} = activity, %User{} = user) do
145 unless skip?(activity, user) do
146 notification = %Notification{user_id: user.id, activity: activity}
147 {:ok, notification} = Repo.insert(notification)
148 Pleroma.Web.Streamer.stream("user", notification)
149 Pleroma.Web.Push.send(notification)
150 notification
151 end
152 end
153
154 def get_notified_from_activity(activity, local_only \\ true)
155
156 def get_notified_from_activity(
157 %Activity{data: %{"to" => _, "type" => type} = _data} = activity,
158 local_only
159 )
160 when type in ["Create", "Like", "Announce", "Follow"] do
161 recipients =
162 []
163 |> Utils.maybe_notify_to_recipients(activity)
164 |> Utils.maybe_notify_mentioned_recipients(activity)
165 |> Utils.maybe_notify_subscribers(activity)
166 |> Enum.uniq()
167
168 User.get_users_from_set(recipients, local_only)
169 end
170
171 def get_notified_from_activity(_, _local_only), do: []
172
173 def skip?(activity, user) do
174 [:self, :blocked, :local, :muted, :followers, :follows, :recently_followed]
175 |> Enum.any?(&skip?(&1, activity, user))
176 end
177
178 def skip?(:self, activity, user) do
179 activity.data["actor"] == user.ap_id
180 end
181
182 def skip?(:blocked, activity, user) do
183 actor = activity.data["actor"]
184 User.blocks?(user, %{ap_id: actor})
185 end
186
187 def skip?(:local, %{local: true}, %{info: %{notification_settings: %{"local" => false}}}),
188 do: true
189
190 def skip?(:local, %{local: false}, %{info: %{notification_settings: %{"remote" => false}}}),
191 do: true
192
193 def skip?(:muted, activity, user) do
194 actor = activity.data["actor"]
195
196 User.mutes?(user, %{ap_id: actor}) or CommonAPI.thread_muted?(user, activity)
197 end
198
199 def skip?(
200 :followers,
201 activity,
202 %{info: %{notification_settings: %{"followers" => false}}} = user
203 ) do
204 actor = activity.data["actor"]
205 follower = User.get_cached_by_ap_id(actor)
206 User.following?(follower, user)
207 end
208
209 def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do
210 actor = activity.data["actor"]
211 followed = User.get_cached_by_ap_id(actor)
212 User.following?(user, followed)
213 end
214
215 def skip?(:recently_followed, %{data: %{"type" => "Follow"}} = activity, user) do
216 actor = activity.data["actor"]
217
218 Notification.for_user(user)
219 |> Enum.any?(fn
220 %{activity: %{data: %{"type" => "Follow", "actor" => ^actor}}} -> true
221 _ -> false
222 end)
223 end
224
225 def skip?(_, _, _), do: false
226 end