Notifications: Make notifications save their type.
authorlain <lain@soykaf.club>
Tue, 2 Jun 2020 11:24:34 +0000 (13:24 +0200)
committerlain <lain@soykaf.club>
Tue, 2 Jun 2020 11:24:34 +0000 (13:24 +0200)
lib/pleroma/following_relationship.ex
lib/pleroma/notification.ex
lib/pleroma/web/activity_pub/transmogrifier.ex
lib/pleroma/web/api_spec/operations/notification_operation.ex
lib/pleroma/web/common_api/common_api.ex
lib/pleroma/web/mastodon_api/views/notification_view.ex
priv/repo/migrations/20200602094828_add_type_to_notifications.exs [new file with mode: 0644]
test/notification_test.exs

index 3a3082e728037e265bc56a55883f8df7f5e36fe9..0343a20d44b5cf1b6a939cf5eff76c79e4418674 100644 (file)
@@ -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
 
index efafbce485dedf070089e2b048b50d575792444a..41ac5350586beda729b1466b0592aae980caa507 100644 (file)
@@ -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()
 
index 4ac0d43fc17fd3e62e3e28beaa22ada6bff05f25..886403fcdadec801a3318811c3372822b30d7f6f 100644 (file)
@@ -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",
index 46e72f8bf0c82bc0065b999f11070ca157863ec1..c966b553ae1e3aba2c2477eb78ea023958c7bf70 100644 (file)
@@ -185,6 +185,7 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
         "mention",
         "poll",
         "pleroma:emoji_reaction",
+        "pleroma:chat_mention",
         "move",
         "follow_request"
       ],
index e0987b1a7d36a2340f839980ad6d12aa43d59648..5a194910dbaa0ea9839fe2487c07a1bc6518d8ca 100644 (file)
@@ -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
index 07d55a3e9c18fd6046e7a6824f0f031c7330e0b2..c090be8ad0b075f6c5499951511bb353966ed822 100644 (file)
@@ -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 (file)
index 0000000..19c7336
--- /dev/null
@@ -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
index 37c255fee5130e96f3d10a4f07f1b7243f3330e2..421b7fc4076ebec4110665d9391bcb64517df1ef 100644 (file)
@@ -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