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