@@ -62,10 +62,13 @@ defmodule Pleroma.FollowingRelationship do | |||
follow(follower, following, state) | |||
following_relationship -> | |||
following_relationship | |||
|> cast(%{state: state}, [:state]) | |||
|> validate_required([:state]) | |||
|> Repo.update() | |||
{:ok, relationship} = | |||
following_relationship | |||
|> cast(%{state: state}, [:state]) | |||
|> validate_required([:state]) | |||
|> Repo.update() | |||
{:ok, relationship} | |||
end | |||
end | |||
@@ -30,12 +30,26 @@ defmodule Pleroma.Notification do | |||
schema "notifications" do | |||
field(:seen, :boolean, default: false) | |||
field(:type, :string) | |||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType) | |||
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType) | |||
timestamps() | |||
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() | |||
def unread_notifications_count(%User{id: user_id}) do | |||
from(q in __MODULE__, | |||
@@ -46,7 +60,7 @@ defmodule Pleroma.Notification do | |||
def changeset(%Notification{} = notification, attrs) do | |||
notification | |||
|> cast(attrs, [:seen]) | |||
|> cast(attrs, [:seen, :type]) | |||
end | |||
@spec last_read_query(User.t()) :: Ecto.Queryable.t() | |||
@@ -330,12 +344,55 @@ defmodule Pleroma.Notification do | |||
{:ok, notifications} | |||
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. | |||
def create_notification(%Activity{} = activity, %User{} = user, do_send \\ true) do | |||
unless skip?(activity, user) do | |||
{:ok, %{notification: notification}} = | |||
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") | |||
|> Repo.transaction() | |||
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
alias Pleroma.Activity | |||
alias Pleroma.EarmarkRenderer | |||
alias Pleroma.FollowingRelationship | |||
alias Pleroma.Notification | |||
alias Pleroma.Object | |||
alias Pleroma.Object.Containment | |||
alias Pleroma.Repo | |||
@@ -595,6 +596,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
User.update_follower_count(followed) | |||
User.update_following_count(follower) | |||
Notification.update_notification_type(followed, follow_activity) | |||
ActivityPub.accept(%{ | |||
to: follow_activity.data["to"], | |||
type: "Accept", | |||
@@ -185,6 +185,7 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do | |||
"mention", | |||
"poll", | |||
"pleroma:emoji_reaction", | |||
"pleroma:chat_mention", | |||
"move", | |||
"follow_request" | |||
], | |||
@@ -121,6 +121,7 @@ defmodule Pleroma.Web.CommonAPI do | |||
object: follow_activity.data["id"], | |||
type: "Accept" | |||
}) do | |||
Notification.update_notification_type(followed, follow_activity) | |||
{:ok, follower} | |||
end | |||
end | |||
@@ -81,22 +81,6 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do | |||
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) | |||
status_render_opts = %{relationships: opts[:relationships]} | |||
@@ -107,7 +91,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do | |||
) do | |||
response = %{ | |||
id: to_string(notification.id), | |||
type: mastodon_type, | |||
type: notification.type, | |||
created_at: CommonAPI.Utils.to_masto_date(notification.inserted_at), | |||
account: account, | |||
pleroma: %{ | |||
@@ -115,7 +99,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do | |||
} | |||
} | |||
case mastodon_type do | |||
case notification.type do | |||
"mention" -> | |||
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 @@ defmodule Pleroma.NotificationTest do | |||
{:ok, [notification]} = Notification.create_notifications(activity) | |||
assert notification.user_id == user.id | |||
assert notification.type == "pleroma:emoji_reaction" | |||
end | |||
test "notifies someone when they are directly addressed" do | |||
@@ -48,6 +49,7 @@ defmodule Pleroma.NotificationTest do | |||
notified_ids = Enum.sort([notification.user_id, other_notification.user_id]) | |||
assert notified_ids == [other_user.id, third_user.id] | |||
assert notification.activity_id == activity.id | |||
assert notification.type == "mention" | |||
assert other_notification.activity_id == activity.id | |||
assert [%Pleroma.Marker{unread_count: 2}] = | |||
@@ -335,9 +337,12 @@ defmodule Pleroma.NotificationTest do | |||
# After request is accepted, the same notification is rendered with type "follow": | |||
assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user) | |||
notification_id = notification.id | |||
assert [%{id: ^notification_id}] = Notification.for_user(followed_user) | |||
assert %{type: "follow"} = NotificationView.render("show.json", render_opts) | |||
notification = | |||
Repo.get(Notification, notification.id) | |||
|> Repo.preload(:activity) | |||
assert %{type: "follow"} = | |||
NotificationView.render("show.json", notification: notification, for: followed_user) | |||
end | |||
test "it doesn't create a notification for follow-unfollow-follow chains" do | |||