@@ -312,19 +312,12 @@ defmodule Pleroma.Web.ActivityPub.Utils do | |||||
|> Map.put("content", emoji) | |> Map.put("content", emoji) | ||||
end | end | ||||
@spec update_element_in_object(String.t(), list(any), Object.t()) :: | |||||
@spec update_element_in_object(String.t(), list(any), Object.t(), integer() | nil) :: | |||||
{:ok, Object.t()} | {:error, Ecto.Changeset.t()} | {:ok, Object.t()} | {:error, Ecto.Changeset.t()} | ||||
def update_element_in_object(property, element, object) do | |||||
def update_element_in_object(property, element, object, count \\ nil) do | |||||
length = | length = | ||||
if is_map(element) do | |||||
element | |||||
|> Map.values() | |||||
|> List.flatten() | |||||
|> length() | |||||
else | |||||
element | |||||
|> length() | |||||
end | |||||
count || | |||||
length(element) | |||||
data = | data = | ||||
Map.merge( | Map.merge( | ||||
@@ -344,29 +337,52 @@ defmodule Pleroma.Web.ActivityPub.Utils do | |||||
%Activity{data: %{"content" => emoji, "actor" => actor}}, | %Activity{data: %{"content" => emoji, "actor" => actor}}, | ||||
object | object | ||||
) do | ) do | ||||
reactions = object.data["reactions"] || %{} | |||||
emoji_actors = reactions[emoji] || [] | |||||
new_emoji_actors = [actor | emoji_actors] |> Enum.uniq() | |||||
new_reactions = Map.put(reactions, emoji, new_emoji_actors) | |||||
update_element_in_object("reaction", new_reactions, object) | |||||
reactions = object.data["reactions"] || [] | |||||
new_reactions = | |||||
case Enum.find_index(reactions, fn [candidate, _] -> emoji == candidate end) do | |||||
nil -> | |||||
reactions ++ [[emoji, [actor]]] | |||||
index -> | |||||
List.update_at( | |||||
reactions, | |||||
index, | |||||
fn [emoji, users] -> [emoji, Enum.uniq([actor | users])] end | |||||
) | |||||
end | |||||
count = emoji_count(new_reactions) | |||||
update_element_in_object("reaction", new_reactions, object, count) | |||||
end | |||||
def emoji_count(reactions_list) do | |||||
Enum.reduce(reactions_list, 0, fn [_, users], acc -> acc + length(users) end) | |||||
end | end | ||||
def remove_emoji_reaction_from_object( | def remove_emoji_reaction_from_object( | ||||
%Activity{data: %{"content" => emoji, "actor" => actor}}, | %Activity{data: %{"content" => emoji, "actor" => actor}}, | ||||
object | object | ||||
) do | ) do | ||||
reactions = object.data["reactions"] || %{} | |||||
emoji_actors = reactions[emoji] || [] | |||||
new_emoji_actors = List.delete(emoji_actors, actor) | |||||
reactions = object.data["reactions"] || [] | |||||
new_reactions = | new_reactions = | ||||
if new_emoji_actors == [] do | |||||
Map.delete(reactions, emoji) | |||||
else | |||||
Map.put(reactions, emoji, new_emoji_actors) | |||||
case Enum.find_index(reactions, fn [candidate, _] -> emoji == candidate end) do | |||||
nil -> | |||||
reactions | |||||
index -> | |||||
List.update_at( | |||||
reactions, | |||||
index, | |||||
fn [emoji, users] -> [emoji, List.delete(users, actor)] end | |||||
) | |||||
|> Enum.reject(fn [_, users] -> Enum.empty?(users) end) | |||||
end | end | ||||
update_element_in_object("reaction", new_reactions, object) | |||||
count = emoji_count(new_reactions) | |||||
update_element_in_object("reaction", new_reactions, object, count) | |||||
end | end | ||||
@spec add_like_to_object(Activity.t(), Object.t()) :: | @spec add_like_to_object(Activity.t(), Object.t()) :: | ||||
@@ -255,12 +255,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||||
emoji_reactions = | emoji_reactions = | ||||
with %{data: %{"reactions" => emoji_reactions}} <- object do | with %{data: %{"reactions" => emoji_reactions}} <- object do | ||||
Enum.map(emoji_reactions, fn {emoji, users} -> | |||||
{emoji, length(users)} | |||||
Enum.map(emoji_reactions, fn [emoji, users] -> | |||||
[emoji, length(users)] | |||||
end) | end) | ||||
|> Enum.into(%{}) | |||||
else | else | ||||
_ -> %{} | |||||
_ -> [] | |||||
end | end | ||||
%{ | %{ | ||||
@@ -43,21 +43,21 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do | |||||
def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do | def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do | ||||
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id), | with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id), | ||||
%Object{data: %{"reactions" => emoji_reactions}} <- Object.normalize(activity) do | |||||
%Object{data: %{"reactions" => emoji_reactions}} when is_list(emoji_reactions) <- | |||||
Object.normalize(activity) do | |||||
reactions = | reactions = | ||||
emoji_reactions | emoji_reactions | ||||
|> Enum.map(fn {emoji, users} -> | |||||
|> Enum.map(fn [emoji, users] -> | |||||
users = Enum.map(users, &User.get_cached_by_ap_id/1) | users = Enum.map(users, &User.get_cached_by_ap_id/1) | ||||
{emoji, AccountView.render("index.json", %{users: users, for: user, as: :user})} | {emoji, AccountView.render("index.json", %{users: users, for: user, as: :user})} | ||||
end) | end) | ||||
|> Enum.into(%{}) | |||||
conn | conn | ||||
|> json(reactions) | |> json(reactions) | ||||
else | else | ||||
_e -> | _e -> | ||||
conn | conn | ||||
|> json(%{}) | |||||
|> json([]) | |||||
end | end | ||||
end | end | ||||
@@ -867,6 +867,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do | |||||
test "adds an emoji reaction activity to the db" do | test "adds an emoji reaction activity to the db" do | ||||
user = insert(:user) | user = insert(:user) | ||||
reactor = insert(:user) | reactor = insert(:user) | ||||
third_user = insert(:user) | |||||
fourth_user = insert(:user) | |||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) | {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) | ||||
assert object = Object.normalize(activity) | assert object = Object.normalize(activity) | ||||
@@ -881,7 +883,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do | |||||
assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]] | assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]] | ||||
assert reaction_activity.data["context"] == object.data["context"] | assert reaction_activity.data["context"] == object.data["context"] | ||||
assert object.data["reaction_count"] == 1 | assert object.data["reaction_count"] == 1 | ||||
assert object.data["reactions"]["🔥"] == [reactor.ap_id] | |||||
assert object.data["reactions"] == [["🔥", [reactor.ap_id]]] | |||||
{:ok, _reaction_activity, object} = ActivityPub.react_with_emoji(third_user, object, "☕") | |||||
assert object.data["reaction_count"] == 2 | |||||
assert object.data["reactions"] == [["🔥", [reactor.ap_id]], ["☕", [third_user.ap_id]]] | |||||
{:ok, _reaction_activity, object} = ActivityPub.react_with_emoji(fourth_user, object, "🔥") | |||||
assert object.data["reaction_count"] == 3 | |||||
assert object.data["reactions"] == [ | |||||
["🔥", [fourth_user.ap_id, reactor.ap_id]], | |||||
["☕", [third_user.ap_id]] | |||||
] | |||||
end | end | ||||
end | end | ||||
@@ -919,7 +935,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do | |||||
object = Object.get_by_ap_id(object.data["id"]) | object = Object.get_by_ap_id(object.data["id"]) | ||||
assert object.data["reaction_count"] == 0 | assert object.data["reaction_count"] == 0 | ||||
assert object.data["reactions"] == %{} | |||||
assert object.data["reactions"] == [] | |||||
end | end | ||||
end | end | ||||
@@ -31,13 +31,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do | |||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "dae cofe??"}) | {:ok, activity} = CommonAPI.post(user, %{"status" => "dae cofe??"}) | ||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, user, "☕") | {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, user, "☕") | ||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") | |||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵") | {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵") | ||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") | |||||
activity = Repo.get(Activity, activity.id) | activity = Repo.get(Activity, activity.id) | ||||
status = StatusView.render("show.json", activity: activity) | status = StatusView.render("show.json", activity: activity) | ||||
assert status[:pleroma][:emoji_reactions]["🍵"] == 1 | |||||
assert status[:pleroma][:emoji_reactions]["☕"] == 2 | |||||
assert status[:pleroma][:emoji_reactions] == [["☕", 2], ["🍵", 1]] | |||||
end | end | ||||
test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do | test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do | ||||
@@ -189,7 +188,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do | |||||
expires_at: nil, | expires_at: nil, | ||||
direct_conversation_id: nil, | direct_conversation_id: nil, | ||||
thread_muted: false, | thread_muted: false, | ||||
emoji_reactions: %{} | |||||
emoji_reactions: [] | |||||
} | } | ||||
} | } | ||||
@@ -62,7 +62,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do | |||||
|> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by") | |> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by") | ||||
|> json_response(200) | |> json_response(200) | ||||
assert result == %{} | |||||
assert result == [] | |||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") | {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") | ||||
@@ -71,7 +71,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do | |||||
|> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by") | |> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by") | ||||
|> json_response(200) | |> json_response(200) | ||||
[represented_user] = result["🎅"] | |||||
[["🎅", [represented_user]]] = result | |||||
assert represented_user["id"] == other_user.id | assert represented_user["id"] == other_user.id | ||||
end | end | ||||