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