diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index 3a3082e72..0343a20d4 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -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 diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index efafbce48..41ac53505 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -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() diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 4ac0d43fc..886403fcd 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -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", diff --git a/lib/pleroma/web/api_spec/operations/notification_operation.ex b/lib/pleroma/web/api_spec/operations/notification_operation.ex index 46e72f8bf..c966b553a 100644 --- a/lib/pleroma/web/api_spec/operations/notification_operation.ex +++ b/lib/pleroma/web/api_spec/operations/notification_operation.ex @@ -185,6 +185,7 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do "mention", "poll", "pleroma:emoji_reaction", + "pleroma:chat_mention", "move", "follow_request" ], diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index e0987b1a7..5a194910d 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -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 diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index 07d55a3e9..c090be8ad 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -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) diff --git a/priv/repo/migrations/20200602094828_add_type_to_notifications.exs b/priv/repo/migrations/20200602094828_add_type_to_notifications.exs new file mode 100644 index 000000000..19c733628 --- /dev/null +++ b/priv/repo/migrations/20200602094828_add_type_to_notifications.exs @@ -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 diff --git a/test/notification_test.exs b/test/notification_test.exs index 37c255fee..421b7fc40 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -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