Notifications: Make notifications save their type.
This commit is contained in:
parent
8e1db6a835
commit
805ab86933
8 changed files with 90 additions and 27 deletions
|
@ -62,10 +62,13 @@ def update(%User{} = follower, %User{} = following, state) do
|
||||||
follow(follower, following, state)
|
follow(follower, following, state)
|
||||||
|
|
||||||
following_relationship ->
|
following_relationship ->
|
||||||
|
{:ok, relationship} =
|
||||||
following_relationship
|
following_relationship
|
||||||
|> cast(%{state: state}, [:state])
|
|> cast(%{state: state}, [:state])
|
||||||
|> validate_required([:state])
|
|> validate_required([:state])
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
|
|
||||||
|
{:ok, relationship}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -30,12 +30,26 @@ defmodule Pleroma.Notification do
|
||||||
|
|
||||||
schema "notifications" do
|
schema "notifications" do
|
||||||
field(:seen, :boolean, default: false)
|
field(:seen, :boolean, default: false)
|
||||||
|
field(:type, :string)
|
||||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||||
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType)
|
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_notification_type(user, activity) do
|
||||||
|
with %__MODULE__{} = notification <-
|
||||||
|
Repo.get_by(__MODULE__, user_id: user.id, activity_id: activity.id) do
|
||||||
|
type =
|
||||||
|
activity
|
||||||
|
|> type_from_activity()
|
||||||
|
|
||||||
|
notification
|
||||||
|
|> changeset(%{type: type})
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@spec unread_notifications_count(User.t()) :: integer()
|
@spec unread_notifications_count(User.t()) :: integer()
|
||||||
def unread_notifications_count(%User{id: user_id}) do
|
def unread_notifications_count(%User{id: user_id}) do
|
||||||
from(q in __MODULE__,
|
from(q in __MODULE__,
|
||||||
|
@ -46,7 +60,7 @@ def unread_notifications_count(%User{id: user_id}) do
|
||||||
|
|
||||||
def changeset(%Notification{} = notification, attrs) do
|
def changeset(%Notification{} = notification, attrs) do
|
||||||
notification
|
notification
|
||||||
|> cast(attrs, [:seen])
|
|> cast(attrs, [:seen, :type])
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec last_read_query(User.t()) :: Ecto.Queryable.t()
|
@spec last_read_query(User.t()) :: Ecto.Queryable.t()
|
||||||
|
@ -330,12 +344,55 @@ defp do_create_notifications(%Activity{} = activity) do
|
||||||
{:ok, notifications}
|
{:ok, notifications}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp type_from_activity(%{data: %{"type" => type}} = activity) do
|
||||||
|
case type do
|
||||||
|
"Follow" ->
|
||||||
|
if Activity.follow_accepted?(activity) do
|
||||||
|
"follow"
|
||||||
|
else
|
||||||
|
"follow_request"
|
||||||
|
end
|
||||||
|
|
||||||
|
"Announce" ->
|
||||||
|
"reblog"
|
||||||
|
|
||||||
|
"Like" ->
|
||||||
|
"favourite"
|
||||||
|
|
||||||
|
"Move" ->
|
||||||
|
"move"
|
||||||
|
|
||||||
|
"EmojiReact" ->
|
||||||
|
"pleroma:emoji_reaction"
|
||||||
|
|
||||||
|
"Create" ->
|
||||||
|
activity
|
||||||
|
|> type_from_activity_object()
|
||||||
|
|
||||||
|
t ->
|
||||||
|
raise "No notification type for activity type #{t}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp type_from_activity_object(%{data: %{"type" => "Create"}} = activity) do
|
||||||
|
object = Object.normalize(activity, false)
|
||||||
|
|
||||||
|
case object.data["type"] do
|
||||||
|
"ChatMessage" -> "pleroma:chat_mention"
|
||||||
|
_ -> "mention"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# TODO move to sql, too.
|
# TODO move to sql, too.
|
||||||
def create_notification(%Activity{} = activity, %User{} = user, do_send \\ true) do
|
def create_notification(%Activity{} = activity, %User{} = user, do_send \\ true) do
|
||||||
unless skip?(activity, user) do
|
unless skip?(activity, user) do
|
||||||
{:ok, %{notification: notification}} =
|
{:ok, %{notification: notification}} =
|
||||||
Multi.new()
|
Multi.new()
|
||||||
|> Multi.insert(:notification, %Notification{user_id: user.id, activity: activity})
|
|> Multi.insert(:notification, %Notification{
|
||||||
|
user_id: user.id,
|
||||||
|
activity: activity,
|
||||||
|
type: type_from_activity(activity)
|
||||||
|
})
|
||||||
|> Marker.multi_set_last_read_id(user, "notifications")
|
|> Marker.multi_set_last_read_id(user, "notifications")
|
||||||
|> Repo.transaction()
|
|> Repo.transaction()
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.EarmarkRenderer
|
alias Pleroma.EarmarkRenderer
|
||||||
alias Pleroma.FollowingRelationship
|
alias Pleroma.FollowingRelationship
|
||||||
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Object.Containment
|
alias Pleroma.Object.Containment
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -595,6 +596,8 @@ def handle_incoming(
|
||||||
User.update_follower_count(followed)
|
User.update_follower_count(followed)
|
||||||
User.update_following_count(follower)
|
User.update_following_count(follower)
|
||||||
|
|
||||||
|
Notification.update_notification_type(followed, follow_activity)
|
||||||
|
|
||||||
ActivityPub.accept(%{
|
ActivityPub.accept(%{
|
||||||
to: follow_activity.data["to"],
|
to: follow_activity.data["to"],
|
||||||
type: "Accept",
|
type: "Accept",
|
||||||
|
|
|
@ -185,6 +185,7 @@ defp notification_type do
|
||||||
"mention",
|
"mention",
|
||||||
"poll",
|
"poll",
|
||||||
"pleroma:emoji_reaction",
|
"pleroma:emoji_reaction",
|
||||||
|
"pleroma:chat_mention",
|
||||||
"move",
|
"move",
|
||||||
"follow_request"
|
"follow_request"
|
||||||
],
|
],
|
||||||
|
|
|
@ -121,6 +121,7 @@ def accept_follow_request(follower, followed) do
|
||||||
object: follow_activity.data["id"],
|
object: follow_activity.data["id"],
|
||||||
type: "Accept"
|
type: "Accept"
|
||||||
}) do
|
}) do
|
||||||
|
Notification.update_notification_type(followed, follow_activity)
|
||||||
{:ok, follower}
|
{:ok, follower}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -81,22 +81,6 @@ def render(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# This returns the notification type by activity, but both chats and statuses
|
|
||||||
# are in "Create" activities.
|
|
||||||
mastodon_type =
|
|
||||||
case Activity.mastodon_notification_type(activity) do
|
|
||||||
"mention" ->
|
|
||||||
object = Object.normalize(activity)
|
|
||||||
|
|
||||||
case object do
|
|
||||||
%{data: %{"type" => "ChatMessage"}} -> "pleroma:chat_mention"
|
|
||||||
_ -> "mention"
|
|
||||||
end
|
|
||||||
|
|
||||||
type ->
|
|
||||||
type
|
|
||||||
end
|
|
||||||
|
|
||||||
# Note: :relationships contain user mutes (needed for :muted flag in :status)
|
# Note: :relationships contain user mutes (needed for :muted flag in :status)
|
||||||
status_render_opts = %{relationships: opts[:relationships]}
|
status_render_opts = %{relationships: opts[:relationships]}
|
||||||
|
|
||||||
|
@ -107,7 +91,7 @@ def render(
|
||||||
) do
|
) do
|
||||||
response = %{
|
response = %{
|
||||||
id: to_string(notification.id),
|
id: to_string(notification.id),
|
||||||
type: mastodon_type,
|
type: notification.type,
|
||||||
created_at: CommonAPI.Utils.to_masto_date(notification.inserted_at),
|
created_at: CommonAPI.Utils.to_masto_date(notification.inserted_at),
|
||||||
account: account,
|
account: account,
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
|
@ -115,7 +99,7 @@ def render(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case mastodon_type do
|
case notification.type do
|
||||||
"mention" ->
|
"mention" ->
|
||||||
put_status(response, activity, reading_user, status_render_opts)
|
put_status(response, activity, reading_user, status_render_opts)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddTypeToNotifications do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:notifications) do
|
||||||
|
add(:type, :string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -31,6 +31,7 @@ test "creates a notification for an emoji reaction" do
|
||||||
{:ok, [notification]} = Notification.create_notifications(activity)
|
{:ok, [notification]} = Notification.create_notifications(activity)
|
||||||
|
|
||||||
assert notification.user_id == user.id
|
assert notification.user_id == user.id
|
||||||
|
assert notification.type == "pleroma:emoji_reaction"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "notifies someone when they are directly addressed" do
|
test "notifies someone when they are directly addressed" do
|
||||||
|
@ -48,6 +49,7 @@ test "notifies someone when they are directly addressed" do
|
||||||
notified_ids = Enum.sort([notification.user_id, other_notification.user_id])
|
notified_ids = Enum.sort([notification.user_id, other_notification.user_id])
|
||||||
assert notified_ids == [other_user.id, third_user.id]
|
assert notified_ids == [other_user.id, third_user.id]
|
||||||
assert notification.activity_id == activity.id
|
assert notification.activity_id == activity.id
|
||||||
|
assert notification.type == "mention"
|
||||||
assert other_notification.activity_id == activity.id
|
assert other_notification.activity_id == activity.id
|
||||||
|
|
||||||
assert [%Pleroma.Marker{unread_count: 2}] =
|
assert [%Pleroma.Marker{unread_count: 2}] =
|
||||||
|
@ -335,9 +337,12 @@ test "it creates `follow_request` notification for pending Follow activity" do
|
||||||
# After request is accepted, the same notification is rendered with type "follow":
|
# After request is accepted, the same notification is rendered with type "follow":
|
||||||
assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
|
assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
|
||||||
|
|
||||||
notification_id = notification.id
|
notification =
|
||||||
assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
|
Repo.get(Notification, notification.id)
|
||||||
assert %{type: "follow"} = NotificationView.render("show.json", render_opts)
|
|> Repo.preload(:activity)
|
||||||
|
|
||||||
|
assert %{type: "follow"} =
|
||||||
|
NotificationView.render("show.json", notification: notification, for: followed_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create a notification for follow-unfollow-follow chains" do
|
test "it doesn't create a notification for follow-unfollow-follow chains" do
|
||||||
|
|
Loading…
Reference in a new issue