@@ -43,7 +43,7 @@ defmodule Pleroma.Conversation do | |||
def maybe_create_recipientships(participation, activity) do | |||
participation = Repo.preload(participation, :recipients) | |||
if participation.recipients |> Enum.empty?() do | |||
if Enum.empty?(participation.recipients) do | |||
recipients = User.get_all_by_ap_id(activity.recipients) | |||
RecipientShip.create(recipients, participation) | |||
end | |||
@@ -69,10 +69,6 @@ defmodule Pleroma.Conversation do | |||
Enum.map(users, fn user -> | |||
invisible_conversation = Enum.any?(users, &User.blocks?(user, &1)) | |||
unless invisible_conversation do | |||
User.increment_unread_conversation_count(conversation, user) | |||
end | |||
opts = Keyword.put(opts, :invisible_conversation, invisible_conversation) | |||
{:ok, participation} = | |||
@@ -63,21 +63,10 @@ defmodule Pleroma.Conversation.Participation do | |||
end | |||
end | |||
def mark_as_read(participation) do | |||
__MODULE__ | |||
|> where(id: ^participation.id) | |||
|> update(set: [read: true]) | |||
|> select([p], p) | |||
|> Repo.update_all([]) | |||
|> case do | |||
{1, [participation]} -> | |||
participation = Repo.preload(participation, :user) | |||
User.set_unread_conversation_count(participation.user) | |||
{:ok, participation} | |||
error -> | |||
error | |||
end | |||
def mark_as_read(%__MODULE__{} = participation) do | |||
participation | |||
|> change(read: true) | |||
|> Repo.update() | |||
end | |||
def mark_all_as_read(%User{local: true} = user, %User{} = target_user) do | |||
@@ -93,7 +82,6 @@ defmodule Pleroma.Conversation.Participation do | |||
|> update([p], set: [read: true]) | |||
|> Repo.update_all([]) | |||
{:ok, user} = User.set_unread_conversation_count(user) | |||
{:ok, user, []} | |||
end | |||
@@ -108,7 +96,6 @@ defmodule Pleroma.Conversation.Participation do | |||
|> select([p], p) | |||
|> Repo.update_all([]) | |||
{:ok, user} = User.set_unread_conversation_count(user) | |||
{:ok, user, participations} | |||
end | |||
@@ -220,6 +207,12 @@ defmodule Pleroma.Conversation.Participation do | |||
{:ok, Repo.preload(participation, :recipients, force: true)} | |||
end | |||
@spec unread_count(User.t()) :: integer() | |||
def unread_count(%User{id: user_id}) do | |||
from(q in __MODULE__, where: q.user_id == ^user_id and q.read == false) | |||
|> Repo.aggregate(:count, :id) | |||
end | |||
def unread_conversation_count_for_user(user) do | |||
from(p in __MODULE__, | |||
where: p.user_id == ^user.id, | |||
@@ -129,7 +129,6 @@ defmodule Pleroma.User do | |||
field(:hide_followers, :boolean, default: false) | |||
field(:hide_follows, :boolean, default: false) | |||
field(:hide_favorites, :boolean, default: true) | |||
field(:unread_conversation_count, :integer, default: 0) | |||
field(:pinned_activities, {:array, :string}, default: []) | |||
field(:email_notifications, :map, default: %{"digest" => false}) | |||
field(:mascot, :map, default: nil) | |||
@@ -1295,47 +1294,6 @@ defmodule Pleroma.User do | |||
|> update_and_set_cache() | |||
end | |||
def set_unread_conversation_count(%User{local: true} = user) do | |||
unread_query = Participation.unread_conversation_count_for_user(user) | |||
User | |||
|> join(:inner, [u], p in subquery(unread_query)) | |||
|> update([u, p], | |||
set: [unread_conversation_count: p.count] | |||
) | |||
|> where([u], u.id == ^user.id) | |||
|> select([u], u) | |||
|> Repo.update_all([]) | |||
|> case do | |||
{1, [user]} -> set_cache(user) | |||
_ -> {:error, user} | |||
end | |||
end | |||
def set_unread_conversation_count(user), do: {:ok, user} | |||
def increment_unread_conversation_count(conversation, %User{local: true} = user) do | |||
unread_query = | |||
Participation.unread_conversation_count_for_user(user) | |||
|> where([p], p.conversation_id == ^conversation.id) | |||
User | |||
|> join(:inner, [u], p in subquery(unread_query)) | |||
|> update([u, p], | |||
inc: [unread_conversation_count: 1] | |||
) | |||
|> where([u], u.id == ^user.id) | |||
|> where([u, p], p.count == 0) | |||
|> select([u], u) | |||
|> Repo.update_all([]) | |||
|> case do | |||
{1, [user]} -> set_cache(user) | |||
_ -> {:error, user} | |||
end | |||
end | |||
def increment_unread_conversation_count(_, user), do: {:ok, user} | |||
@spec get_users_from_set([String.t()], keyword()) :: [User.t()] | |||
def get_users_from_set(ap_ids, opts \\ []) do | |||
local_only = Keyword.get(opts, :local_only, true) | |||
@@ -386,7 +386,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do | |||
data | |||
|> Kernel.put_in( | |||
[:pleroma, :unread_conversation_count], | |||
user.unread_conversation_count | |||
Pleroma.Conversation.Participation.unread_count(user) | |||
) | |||
end | |||
@@ -0,0 +1,38 @@ | |||
defmodule Pleroma.Repo.Migrations.RemoveUnreadConversationCountFromUser do | |||
use Ecto.Migration | |||
import Ecto.Query | |||
alias Pleroma.Repo | |||
def up do | |||
alter table(:users) do | |||
remove_if_exists(:unread_conversation_count, :integer) | |||
end | |||
end | |||
def down do | |||
alter table(:users) do | |||
add_if_not_exists(:unread_conversation_count, :integer, default: 0) | |||
end | |||
flush() | |||
recalc_unread_conversation_count() | |||
end | |||
defp recalc_unread_conversation_count do | |||
participations_subquery = | |||
from( | |||
p in "conversation_participations", | |||
where: p.read == false, | |||
group_by: p.user_id, | |||
select: %{user_id: p.user_id, unread_conversation_count: count(p.id)} | |||
) | |||
from( | |||
u in "users", | |||
join: p in subquery(participations_subquery), | |||
on: p.user_id == u.id, | |||
update: [set: [unread_conversation_count: p.unread_conversation_count]] | |||
) | |||
|> Repo.update_all([]) | |||
end | |||
end |
@@ -0,0 +1,12 @@ | |||
defmodule Pleroma.Repo.Migrations.AddUnreadIndexToConversationParticipation do | |||
use Ecto.Migration | |||
def change do | |||
create( | |||
index(:conversation_participations, [:user_id], | |||
where: "read = false", | |||
name: "unread_conversation_participation_count_index" | |||
) | |||
) | |||
end | |||
end |
@@ -37,9 +37,8 @@ defmodule Pleroma.Conversation.ParticipationTest do | |||
[%{read: true}] = Participation.for_user(user) | |||
[%{read: false} = participation] = Participation.for_user(other_user) | |||
assert User.get_cached_by_id(user.id).unread_conversation_count == 0 | |||
assert User.get_cached_by_id(other_user.id).unread_conversation_count == 1 | |||
assert Participation.unread_count(user) == 0 | |||
assert Participation.unread_count(other_user) == 1 | |||
{:ok, _} = | |||
CommonAPI.post(other_user, %{ | |||
@@ -54,8 +53,8 @@ defmodule Pleroma.Conversation.ParticipationTest do | |||
[%{read: false}] = Participation.for_user(user) | |||
[%{read: true}] = Participation.for_user(other_user) | |||
assert User.get_cached_by_id(user.id).unread_conversation_count == 1 | |||
assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0 | |||
assert Participation.unread_count(user) == 1 | |||
assert Participation.unread_count(other_user) == 0 | |||
end | |||
test "for a new conversation, it sets the recipents of the participation" do | |||
@@ -264,7 +263,7 @@ defmodule Pleroma.Conversation.ParticipationTest do | |||
assert [%{read: false}, %{read: false}, %{read: false}, %{read: false}] = | |||
Participation.for_user(blocker) | |||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 4 | |||
assert Participation.unread_count(blocker) == 4 | |||
{:ok, _user_relationship} = User.block(blocker, blocked) | |||
@@ -272,15 +271,15 @@ defmodule Pleroma.Conversation.ParticipationTest do | |||
assert [%{read: true}, %{read: true}, %{read: true}, %{read: false}] = | |||
Participation.for_user(blocker) | |||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 1 | |||
assert Participation.unread_count(blocker) == 1 | |||
# The conversation is not marked as read for the blocked user | |||
assert [_, _, %{read: false}] = Participation.for_user(blocked) | |||
assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1 | |||
assert Participation.unread_count(blocker) == 1 | |||
# The conversation is not marked as read for the third user | |||
assert [%{read: false}, _, _] = Participation.for_user(third_user) | |||
assert User.get_cached_by_id(third_user.id).unread_conversation_count == 1 | |||
assert Participation.unread_count(third_user) == 1 | |||
end | |||
test "the new conversation with the blocked user is not marked as unread " do | |||
@@ -298,7 +297,7 @@ defmodule Pleroma.Conversation.ParticipationTest do | |||
}) | |||
assert [%{read: true}] = Participation.for_user(blocker) | |||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 | |||
assert Participation.unread_count(blocker) == 0 | |||
# When the blocked user is a recipient | |||
{:ok, _direct2} = | |||
@@ -308,10 +307,10 @@ defmodule Pleroma.Conversation.ParticipationTest do | |||
}) | |||
assert [%{read: true}, %{read: true}] = Participation.for_user(blocker) | |||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 | |||
assert Participation.unread_count(blocker) == 0 | |||
assert [%{read: false}, _] = Participation.for_user(blocked) | |||
assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1 | |||
assert Participation.unread_count(blocked) == 1 | |||
end | |||
test "the conversation with the blocked user is not marked as unread on a reply" do | |||
@@ -327,8 +326,8 @@ defmodule Pleroma.Conversation.ParticipationTest do | |||
{:ok, _user_relationship} = User.block(blocker, blocked) | |||
assert [%{read: true}] = Participation.for_user(blocker) | |||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 | |||
assert Participation.unread_count(blocker) == 0 | |||
assert [blocked_participation] = Participation.for_user(blocked) | |||
# When it's a reply from the blocked user | |||
@@ -340,8 +339,8 @@ defmodule Pleroma.Conversation.ParticipationTest do | |||
}) | |||
assert [%{read: true}] = Participation.for_user(blocker) | |||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 | |||
assert Participation.unread_count(blocker) == 0 | |||
assert [third_user_participation] = Participation.for_user(third_user) | |||
# When it's a reply from the third user | |||
@@ -353,11 +352,12 @@ defmodule Pleroma.Conversation.ParticipationTest do | |||
}) | |||
assert [%{read: true}] = Participation.for_user(blocker) | |||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 | |||
assert Participation.unread_count(blocker) == 0 | |||
# Marked as unread for the blocked user | |||
assert [%{read: false}] = Participation.for_user(blocked) | |||
assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1 | |||
assert Participation.unread_count(blocked) == 1 | |||
end | |||
end | |||
end |
@@ -5,6 +5,7 @@ | |||
defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do | |||
use Pleroma.Web.ConnCase | |||
alias Pleroma.Conversation.Participation | |||
alias Pleroma.User | |||
alias Pleroma.Web.CommonAPI | |||
@@ -28,10 +29,10 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do | |||
user_three: user_three, | |||
conn: conn | |||
} do | |||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 | |||
assert Participation.unread_count(user_two) == 0 | |||
{:ok, direct} = create_direct_message(user_one, [user_two, user_three]) | |||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 | |||
assert Participation.unread_count(user_two) == 1 | |||
{:ok, _follower_only} = | |||
CommonAPI.post(user_one, %{ | |||
@@ -59,7 +60,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do | |||
assert is_binary(res_id) | |||
assert unread == false | |||
assert res_last_status["id"] == direct.id | |||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 | |||
assert Participation.unread_count(user_one) == 0 | |||
end | |||
test "observes limit params", %{ | |||
@@ -134,8 +135,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do | |||
user_two = insert(:user) | |||
{:ok, direct} = create_direct_message(user_one, [user_two]) | |||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 | |||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 | |||
assert Participation.unread_count(user_one) == 0 | |||
assert Participation.unread_count(user_two) == 1 | |||
user_two_conn = | |||
build_conn() | |||
@@ -155,8 +156,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do | |||
|> post("/api/v1/conversations/#{direct_conversation_id}/read") | |||
|> json_response_and_validate_schema(200) | |||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 | |||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 | |||
assert Participation.unread_count(user_one) == 0 | |||
assert Participation.unread_count(user_two) == 0 | |||
# The conversation is marked as unread on reply | |||
{:ok, _} = | |||
@@ -171,8 +172,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do | |||
|> get("/api/v1/conversations") | |||
|> json_response_and_validate_schema(200) | |||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1 | |||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 | |||
assert Participation.unread_count(user_one) == 1 | |||
assert Participation.unread_count(user_two) == 0 | |||
# A reply doesn't increment the user's unread_conversation_count if the conversation is unread | |||
{:ok, _} = | |||
@@ -182,8 +183,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do | |||
in_reply_to_status_id: direct.id | |||
}) | |||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1 | |||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 | |||
assert Participation.unread_count(user_one) == 1 | |||
assert Participation.unread_count(user_two) == 0 | |||
end | |||
test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do | |||
@@ -121,7 +121,7 @@ defmodule Pleroma.Web.PleromaAPI.ConversationControllerTest do | |||
[participation2, participation1] = Participation.for_user(other_user) | |||
assert Participation.get(participation2.id).read == false | |||
assert Participation.get(participation1.id).read == false | |||
assert User.get_cached_by_id(other_user.id).unread_conversation_count == 2 | |||
assert Participation.unread_count(other_user) == 2 | |||
[%{"unread" => false}, %{"unread" => false}] = | |||
conn | |||
@@ -131,6 +131,6 @@ defmodule Pleroma.Web.PleromaAPI.ConversationControllerTest do | |||
[participation2, participation1] = Participation.for_user(other_user) | |||
assert Participation.get(participation2.id).read == true | |||
assert Participation.get(participation1.id).read == true | |||
assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0 | |||
assert Participation.unread_count(other_user) == 0 | |||
end | |||
end |