Mastodon API: Mark the conversation as read for the author when they send a new direc...
authoreugenijm <eugenijm@protonmail.com>
Thu, 17 Oct 2019 12:25:15 +0000 (15:25 +0300)
committereugenijm <eugenijm@protonmail.com>
Thu, 17 Oct 2019 13:49:39 +0000 (16:49 +0300)
CHANGELOG.md
lib/pleroma/conversation/participation.ex
lib/pleroma/web/activity_pub/activity_pub.ex
test/conversation/participation_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/mastodon_api/controllers/conversation_controller_test.exs
test/web/mastodon_api/views/account_view_test.exs

index 24876d3f2b08fc2909c09bc03b8295b72acd0371..f9f84b0568570a539930cd31826191c5f92442ed 100644 (file)
@@ -32,6 +32,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - MRF (Simple Policy): Also use `:accept`/`:reject` on the actors rather than only their activities
 - OStatus: Extract RSS functionality
 - Mastodon API: Add `pleroma.direct_conversation_id` to the status endpoint (`GET /api/v1/statuses/:id`)
 - MRF (Simple Policy): Also use `:accept`/`:reject` on the actors rather than only their activities
 - OStatus: Extract RSS functionality
 - Mastodon API: Add `pleroma.direct_conversation_id` to the status endpoint (`GET /api/v1/statuses/:id`)
+- Mastodon API: Mark the direct conversation as read for the author when they send a new direct message
 
 ### Fixed
 - Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`)
 
 ### Fixed
 - Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`)
index ab81f32173fd1a69694eed8aa39c1be0013843bc..e17f49e58da622ec8db706bc2dfad9ca0a2a8f5b 100644 (file)
@@ -48,6 +48,12 @@ defmodule Pleroma.Conversation.Participation do
     |> validate_required([:read])
   end
 
     |> validate_required([:read])
   end
 
+  def mark_as_read(%User{} = user, %Conversation{} = conversation) do
+    with %__MODULE__{} = participation <- for_user_and_conversation(user, conversation) do
+      mark_as_read(participation)
+    end
+  end
+
   def mark_as_read(participation) do
     participation
     |> read_cng(%{read: true})
   def mark_as_read(participation) do
     participation
     |> read_cng(%{read: true})
index 091ec2588767c215b7f9753715c821ce2dbf723c..d391732a2e380c872e1f0ac107ac78fa77f93304 100644 (file)
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
   alias Pleroma.Activity.Ir.Topics
   alias Pleroma.Config
   alias Pleroma.Conversation
   alias Pleroma.Activity.Ir.Topics
   alias Pleroma.Config
   alias Pleroma.Conversation
+  alias Pleroma.Conversation.Participation
   alias Pleroma.Notification
   alias Pleroma.Object
   alias Pleroma.Object.Containment
   alias Pleroma.Notification
   alias Pleroma.Object
   alias Pleroma.Object.Containment
@@ -153,11 +154,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
       Notification.create_notifications(activity)
 
 
       Notification.create_notifications(activity)
 
-      participations =
-        activity
-        |> Conversation.create_or_bump_for()
-        |> get_participations()
-
+      conversation = create_or_bump_conversation(activity, map["actor"])
+      participations = get_participations(conversation)
       stream_out(activity)
       stream_out_participations(participations)
       {:ok, activity}
       stream_out(activity)
       stream_out_participations(participations)
       {:ok, activity}
@@ -182,7 +180,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     end
   end
 
     end
   end
 
-  defp get_participations({:ok, %{participations: participations}}), do: participations
+  defp create_or_bump_conversation(activity, actor) do
+    with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
+         %User{} = user <- User.get_cached_by_ap_id(actor),
+         Participation.mark_as_read(user, conversation) do
+      {:ok, conversation}
+    end
+  end
+
+  defp get_participations({:ok, conversation}) do
+    conversation
+    |> Repo.preload(:participations, force: true)
+    |> Map.get(:participations)
+  end
+
   defp get_participations(_), do: []
 
   def stream_out_participations(participations) do
   defp get_participations(_), do: []
 
   def stream_out_participations(participations) do
index f430bdf75f5def543af0eab7a9cf9cceb4ae5cbf..a5af0d1b21c2a13f3286d12ac72a4e7b1bb58233 100644 (file)
@@ -23,6 +23,39 @@ defmodule Pleroma.Conversation.ParticipationTest do
     assert %Pleroma.Conversation{} = participation.conversation
   end
 
     assert %Pleroma.Conversation{} = participation.conversation
   end
 
+  test "for a new conversation or a reply, it doesn't mark the author's participation as unread" do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    {:ok, _} =
+      CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
+
+    user = User.get_cached_by_id(user.id)
+    other_user = User.get_cached_by_id(other_user.id)
+
+    [%{read: true}] = Participation.for_user(user)
+    [%{read: false} = participation] = Participation.for_user(other_user)
+
+    assert User.get_cached_by_id(user.id).info.unread_conversation_count == 0
+    assert User.get_cached_by_id(other_user.id).info.unread_conversation_count == 1
+
+    {:ok, _} =
+      CommonAPI.post(other_user, %{
+        "status" => "Hey @#{user.nickname}.",
+        "visibility" => "direct",
+        "in_reply_to_conversation_id" => participation.id
+      })
+
+    user = User.get_cached_by_id(user.id)
+    other_user = User.get_cached_by_id(other_user.id)
+
+    [%{read: false}] = Participation.for_user(user)
+    [%{read: true}] = Participation.for_user(other_user)
+
+    assert User.get_cached_by_id(user.id).info.unread_conversation_count == 1
+    assert User.get_cached_by_id(other_user.id).info.unread_conversation_count == 0
+  end
+
   test "for a new conversation, it sets the recipents of the participation" do
     user = insert(:user)
     other_user = insert(:user)
   test "for a new conversation, it sets the recipents of the participation" do
     user = insert(:user)
     other_user = insert(:user)
@@ -32,7 +65,7 @@ defmodule Pleroma.Conversation.ParticipationTest do
       CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
 
     user = User.get_cached_by_id(user.id)
       CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
 
     user = User.get_cached_by_id(user.id)
-    other_user = User.get_cached_by_id(user.id)
+    other_user = User.get_cached_by_id(other_user.id)
     [participation] = Participation.for_user(user)
     participation = Pleroma.Repo.preload(participation, :recipients)
 
     [participation] = Participation.for_user(user)
     participation = Pleroma.Repo.preload(participation, :recipients)
 
index 3a5a2f9840a4674ef51560e19c11875c5d656df6..28a9b773c49f81a9050c81be13956dc90d04c7e2 100644 (file)
@@ -41,6 +41,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
         assert called(Pleroma.Web.Streamer.stream("participation", participations))
       end
     end
         assert called(Pleroma.Web.Streamer.stream("participation", participations))
       end
     end
+
+    test "streams them out on activity creation" do
+      user_one = insert(:user)
+      user_two = insert(:user)
+
+      with_mock Pleroma.Web.Streamer,
+        stream: fn _, _ -> nil end do
+        {:ok, activity} =
+          CommonAPI.post(user_one, %{
+            "status" => "@#{user_two.nickname}",
+            "visibility" => "direct"
+          })
+
+        conversation =
+          activity.data["context"]
+          |> Pleroma.Conversation.get_for_ap_id()
+          |> Repo.preload(participations: :user)
+
+        assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
+      end
+    end
   end
 
   describe "fetching restricted by visibility" do
   end
 
   describe "fetching restricted by visibility" do
index a308a76201e216edb106cb92fb7b4ee971f41f61..d89a8717930083787f166365b6f30e68d68983e8 100644 (file)
@@ -54,9 +54,9 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
     assert user_two.id in account_ids
     assert user_three.id in account_ids
     assert is_binary(res_id)
     assert user_two.id in account_ids
     assert user_three.id in account_ids
     assert is_binary(res_id)
-    assert unread == true
+    assert unread == false
     assert res_last_status["id"] == direct.id
     assert res_last_status["id"] == direct.id
-    assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
+    assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 0
   end
 
   test "updates the last_status on reply", %{conn: conn} do
   end
 
   test "updates the last_status on reply", %{conn: conn} do
@@ -95,19 +95,23 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
         "visibility" => "direct"
       })
 
         "visibility" => "direct"
       })
 
+    assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 0
+    assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 1
+
     [%{"id" => direct_conversation_id, "unread" => true}] =
       conn
     [%{"id" => direct_conversation_id, "unread" => true}] =
       conn
-      |> assign(:user, user_one)
+      |> assign(:user, user_two)
       |> get("/api/v1/conversations")
       |> json_response(200)
 
     %{"unread" => false} =
       conn
       |> get("/api/v1/conversations")
       |> json_response(200)
 
     %{"unread" => false} =
       conn
-      |> assign(:user, user_one)
+      |> assign(:user, user_two)
       |> post("/api/v1/conversations/#{direct_conversation_id}/read")
       |> json_response(200)
 
     assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 0
       |> post("/api/v1/conversations/#{direct_conversation_id}/read")
       |> json_response(200)
 
     assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 0
+    assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 0
 
     # The conversation is marked as unread on reply
     {:ok, _} =
 
     # The conversation is marked as unread on reply
     {:ok, _} =
@@ -124,6 +128,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
       |> json_response(200)
 
     assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
       |> json_response(200)
 
     assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
+    assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 0
 
     # A reply doesn't increment the user's unread_conversation_count if the conversation is unread
     {:ok, _} =
 
     # A reply doesn't increment the user's unread_conversation_count if the conversation is unread
     {:ok, _} =
@@ -134,6 +139,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
       })
 
     assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
       })
 
     assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
+    assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 0
   end
 
   test "(vanilla) Mastodon frontend behaviour", %{conn: conn} do
   end
 
   test "(vanilla) Mastodon frontend behaviour", %{conn: conn} do
index b7a4938a65996df0ea312bdd9381817aed0dc7b2..ad209b4a34d8d20e1e24fa502d9ba2a67a4f55e0 100644 (file)
@@ -424,8 +424,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
       other_user = insert(:user)
 
       {:ok, _activity} =
       other_user = insert(:user)
 
       {:ok, _activity} =
-        CommonAPI.post(user, %{
-          "status" => "Hey @#{other_user.nickname}.",
+        CommonAPI.post(other_user, %{
+          "status" => "Hey @#{user.nickname}.",
           "visibility" => "direct"
         })
 
           "visibility" => "direct"
         })