@@ -151,4 +151,10 @@ defmodule Pleroma.FollowingRelationship do | |||
) | |||
|> Repo.all() | |||
end | |||
def find(following_relationships, follower, following) do | |||
Enum.find(following_relationships, fn | |||
fr -> fr.follower_id == follower.id and fr.following_id == following.id | |||
end) | |||
end | |||
end |
@@ -218,7 +218,10 @@ defmodule Pleroma.User do | |||
end | |||
end | |||
@doc "Dumps id to SQL-compatible format" | |||
@doc """ | |||
Dumps Flake Id to SQL-compatible format (16-byte UUID). | |||
E.g. "9pQtDGXuq4p3VlcJEm" -> <<0, 0, 1, 110, 179, 218, 42, 92, 213, 41, 44, 227, 95, 213, 0, 0>> | |||
""" | |||
def binary_id(source_id) when is_binary(source_id) do | |||
with {:ok, dumped_id} <- FlakeId.Ecto.CompatType.dump(source_id) do | |||
dumped_id | |||
@@ -8,6 +8,7 @@ defmodule Pleroma.UserRelationship do | |||
import Ecto.Changeset | |||
import Ecto.Query | |||
alias Pleroma.FollowingRelationship | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
alias Pleroma.UserRelationship | |||
@@ -124,6 +125,25 @@ defmodule Pleroma.UserRelationship do | |||
end | |||
end | |||
@doc ":relationships option for StatusView / AccountView / NotificationView" | |||
def view_relationships_option(nil = _reading_user, _actors) do | |||
%{user_relationships: [], following_relationships: []} | |||
end | |||
def view_relationships_option(%User{} = reading_user, actors) do | |||
user_relationships = | |||
UserRelationship.dictionary( | |||
[reading_user], | |||
actors, | |||
[:block, :mute, :notification_mute, :reblog_mute], | |||
[:block, :inverse_subscription] | |||
) | |||
following_relationships = FollowingRelationship.all_between_user_sets([reading_user], actors) | |||
%{user_relationships: user_relationships, following_relationships: following_relationships} | |||
end | |||
defp validate_not_self_relationship(%Ecto.Changeset{} = changeset) do | |||
changeset | |||
|> validate_change(:target_id, fn _, target_id -> | |||
@@ -5,20 +5,23 @@ | |||
defmodule Pleroma.Web.MastodonAPI.AccountView do | |||
use Pleroma.Web, :view | |||
alias Pleroma.FollowingRelationship | |||
alias Pleroma.User | |||
alias Pleroma.UserRelationship | |||
alias Pleroma.Web.CommonAPI.Utils | |||
alias Pleroma.Web.MastodonAPI.AccountView | |||
alias Pleroma.Web.MastodonAPI.StatusView | |||
alias Pleroma.Web.MediaProxy | |||
defp find_following_rel(following_relationships, follower, following) do | |||
Enum.find(following_relationships, fn | |||
fr -> fr.follower_id == follower.id and fr.following_id == following.id | |||
end) | |||
end | |||
def render("index.json", %{users: users} = opts) do | |||
relationships_opt = | |||
if Map.has_key?(opts, :relationships) do | |||
opts[:relationships] | |||
else | |||
UserRelationship.view_relationships_option(opts[:for], users) | |||
end | |||
opts = Map.put(opts, :relationships, relationships_opt) | |||
users | |||
|> render_many(AccountView, "show.json", opts) | |||
|> Enum.filter(&Enum.any?/1) | |||
@@ -53,7 +56,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do | |||
follow_state = | |||
if following_relationships do | |||
user_to_target_following_relation = | |||
find_following_rel(following_relationships, reading_user, target) | |||
FollowingRelationship.find(following_relationships, reading_user, target) | |||
User.get_follow_state(reading_user, target, user_to_target_following_relation) | |||
else | |||
@@ -62,7 +65,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do | |||
followed_by = | |||
if following_relationships do | |||
case find_following_rel(following_relationships, target, reading_user) do | |||
case FollowingRelationship.find(following_relationships, target, reading_user) do | |||
%{state: "accept"} -> true | |||
_ -> false | |||
end | |||
@@ -70,7 +73,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do | |||
User.following?(target, reading_user) | |||
end | |||
# NOTE: adjust StatusView.relationships_opts/2 if adding new relation-related flags | |||
# NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags | |||
%{ | |||
id: to_string(target.id), | |||
following: follow_state == "accept", | |||
@@ -129,11 +132,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do | |||
} | |||
end | |||
def render("relationships.json", %{user: user, targets: targets}) do | |||
relationships_opts = StatusView.relationships_opts(user, targets) | |||
opts = %{as: :target, user: user, relationships: relationships_opts} | |||
def render("relationships.json", %{user: user, targets: targets} = opts) do | |||
relationships_opt = | |||
if Map.has_key?(opts, :relationships) do | |||
opts[:relationships] | |||
else | |||
UserRelationship.view_relationships_option(user, targets) | |||
end | |||
render_many(targets, AccountView, "relationship.json", opts) | |||
render_opts = %{as: :target, user: user, relationships: relationships_opt} | |||
render_many(targets, AccountView, "relationship.json", render_opts) | |||
end | |||
defp do_render("show.json", %{user: user} = opts) do | |||
@@ -8,12 +8,13 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do | |||
alias Pleroma.Activity | |||
alias Pleroma.Notification | |||
alias Pleroma.User | |||
alias Pleroma.UserRelationship | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.MastodonAPI.AccountView | |||
alias Pleroma.Web.MastodonAPI.NotificationView | |||
alias Pleroma.Web.MastodonAPI.StatusView | |||
def render("index.json", %{notifications: notifications, for: reading_user}) do | |||
def render("index.json", %{notifications: notifications, for: reading_user} = opts) do | |||
activities = Enum.map(notifications, & &1.activity) | |||
parent_activities = | |||
@@ -30,21 +31,28 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do | |||
|> Activity.with_preloaded_object(:left) | |||
|> Pleroma.Repo.all() | |||
move_activities_targets = | |||
activities | |||
|> Enum.filter(&(Activity.mastodon_notification_type(&1) == "move")) | |||
|> Enum.map(&User.get_cached_by_ap_id(&1.data["target"])) | |||
actors = | |||
activities | |||
|> Enum.map(fn a -> User.get_cached_by_ap_id(a.data["actor"]) end) | |||
|> Enum.filter(& &1) | |||
|> Kernel.++(move_activities_targets) | |||
relationships_opt = | |||
if Map.has_key?(opts, :relationships) do | |||
opts[:relationships] | |||
else | |||
move_activities_targets = | |||
activities | |||
|> Enum.filter(&(Activity.mastodon_notification_type(&1) == "move")) | |||
|> Enum.map(&User.get_cached_by_ap_id(&1.data["target"])) | |||
actors = | |||
activities | |||
|> Enum.map(fn a -> User.get_cached_by_ap_id(a.data["actor"]) end) | |||
|> Enum.filter(& &1) | |||
|> Kernel.++(move_activities_targets) | |||
UserRelationship.view_relationships_option(reading_user, actors) | |||
end | |||
opts = %{ | |||
for: reading_user, | |||
parent_activities: parent_activities, | |||
relationships: StatusView.relationships_opts(reading_user, actors) | |||
relationships: relationships_opt | |||
} | |||
safe_render_many(notifications, NotificationView, "show.json", opts) | |||
@@ -85,27 +93,27 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do | |||
} | |||
} | |||
relationships_opts = %{relationships: opts[:relationships]} | |||
relationships_opt = %{relationships: opts[:relationships]} | |||
case mastodon_type do | |||
"mention" -> | |||
put_status(response, activity, reading_user, relationships_opts) | |||
put_status(response, activity, reading_user, relationships_opt) | |||
"favourite" -> | |||
put_status(response, parent_activity_fn.(), reading_user, relationships_opts) | |||
put_status(response, parent_activity_fn.(), reading_user, relationships_opt) | |||
"reblog" -> | |||
put_status(response, parent_activity_fn.(), reading_user, relationships_opts) | |||
put_status(response, parent_activity_fn.(), reading_user, relationships_opt) | |||
"move" -> | |||
put_target(response, activity, reading_user, relationships_opts) | |||
put_target(response, activity, reading_user, relationships_opt) | |||
"follow" -> | |||
response | |||
"pleroma:emoji_reaction" -> | |||
response | |||
|> put_status(parent_activity_fn.(), reading_user, relationships_opts) | |||
|> put_status(parent_activity_fn.(), reading_user, relationships_opt) | |||
|> put_emoji(activity) | |||
_ -> | |||
@@ -9,7 +9,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
alias Pleroma.Activity | |||
alias Pleroma.ActivityExpiration | |||
alias Pleroma.FollowingRelationship | |||
alias Pleroma.HTML | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
@@ -72,24 +71,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
present?(user && user.ap_id in (object.data["announcements"] || [])) | |||
end | |||
def relationships_opts(_reading_user = nil, _actors) do | |||
%{user_relationships: [], following_relationships: []} | |||
end | |||
def relationships_opts(reading_user, actors) do | |||
user_relationships = | |||
UserRelationship.dictionary( | |||
[reading_user], | |||
actors, | |||
[:block, :mute, :notification_mute, :reblog_mute], | |||
[:block, :inverse_subscription] | |||
) | |||
following_relationships = FollowingRelationship.all_between_user_sets([reading_user], actors) | |||
%{user_relationships: user_relationships, following_relationships: following_relationships} | |||
end | |||
def render("index.json", opts) do | |||
# To do: check AdminAPIControllerTest on the reasons behind nil activities in the list | |||
activities = Enum.filter(opts.activities, & &1) | |||
@@ -105,13 +86,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
|> Activity.with_set_thread_muted_field(opts[:for]) | |||
|> Repo.all() | |||
actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"])) | |||
relationships_opt = | |||
if Map.has_key?(opts, :relationships) do | |||
opts[:relationships] | |||
else | |||
actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"])) | |||
UserRelationship.view_relationships_option(opts[:for], actors) | |||
end | |||
opts = | |||
opts | |||
|> Map.put(:replied_to_activities, replied_to_activities) | |||
|> Map.put(:parent_activities, parent_activities) | |||
|> Map.put(:relationships, relationships_opts(opts[:for], actors)) | |||
|> Map.put(:relationships, relationships_opt) | |||
safe_render_many(activities, StatusView, "show.json", opts) | |||
end | |||
@@ -4,8 +4,11 @@ | |||
defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
use Pleroma.DataCase | |||
import Pleroma.Factory | |||
alias Pleroma.User | |||
alias Pleroma.UserRelationship | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.MastodonAPI.AccountView | |||
@@ -182,6 +185,29 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
end | |||
describe "relationship" do | |||
defp test_relationship_rendering(user, other_user, expected_result) do | |||
opts = %{user: user, target: other_user} | |||
assert expected_result == AccountView.render("relationship.json", opts) | |||
relationships_opt = UserRelationship.view_relationships_option(user, [other_user]) | |||
opts = Map.put(opts, :relationships, relationships_opt) | |||
assert expected_result == AccountView.render("relationship.json", opts) | |||
end | |||
@blank_response %{ | |||
following: false, | |||
followed_by: false, | |||
blocking: false, | |||
blocked_by: false, | |||
muting: false, | |||
muting_notifications: false, | |||
subscribing: false, | |||
requested: false, | |||
domain_blocking: false, | |||
showing_reblogs: true, | |||
endorsed: false | |||
} | |||
test "represent a relationship for the following and followed user" do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
@@ -192,23 +218,21 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
{:ok, _user_relationships} = User.mute(user, other_user, true) | |||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, other_user) | |||
expected = %{ | |||
id: to_string(other_user.id), | |||
following: true, | |||
followed_by: true, | |||
blocking: false, | |||
blocked_by: false, | |||
muting: true, | |||
muting_notifications: true, | |||
subscribing: true, | |||
requested: false, | |||
domain_blocking: false, | |||
showing_reblogs: false, | |||
endorsed: false | |||
} | |||
assert expected == | |||
AccountView.render("relationship.json", %{user: user, target: other_user}) | |||
expected = | |||
Map.merge( | |||
@blank_response, | |||
%{ | |||
following: true, | |||
followed_by: true, | |||
muting: true, | |||
muting_notifications: true, | |||
subscribing: true, | |||
showing_reblogs: false, | |||
id: to_string(other_user.id) | |||
} | |||
) | |||
test_relationship_rendering(user, other_user, expected) | |||
end | |||
test "represent a relationship for the blocking and blocked user" do | |||
@@ -220,23 +244,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
{:ok, _user_relationship} = User.block(user, other_user) | |||
{:ok, _user_relationship} = User.block(other_user, user) | |||
expected = %{ | |||
id: to_string(other_user.id), | |||
following: false, | |||
followed_by: false, | |||
blocking: true, | |||
blocked_by: true, | |||
muting: false, | |||
muting_notifications: false, | |||
subscribing: false, | |||
requested: false, | |||
domain_blocking: false, | |||
showing_reblogs: true, | |||
endorsed: false | |||
} | |||
expected = | |||
Map.merge( | |||
@blank_response, | |||
%{following: false, blocking: true, blocked_by: true, id: to_string(other_user.id)} | |||
) | |||
assert expected == | |||
AccountView.render("relationship.json", %{user: user, target: other_user}) | |||
test_relationship_rendering(user, other_user, expected) | |||
end | |||
test "represent a relationship for the user blocking a domain" do | |||
@@ -245,8 +259,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
{:ok, user} = User.block_domain(user, "bad.site") | |||
assert %{domain_blocking: true, blocking: false} = | |||
AccountView.render("relationship.json", %{user: user, target: other_user}) | |||
expected = | |||
Map.merge( | |||
@blank_response, | |||
%{domain_blocking: true, blocking: false, id: to_string(other_user.id)} | |||
) | |||
test_relationship_rendering(user, other_user, expected) | |||
end | |||
test "represent a relationship for the user with a pending follow request" do | |||
@@ -257,23 +276,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
user = User.get_cached_by_id(user.id) | |||
other_user = User.get_cached_by_id(other_user.id) | |||
expected = %{ | |||
id: to_string(other_user.id), | |||
following: false, | |||
followed_by: false, | |||
blocking: false, | |||
blocked_by: false, | |||
muting: false, | |||
muting_notifications: false, | |||
subscribing: false, | |||
requested: true, | |||
domain_blocking: false, | |||
showing_reblogs: true, | |||
endorsed: false | |||
} | |||
expected = | |||
Map.merge( | |||
@blank_response, | |||
%{requested: true, following: false, id: to_string(other_user.id)} | |||
) | |||
assert expected == | |||
AccountView.render("relationship.json", %{user: user, target: other_user}) | |||
test_relationship_rendering(user, other_user, expected) | |||
end | |||
end | |||
@@ -16,6 +16,21 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do | |||
alias Pleroma.Web.MastodonAPI.StatusView | |||
import Pleroma.Factory | |||
defp test_notifications_rendering(notifications, user, expected_result) do | |||
result = NotificationView.render("index.json", %{notifications: notifications, for: user}) | |||
assert expected_result == result | |||
result = | |||
NotificationView.render("index.json", %{ | |||
notifications: notifications, | |||
for: user, | |||
relationships: nil | |||
}) | |||
assert expected_result == result | |||
end | |||
test "Mention notification" do | |||
user = insert(:user) | |||
mentioned_user = insert(:user) | |||
@@ -32,10 +47,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do | |||
created_at: Utils.to_masto_date(notification.inserted_at) | |||
} | |||
result = | |||
NotificationView.render("index.json", %{notifications: [notification], for: mentioned_user}) | |||
assert [expected] == result | |||
test_notifications_rendering([notification], mentioned_user, [expected]) | |||
end | |||
test "Favourite notification" do | |||
@@ -55,9 +67,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do | |||
created_at: Utils.to_masto_date(notification.inserted_at) | |||
} | |||
result = NotificationView.render("index.json", %{notifications: [notification], for: user}) | |||
assert [expected] == result | |||
test_notifications_rendering([notification], user, [expected]) | |||
end | |||
test "Reblog notification" do | |||
@@ -77,9 +87,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do | |||
created_at: Utils.to_masto_date(notification.inserted_at) | |||
} | |||
result = NotificationView.render("index.json", %{notifications: [notification], for: user}) | |||
assert [expected] == result | |||
test_notifications_rendering([notification], user, [expected]) | |||
end | |||
test "Follow notification" do | |||
@@ -96,16 +104,12 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do | |||
created_at: Utils.to_masto_date(notification.inserted_at) | |||
} | |||
result = | |||
NotificationView.render("index.json", %{notifications: [notification], for: followed}) | |||
assert [expected] == result | |||
test_notifications_rendering([notification], followed, [expected]) | |||
User.perform(:delete, follower) | |||
notification = Notification |> Repo.one() |> Repo.preload(:activity) | |||
assert [] == | |||
NotificationView.render("index.json", %{notifications: [notification], for: followed}) | |||
test_notifications_rendering([notification], followed, []) | |||
end | |||
test "Move notification" do | |||
@@ -131,8 +135,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do | |||
created_at: Utils.to_masto_date(notification.inserted_at) | |||
} | |||
assert [expected] == | |||
NotificationView.render("index.json", %{notifications: [notification], for: follower}) | |||
test_notifications_rendering([notification], follower, [expected]) | |||
end | |||
test "EmojiReact notification" do | |||
@@ -158,7 +161,6 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do | |||
created_at: Utils.to_masto_date(notification.inserted_at) | |||
} | |||
assert expected == | |||
NotificationView.render("show.json", %{notification: notification, for: user}) | |||
test_notifications_rendering([notification], user, [expected]) | |||
end | |||
end |
@@ -12,10 +12,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
alias Pleroma.UserRelationship | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.CommonAPI.Utils | |||
alias Pleroma.Web.MastodonAPI.AccountView | |||
alias Pleroma.Web.MastodonAPI.StatusView | |||
import Pleroma.Factory | |||
import Tesla.Mock | |||
@@ -212,12 +214,21 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do | |||
{:ok, _user_relationships} = User.mute(user, other_user) | |||
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"}) | |||
status = StatusView.render("show.json", %{activity: activity}) | |||
relationships_opt = UserRelationship.view_relationships_option(user, [other_user]) | |||
opts = %{activity: activity} | |||
status = StatusView.render("show.json", opts) | |||
assert status.muted == false | |||
status = StatusView.render("show.json", %{activity: activity, for: user}) | |||
status = StatusView.render("show.json", Map.put(opts, :relationships, relationships_opt)) | |||
assert status.muted == false | |||
for_opts = %{activity: activity, for: user} | |||
status = StatusView.render("show.json", for_opts) | |||
assert status.muted == true | |||
status = StatusView.render("show.json", Map.put(for_opts, :relationships, relationships_opt)) | |||
assert status.muted == true | |||
end | |||