- 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`)
|> 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})
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
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}
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
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)
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)
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
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 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
"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
- |> assign(:user, user_one)
+ |> assign(:user, user_two)
|> 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
+ assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 0
# The conversation is marked as unread on reply
{:ok, _} =
|> 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, _} =
})
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
other_user = insert(:user)
{:ok, _activity} =
- CommonAPI.post(user, %{
- "status" => "Hey @#{other_user.nickname}.",
+ CommonAPI.post(other_user, %{
+ "status" => "Hey @#{user.nickname}.",
"visibility" => "direct"
})