From c2e415143b1dfe5d89eff06fbce6840c445aa5fa Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov <ivantashkinov@gmail.com>
Date: Sun, 22 Mar 2020 21:51:44 +0300
Subject: [PATCH 1/8] WIP: preloading of user relations for timeline/statuses
 rendering (performance improvement).

---
 lib/pleroma/user.ex                           |  6 +-
 lib/pleroma/user_relationship.ex              | 44 ++++++++++++
 .../web/mastodon_api/views/account_view.ex    | 69 ++++++++++++++++---
 .../web/mastodon_api/views/status_view.ex     | 60 ++++++++++++++--
 4 files changed, 160 insertions(+), 19 deletions(-)

diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 12c2ad815..daaa6d86b 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -1642,8 +1642,12 @@ defmodule Pleroma.User do
     |> Repo.all()
   end
 
+  def muting_reblogs?(%User{} = user, %User{} = target) do
+    UserRelationship.reblog_mute_exists?(user, target)
+  end
+
   def showing_reblogs?(%User{} = user, %User{} = target) do
-    not UserRelationship.reblog_mute_exists?(user, target)
+    not muting_reblogs?(user, target)
   end
 
   @doc """
diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex
index 393947942..167a3919c 100644
--- a/lib/pleroma/user_relationship.ex
+++ b/lib/pleroma/user_relationship.ex
@@ -8,6 +8,7 @@ defmodule Pleroma.UserRelationship do
   import Ecto.Changeset
   import Ecto.Query
 
+  alias FlakeId.Ecto.CompatType
   alias Pleroma.Repo
   alias Pleroma.User
   alias Pleroma.UserRelationship
@@ -34,6 +35,10 @@ defmodule Pleroma.UserRelationship do
       do: exists?(unquote(relationship_type), source, target)
   end
 
+  def user_relationship_types, do: Keyword.keys(user_relationship_mappings())
+
+  def user_relationship_mappings, do: UserRelationshipTypeEnum.__enum_map__()
+
   def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do
     user_relationship
     |> cast(params, [:relationship_type, :source_id, :target_id])
@@ -72,6 +77,45 @@ defmodule Pleroma.UserRelationship do
     end
   end
 
+  def dictionary(
+        source_users,
+        target_users,
+        source_to_target_rel_types \\ nil,
+        target_to_source_rel_types \\ nil
+      )
+      when is_list(source_users) and is_list(target_users) do
+    get_bin_ids = fn user ->
+      with {:ok, bin_id} <- CompatType.dump(user.id), do: bin_id
+    end
+
+    source_user_ids = Enum.map(source_users, &get_bin_ids.(&1))
+    target_user_ids = Enum.map(target_users, &get_bin_ids.(&1))
+
+    get_rel_type_codes = fn rel_type -> user_relationship_mappings()[rel_type] end
+
+    source_to_target_rel_types =
+      Enum.map(source_to_target_rel_types || user_relationship_types(), &get_rel_type_codes.(&1))
+
+    target_to_source_rel_types =
+      Enum.map(target_to_source_rel_types || user_relationship_types(), &get_rel_type_codes.(&1))
+
+    __MODULE__
+    |> where(
+      fragment(
+        "(source_id = ANY(?) AND target_id = ANY(?) AND relationship_type = ANY(?)) OR \
+        (source_id = ANY(?) AND target_id = ANY(?) AND relationship_type = ANY(?))",
+        ^source_user_ids,
+        ^target_user_ids,
+        ^source_to_target_rel_types,
+        ^target_user_ids,
+        ^source_user_ids,
+        ^target_to_source_rel_types
+      )
+    )
+    |> select([ur], [ur.relationship_type, ur.source_id, ur.target_id])
+    |> Repo.all()
+  end
+
   defp validate_not_self_relationship(%Ecto.Changeset{} = changeset) do
     changeset
     |> validate_change(:target_id, fn _, target_id ->
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 4ebce73b4..15a579278 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -10,6 +10,19 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
   alias Pleroma.Web.MastodonAPI.AccountView
   alias Pleroma.Web.MediaProxy
 
+  def test_rel(user_relationships, rel_type, source, target, func) do
+    cond do
+      is_nil(source) or is_nil(target) ->
+        false
+
+      user_relationships ->
+        [rel_type, source.id, target.id] in user_relationships
+
+      true ->
+        func.(source, target)
+    end
+  end
+
   def render("index.json", %{users: users} = opts) do
     users
     |> render_many(AccountView, "show.json", opts)
@@ -35,21 +48,50 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
     %{}
   end
 
-  def render("relationship.json", %{user: %User{} = user, target: %User{} = target}) do
-    follow_state = User.get_follow_state(user, target)
+  def render(
+        "relationship.json",
+        %{user: %User{} = reading_user, target: %User{} = target} = opts
+      ) do
+    user_relationships = Map.get(opts, :user_relationships)
 
+    follow_state = User.get_follow_state(reading_user, target)
+
+    # TODO: add a note on adjusting StatusView.user_relationships_opt/1 re: preloading of user relations
     %{
       id: to_string(target.id),
       following: follow_state == "accept",
-      followed_by: User.following?(target, user),
-      blocking: User.blocks_user?(user, target),
-      blocked_by: User.blocks_user?(target, user),
-      muting: User.mutes?(user, target),
-      muting_notifications: User.muted_notifications?(user, target),
-      subscribing: User.subscribed_to?(user, target),
+      followed_by: User.following?(target, reading_user),
+      blocking:
+        test_rel(user_relationships, :block, reading_user, target, &User.blocks_user?(&1, &2)),
+      blocked_by:
+        test_rel(user_relationships, :block, target, reading_user, &User.blocks_user?(&1, &2)),
+      muting: test_rel(user_relationships, :mute, reading_user, target, &User.mutes?(&1, &2)),
+      muting_notifications:
+        test_rel(
+          user_relationships,
+          :notification_mute,
+          reading_user,
+          target,
+          &User.muted_notifications?(&1, &2)
+        ),
+      subscribing:
+        test_rel(
+          user_relationships,
+          :inverse_subscription,
+          target,
+          reading_user,
+          &User.subscribed_to?(&2, &1)
+        ),
       requested: follow_state == "pending",
-      domain_blocking: User.blocks_domain?(user, target),
-      showing_reblogs: User.showing_reblogs?(user, target),
+      domain_blocking: User.blocks_domain?(reading_user, target),
+      showing_reblogs:
+        not test_rel(
+          user_relationships,
+          :reblog_mute,
+          reading_user,
+          target,
+          &User.muting_reblogs?(&1, &2)
+        ),
       endorsed: false
     }
   end
@@ -93,7 +135,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
         }
       end)
 
-    relationship = render("relationship.json", %{user: opts[:for], target: user})
+    relationship =
+      render("relationship.json", %{
+        user: opts[:for],
+        target: user,
+        user_relationships: opts[:user_relationships]
+      })
 
     %{
       id: to_string(user.id),
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index f7469cdff..e0c368ec9 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -13,6 +13,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView 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
@@ -70,11 +71,34 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
     present?(user && user.ap_id in (object.data["announcements"] || []))
   end
 
-  def render("index.json", opts) do
-    replied_to_activities = get_replied_to_activities(opts.activities)
-    opts = Map.put(opts, :replied_to_activities, replied_to_activities)
+  defp user_relationships_opt(opts) do
+    reading_user = opts[:for]
 
-    safe_render_many(opts.activities, StatusView, "show.json", opts)
+    if reading_user do
+      activities = opts[:activities]
+      actors = Enum.map(activities, fn a -> get_user(a.data["actor"]) end)
+
+      UserRelationship.dictionary(
+        [reading_user],
+        actors,
+        [:block, :mute, :notification_mute, :reblog_mute],
+        [:block, :inverse_subscription]
+      )
+    else
+      []
+    end
+  end
+
+  def render("index.json", opts) do
+    activities = opts.activities
+    replied_to_activities = get_replied_to_activities(activities)
+
+    opts =
+      opts
+      |> Map.put(:replied_to_activities, replied_to_activities)
+      |> Map.put(:user_relationships, user_relationships_opt(opts))
+
+    safe_render_many(activities, StatusView, "show.json", opts)
   end
 
   def render(
@@ -107,7 +131,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
       id: to_string(activity.id),
       uri: activity_object.data["id"],
       url: activity_object.data["id"],
-      account: AccountView.render("show.json", %{user: user, for: opts[:for]}),
+      account:
+        AccountView.render("show.json", %{
+          user: user,
+          for: opts[:for],
+          user_relationships: opts[:user_relationships]
+        }),
       in_reply_to_id: nil,
       in_reply_to_account_id: nil,
       reblog: reblogged,
@@ -253,11 +282,28 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
         _ -> []
       end
 
+    user_relationships_opt = opts[:user_relationships]
+
+    muted =
+      thread_muted? ||
+        Pleroma.Web.MastodonAPI.AccountView.test_rel(
+          user_relationships_opt,
+          :mute,
+          opts[:for],
+          user,
+          fn for_user, user -> User.mutes?(for_user, user) end
+        )
+
     %{
       id: to_string(activity.id),
       uri: object.data["id"],
       url: url,
-      account: AccountView.render("show.json", %{user: user, for: opts[:for]}),
+      account:
+        AccountView.render("show.json", %{
+          user: user,
+          for: opts[:for],
+          user_relationships: user_relationships_opt
+        }),
       in_reply_to_id: reply_to && to_string(reply_to.id),
       in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
       reblog: nil,
@@ -270,7 +316,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
       reblogged: reblogged?(activity, opts[:for]),
       favourited: present?(favorited),
       bookmarked: present?(bookmarked),
-      muted: thread_muted? || User.mutes?(opts[:for], user),
+      muted: muted,
       pinned: pinned?(activity, user),
       sensitive: sensitive,
       spoiler_text: summary,

From 3c78e5f3275494b3dc4546e65f19eb3a3c97033a Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov <ivantashkinov@gmail.com>
Date: Mon, 23 Mar 2020 12:01:11 +0300
Subject: [PATCH 2/8] Preloading of follow relations for timeline/statuses
 rendering (performance improvement). Refactoring.

---
 lib/pleroma/following_relationship.ex         | 26 +++++++
 lib/pleroma/user.ex                           |  7 ++
 lib/pleroma/user_relationship.ex              | 13 ++++
 .../web/mastodon_api/views/account_view.ex    | 75 ++++++++++++++-----
 .../web/mastodon_api/views/status_view.ex     | 46 +++++++-----
 5 files changed, 130 insertions(+), 37 deletions(-)

diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex
index a6d281151..dd1696136 100644
--- a/lib/pleroma/following_relationship.ex
+++ b/lib/pleroma/following_relationship.ex
@@ -129,4 +129,30 @@ defmodule Pleroma.FollowingRelationship do
         move_following(origin, target)
     end
   end
+
+  def all_between_user_sets(
+        source_users,
+        target_users
+      )
+      when is_list(source_users) and is_list(target_users) do
+    get_bin_ids = fn user ->
+      with {:ok, bin_id} <- CompatType.dump(user.id), do: bin_id
+    end
+
+    source_user_ids = Enum.map(source_users, &get_bin_ids.(&1))
+    target_user_ids = Enum.map(target_users, &get_bin_ids.(&1))
+
+    __MODULE__
+    |> where(
+      fragment(
+        "(follower_id = ANY(?) AND following_id = ANY(?)) OR \
+        (follower_id = ANY(?) AND following_id = ANY(?))",
+        ^source_user_ids,
+        ^target_user_ids,
+        ^target_user_ids,
+        ^source_user_ids
+      )
+    )
+    |> Repo.all()
+  end
 end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index daaa6d86b..eb72755a0 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -674,7 +674,14 @@ defmodule Pleroma.User do
 
   def get_follow_state(%User{} = follower, %User{} = following) do
     following_relationship = FollowingRelationship.get(follower, following)
+    get_follow_state(follower, following, following_relationship)
+  end
 
+  def get_follow_state(
+        %User{} = follower,
+        %User{} = following,
+        following_relationship
+      ) do
     case {following_relationship, following.local} do
       {nil, false} ->
         case Utils.fetch_latest_follow(follower, following) do
diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex
index 167a3919c..9423e3a42 100644
--- a/lib/pleroma/user_relationship.ex
+++ b/lib/pleroma/user_relationship.ex
@@ -116,6 +116,19 @@ defmodule Pleroma.UserRelationship do
     |> Repo.all()
   end
 
+  def exists?(dictionary, rel_type, source, target, func) do
+    cond do
+      is_nil(source) or is_nil(target) ->
+        false
+
+      dictionary ->
+        [rel_type, source.id, target.id] in dictionary
+
+      true ->
+        func.(source, target)
+    end
+  end
+
   defp validate_not_self_relationship(%Ecto.Changeset{} = changeset) do
     changeset
     |> validate_change(:target_id, fn _, target_id ->
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 15a579278..2fe46158b 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -6,21 +6,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
   use Pleroma.Web, :view
 
   alias Pleroma.User
+  alias Pleroma.UserRelationship
   alias Pleroma.Web.CommonAPI.Utils
   alias Pleroma.Web.MastodonAPI.AccountView
   alias Pleroma.Web.MediaProxy
 
-  def test_rel(user_relationships, rel_type, source, target, func) do
-    cond do
-      is_nil(source) or is_nil(target) ->
-        false
-
-      user_relationships ->
-        [rel_type, source.id, target.id] in user_relationships
-
-      true ->
-        func.(source, target)
-    end
+  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
@@ -53,21 +47,61 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
         %{user: %User{} = reading_user, target: %User{} = target} = opts
       ) do
     user_relationships = Map.get(opts, :user_relationships)
+    following_relationships = opts[:following_relationships]
 
-    follow_state = User.get_follow_state(reading_user, target)
+    follow_state =
+      if following_relationships do
+        user_to_target_following_relation =
+          find_following_rel(following_relationships, reading_user, target)
+
+        User.get_follow_state(reading_user, target, user_to_target_following_relation)
+      else
+        User.get_follow_state(reading_user, target)
+      end
+
+    followed_by =
+      if following_relationships do
+        with %{state: "accept"} <-
+               find_following_rel(following_relationships, target, reading_user) do
+          true
+        else
+          _ -> false
+        end
+      else
+        User.following?(target, reading_user)
+      end
 
     # TODO: add a note on adjusting StatusView.user_relationships_opt/1 re: preloading of user relations
     %{
       id: to_string(target.id),
       following: follow_state == "accept",
-      followed_by: User.following?(target, reading_user),
+      followed_by: followed_by,
       blocking:
-        test_rel(user_relationships, :block, reading_user, target, &User.blocks_user?(&1, &2)),
+        UserRelationship.exists?(
+          user_relationships,
+          :block,
+          reading_user,
+          target,
+          &User.blocks_user?(&1, &2)
+        ),
       blocked_by:
-        test_rel(user_relationships, :block, target, reading_user, &User.blocks_user?(&1, &2)),
-      muting: test_rel(user_relationships, :mute, reading_user, target, &User.mutes?(&1, &2)),
+        UserRelationship.exists?(
+          user_relationships,
+          :block,
+          target,
+          reading_user,
+          &User.blocks_user?(&1, &2)
+        ),
+      muting:
+        UserRelationship.exists?(
+          user_relationships,
+          :mute,
+          reading_user,
+          target,
+          &User.mutes?(&1, &2)
+        ),
       muting_notifications:
-        test_rel(
+        UserRelationship.exists?(
           user_relationships,
           :notification_mute,
           reading_user,
@@ -75,7 +109,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
           &User.muted_notifications?(&1, &2)
         ),
       subscribing:
-        test_rel(
+        UserRelationship.exists?(
           user_relationships,
           :inverse_subscription,
           target,
@@ -85,7 +119,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
       requested: follow_state == "pending",
       domain_blocking: User.blocks_domain?(reading_user, target),
       showing_reblogs:
-        not test_rel(
+        not UserRelationship.exists?(
           user_relationships,
           :reblog_mute,
           reading_user,
@@ -139,7 +173,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
       render("relationship.json", %{
         user: opts[:for],
         target: user,
-        user_relationships: opts[:user_relationships]
+        user_relationships: opts[:user_relationships],
+        following_relationships: opts[:following_relationships]
       })
 
     %{
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index e0c368ec9..55a5513f9 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
 
   alias Pleroma.Activity
   alias Pleroma.ActivityExpiration
+  alias Pleroma.FollowingRelationship
   alias Pleroma.HTML
   alias Pleroma.Object
   alias Pleroma.Repo
@@ -71,22 +72,31 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
     present?(user && user.ap_id in (object.data["announcements"] || []))
   end
 
-  defp user_relationships_opt(opts) do
+  defp relationships_opts(opts) do
     reading_user = opts[:for]
 
-    if reading_user do
-      activities = opts[:activities]
-      actors = Enum.map(activities, fn a -> get_user(a.data["actor"]) end)
+    {user_relationships, following_relationships} =
+      if reading_user do
+        activities = opts[:activities]
+        actors = Enum.map(activities, fn a -> get_user(a.data["actor"]) end)
 
-      UserRelationship.dictionary(
-        [reading_user],
-        actors,
-        [:block, :mute, :notification_mute, :reblog_mute],
-        [:block, :inverse_subscription]
-      )
-    else
-      []
-    end
+        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, following_relationships}
+      else
+        {[], []}
+      end
+
+    %{user_relationships: user_relationships, following_relationships: following_relationships}
   end
 
   def render("index.json", opts) do
@@ -96,7 +106,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
     opts =
       opts
       |> Map.put(:replied_to_activities, replied_to_activities)
-      |> Map.put(:user_relationships, user_relationships_opt(opts))
+      |> Map.merge(relationships_opts(opts))
 
     safe_render_many(activities, StatusView, "show.json", opts)
   end
@@ -135,7 +145,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
         AccountView.render("show.json", %{
           user: user,
           for: opts[:for],
-          user_relationships: opts[:user_relationships]
+          user_relationships: opts[:user_relationships],
+          following_relationships: opts[:following_relationships]
         }),
       in_reply_to_id: nil,
       in_reply_to_account_id: nil,
@@ -286,7 +297,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
 
     muted =
       thread_muted? ||
-        Pleroma.Web.MastodonAPI.AccountView.test_rel(
+        UserRelationship.exists?(
           user_relationships_opt,
           :mute,
           opts[:for],
@@ -302,7 +313,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
         AccountView.render("show.json", %{
           user: user,
           for: opts[:for],
-          user_relationships: user_relationships_opt
+          user_relationships: user_relationships_opt,
+          following_relationships: opts[:following_relationships]
         }),
       in_reply_to_id: reply_to && to_string(reply_to.id),
       in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),

From eec1fcaf55bdcbc2d3aed4eaf044bb8ef6c4effa Mon Sep 17 00:00:00 2001
From: lain <lain@soykaf.club>
Date: Mon, 23 Mar 2020 15:58:55 +0100
Subject: [PATCH 3/8] Home timeline tests: Add failing test for relationships

---
 .../controllers/timeline_controller_test.exs  | 57 +++++++++++++++++--
 1 file changed, 53 insertions(+), 4 deletions(-)

diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs
index 6fedb4223..47849fc48 100644
--- a/test/web/mastodon_api/controllers/timeline_controller_test.exs
+++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs
@@ -21,9 +21,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
     setup do: oauth_access(["read:statuses"])
 
     test "the home timeline", %{user: user, conn: conn} do
-      following = insert(:user)
+      following = insert(:user, nickname: "followed")
+      third_user = insert(:user, nickname: "repeated")
 
-      {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
+      {:ok, _activity} = CommonAPI.post(following, %{"status" => "post"})
+      {:ok, activity} = CommonAPI.post(third_user, %{"status" => "repeated post"})
+      {:ok, _, _} = CommonAPI.repeat(activity.id, following)
 
       ret_conn = get(conn, "/api/v1/timelines/home")
 
@@ -31,9 +34,55 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
 
       {:ok, _user} = User.follow(user, following)
 
-      conn = get(conn, "/api/v1/timelines/home")
+      ret_conn = get(conn, "/api/v1/timelines/home")
 
-      assert [%{"content" => "test"}] = json_response(conn, :ok)
+      assert [
+               %{
+                 "reblog" => %{
+                   "content" => "repeated post",
+                   "account" => %{
+                     "pleroma" => %{
+                       "relationship" => %{"following" => false, "followed_by" => false}
+                     }
+                   }
+                 },
+                 "account" => %{"pleroma" => %{"relationship" => %{"following" => true}}}
+               },
+               %{
+                 "content" => "post",
+                 "account" => %{
+                   "acct" => "followed",
+                   "pleroma" => %{"relationship" => %{"following" => true}}
+                 }
+               }
+             ] = json_response(ret_conn, :ok)
+
+      {:ok, _user} = User.follow(third_user, user)
+
+      ret_conn = get(conn, "/api/v1/timelines/home")
+
+      assert [
+               %{
+                 "reblog" => %{
+                   "content" => "repeated post",
+                   "account" => %{
+                     "acct" => "repeated",
+                     "pleroma" => %{
+                       # This part does not match correctly
+                       "relationship" => %{"following" => false, "followed_by" => true}
+                     }
+                   }
+                 },
+                 "account" => %{"pleroma" => %{"relationship" => %{"following" => true}}}
+               },
+               %{
+                 "content" => "post",
+                 "account" => %{
+                   "acct" => "followed",
+                   "pleroma" => %{"relationship" => %{"following" => true}}
+                 }
+               }
+             ] = json_response(ret_conn, :ok)
     end
 
     test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do

From 13cbb9f6ada8dcb15bb7ed12be4d88a18c5db7f7 Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov <ivantashkinov@gmail.com>
Date: Tue, 24 Mar 2020 22:14:26 +0300
Subject: [PATCH 4/8] Implemented preloading of relationships with parent
 activities' actors for statuses/timeline rendering. Applied preloading for
 notifications rendering. Fixed announces rendering issue
 (preloading-related).

---
 lib/pleroma/activity/queries.ex               |  7 ++
 .../web/mastodon_api/views/account_view.ex    | 15 ++-
 .../mastodon_api/views/notification_view.ex   | 98 +++++++++++++++----
 .../web/mastodon_api/views/status_view.ex     | 85 +++++++++-------
 .../controllers/timeline_controller_test.exs  |  1 -
 5 files changed, 138 insertions(+), 68 deletions(-)

diff --git a/lib/pleroma/activity/queries.ex b/lib/pleroma/activity/queries.ex
index 04593b9fb..a34c20343 100644
--- a/lib/pleroma/activity/queries.ex
+++ b/lib/pleroma/activity/queries.ex
@@ -35,6 +35,13 @@ defmodule Pleroma.Activity.Queries do
     from(a in query, where: a.actor == ^ap_id)
   end
 
+  def find_by_object_ap_id(activities, object_ap_id) do
+    Enum.find(
+      activities,
+      &(object_ap_id in [is_map(&1.data["object"]) && &1.data["object"]["id"], &1.data["object"]])
+    )
+  end
+
   @spec by_object_id(query, String.t() | [String.t()]) :: query
   def by_object_id(query \\ Activity, object_id)
 
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 2fe46158b..89bea9957 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -46,8 +46,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
         "relationship.json",
         %{user: %User{} = reading_user, target: %User{} = target} = opts
       ) do
-    user_relationships = Map.get(opts, :user_relationships)
-    following_relationships = opts[:following_relationships]
+    user_relationships = get_in(opts, [:relationships, :user_relationships])
+    following_relationships = get_in(opts, [:relationships, :following_relationships])
 
     follow_state =
       if following_relationships do
@@ -61,17 +61,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
 
     followed_by =
       if following_relationships do
-        with %{state: "accept"} <-
-               find_following_rel(following_relationships, target, reading_user) do
-          true
-        else
+        case find_following_rel(following_relationships, target, reading_user) do
+          %{state: "accept"} -> true
           _ -> false
         end
       else
         User.following?(target, reading_user)
       end
 
-    # TODO: add a note on adjusting StatusView.user_relationships_opt/1 re: preloading of user relations
+    # NOTE: adjust StatusView.relationships_opts/2 if adding new relation-related flags
     %{
       id: to_string(target.id),
       following: follow_state == "accept",
@@ -173,8 +171,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
       render("relationship.json", %{
         user: opts[:for],
         target: user,
-        user_relationships: opts[:user_relationships],
-        following_relationships: opts[:following_relationships]
+        relationships: opts[:relationships]
       })
 
     %{
diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex
index 33145c484..e9c618496 100644
--- a/lib/pleroma/web/mastodon_api/views/notification_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex
@@ -13,19 +13,68 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
   alias Pleroma.Web.MastodonAPI.NotificationView
   alias Pleroma.Web.MastodonAPI.StatusView
 
-  def render("index.json", %{notifications: notifications, for: user}) do
-    safe_render_many(notifications, NotificationView, "show.json", %{for: user})
+  def render("index.json", %{notifications: notifications, for: reading_user}) do
+    activities = Enum.map(notifications, & &1.activity)
+
+    parent_activities =
+      activities
+      |> Enum.filter(
+        &(Activity.mastodon_notification_type(&1) in [
+            "favourite",
+            "reblog",
+            "pleroma:emoji_reaction"
+          ])
+      )
+      |> Enum.map(& &1.data["object"])
+      |> Activity.create_by_object_ap_id()
+      |> 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)
+
+    opts = %{
+      for: reading_user,
+      parent_activities: parent_activities,
+      relationships: StatusView.relationships_opts(reading_user, actors)
+    }
+
+    safe_render_many(notifications, NotificationView, "show.json", opts)
   end
 
-  def render("show.json", %{
-        notification: %Notification{activity: activity} = notification,
-        for: user
-      }) do
+  def render(
+        "show.json",
+        %{
+          notification: %Notification{activity: activity} = notification,
+          for: reading_user
+        } = opts
+      ) do
     actor = User.get_cached_by_ap_id(activity.data["actor"])
-    parent_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
+
+    parent_activity_fn = fn ->
+      if opts[:parent_activities] do
+        Activity.Queries.find_by_object_ap_id(opts[:parent_activities], activity.data["object"])
+      else
+        Activity.get_create_by_object_ap_id(activity.data["object"])
+      end
+    end
+
     mastodon_type = Activity.mastodon_notification_type(activity)
 
-    with %{id: _} = account <- AccountView.render("show.json", %{user: actor, for: user}) do
+    with %{id: _} = account <-
+           AccountView.render("show.json", %{
+             user: actor,
+             for: reading_user,
+             relationships: opts[:relationships]
+           }) do
       response = %{
         id: to_string(notification.id),
         type: mastodon_type,
@@ -36,24 +85,28 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
         }
       }
 
+      relationships_opts = %{relationships: opts[:relationships]}
+
       case mastodon_type do
         "mention" ->
-          put_status(response, activity, user)
+          put_status(response, activity, reading_user, relationships_opts)
 
         "favourite" ->
-          put_status(response, parent_activity, user)
+          put_status(response, parent_activity_fn.(), reading_user, relationships_opts)
 
         "reblog" ->
-          put_status(response, parent_activity, user)
+          put_status(response, parent_activity_fn.(), reading_user, relationships_opts)
 
         "move" ->
-          put_target(response, activity, user)
+          put_target(response, activity, reading_user, relationships_opts)
 
         "follow" ->
           response
 
         "pleroma:emoji_reaction" ->
-          put_status(response, parent_activity, user) |> put_emoji(activity)
+          response
+          |> put_status(parent_activity_fn.(), reading_user, relationships_opts)
+          |> put_emoji(activity)
 
         _ ->
           nil
@@ -64,16 +117,21 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
   end
 
   defp put_emoji(response, activity) do
-    response
-    |> Map.put(:emoji, activity.data["content"])
+    Map.put(response, :emoji, activity.data["content"])
   end
 
-  defp put_status(response, activity, user) do
-    Map.put(response, :status, StatusView.render("show.json", %{activity: activity, for: user}))
+  defp put_status(response, activity, reading_user, opts) do
+    status_render_opts = Map.merge(opts, %{activity: activity, for: reading_user})
+    status_render = StatusView.render("show.json", status_render_opts)
+
+    Map.put(response, :status, status_render)
   end
 
-  defp put_target(response, activity, user) do
-    target = User.get_cached_by_ap_id(activity.data["target"])
-    Map.put(response, :target, AccountView.render("show.json", %{user: target, for: user}))
+  defp put_target(response, activity, reading_user, opts) do
+    target_user = User.get_cached_by_ap_id(activity.data["target"])
+    target_render_opts = Map.merge(opts, %{user: target_user, for: reading_user})
+    target_render = AccountView.render("show.json", target_render_opts)
+
+    Map.put(response, :target, target_render)
   end
 end
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index 55a5513f9..0ef65b352 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -72,41 +72,46 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
     present?(user && user.ap_id in (object.data["announcements"] || []))
   end
 
-  defp relationships_opts(opts) do
-    reading_user = opts[:for]
+  def relationships_opts(_reading_user = nil, _actors) do
+    %{user_relationships: [], following_relationships: []}
+  end
 
-    {user_relationships, following_relationships} =
-      if reading_user do
-        activities = opts[:activities]
-        actors = Enum.map(activities, fn a -> get_user(a.data["actor"]) end)
+  def relationships_opts(reading_user, actors) do
+    user_relationships =
+      UserRelationship.dictionary(
+        [reading_user],
+        actors,
+        [:block, :mute, :notification_mute, :reblog_mute],
+        [:block, :inverse_subscription]
+      )
 
-        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, following_relationships}
-      else
-        {[], []}
-      end
+    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
-    activities = opts.activities
+    # To do: check AdminAPIControllerTest on the reasons behind nil activities in the list
+    activities = Enum.filter(opts.activities, & &1)
     replied_to_activities = get_replied_to_activities(activities)
 
+    parent_activities =
+      activities
+      |> Enum.filter(&(&1.data["type"] == "Announce" && &1.data["object"]))
+      |> Enum.map(&Object.normalize(&1).data["id"])
+      |> Activity.create_by_object_ap_id()
+      |> Activity.with_preloaded_object(:left)
+      |> Activity.with_preloaded_bookmark(opts[:for])
+      |> Activity.with_set_thread_muted_field(opts[:for])
+      |> Repo.all()
+
+    actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"]))
+
     opts =
       opts
       |> Map.put(:replied_to_activities, replied_to_activities)
-      |> Map.merge(relationships_opts(opts))
+      |> Map.put(:parent_activities, parent_activities)
+      |> Map.put(:relationships, relationships_opts(opts[:for], actors))
 
     safe_render_many(activities, StatusView, "show.json", opts)
   end
@@ -119,17 +124,25 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
     created_at = Utils.to_masto_date(activity.data["published"])
     activity_object = Object.normalize(activity)
 
-    reblogged_activity =
-      Activity.create_by_object_ap_id(activity_object.data["id"])
-      |> Activity.with_preloaded_bookmark(opts[:for])
-      |> Activity.with_set_thread_muted_field(opts[:for])
-      |> Repo.one()
+    reblogged_parent_activity =
+      if opts[:parent_activities] do
+        Activity.Queries.find_by_object_ap_id(
+          opts[:parent_activities],
+          activity_object.data["id"]
+        )
+      else
+        Activity.create_by_object_ap_id(activity_object.data["id"])
+        |> Activity.with_preloaded_bookmark(opts[:for])
+        |> Activity.with_set_thread_muted_field(opts[:for])
+        |> Repo.one()
+      end
 
-    reblogged = render("show.json", Map.put(opts, :activity, reblogged_activity))
+    reblog_rendering_opts = Map.put(opts, :activity, reblogged_parent_activity)
+    reblogged = render("show.json", reblog_rendering_opts)
 
     favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || [])
 
-    bookmarked = Activity.get_bookmark(reblogged_activity, opts[:for]) != nil
+    bookmarked = Activity.get_bookmark(reblogged_parent_activity, opts[:for]) != nil
 
     mentions =
       activity.recipients
@@ -145,8 +158,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
         AccountView.render("show.json", %{
           user: user,
           for: opts[:for],
-          user_relationships: opts[:user_relationships],
-          following_relationships: opts[:following_relationships]
+          relationships: opts[:relationships]
         }),
       in_reply_to_id: nil,
       in_reply_to_account_id: nil,
@@ -156,7 +168,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
       reblogs_count: 0,
       replies_count: 0,
       favourites_count: 0,
-      reblogged: reblogged?(reblogged_activity, opts[:for]),
+      reblogged: reblogged?(reblogged_parent_activity, opts[:for]),
       favourited: present?(favorited),
       bookmarked: present?(bookmarked),
       muted: false,
@@ -293,12 +305,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
         _ -> []
       end
 
-    user_relationships_opt = opts[:user_relationships]
-
     muted =
       thread_muted? ||
         UserRelationship.exists?(
-          user_relationships_opt,
+          get_in(opts, [:relationships, :user_relationships]),
           :mute,
           opts[:for],
           user,
@@ -313,8 +323,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
         AccountView.render("show.json", %{
           user: user,
           for: opts[:for],
-          user_relationships: user_relationships_opt,
-          following_relationships: opts[:following_relationships]
+          relationships: opts[:relationships]
         }),
       in_reply_to_id: reply_to && to_string(reply_to.id),
       in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs
index 47849fc48..97b1c3e66 100644
--- a/test/web/mastodon_api/controllers/timeline_controller_test.exs
+++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs
@@ -68,7 +68,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
                    "account" => %{
                      "acct" => "repeated",
                      "pleroma" => %{
-                       # This part does not match correctly
                        "relationship" => %{"following" => false, "followed_by" => true}
                      }
                    }

From be5e2c4dbba63831ea6a0617556e686969b5080f Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov <ivantashkinov@gmail.com>
Date: Wed, 25 Mar 2020 17:01:45 +0300
Subject: [PATCH 5/8] Applied relationships preloading to GET
 /api/v1/accounts/relationships. Refactoring (User.binary_id/1).

---
 lib/pleroma/conversation/participation.ex         | 11 ++++-------
 lib/pleroma/following_relationship.ex             |  8 ++------
 lib/pleroma/thread_mute.ex                        |  4 ++--
 lib/pleroma/user.ex                               | 15 +++++++++++++++
 lib/pleroma/user_relationship.ex                  |  9 ++-------
 .../web/mastodon_api/views/account_view.ex        |  6 +++++-
 6 files changed, 30 insertions(+), 23 deletions(-)

diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex
index 693825cf5..215265fc9 100644
--- a/lib/pleroma/conversation/participation.ex
+++ b/lib/pleroma/conversation/participation.ex
@@ -129,21 +129,18 @@ defmodule Pleroma.Conversation.Participation do
   end
 
   def restrict_recipients(query, user, %{"recipients" => user_ids}) do
-    user_ids =
+    user_binary_ids =
       [user.id | user_ids]
       |> Enum.uniq()
-      |> Enum.reduce([], fn user_id, acc ->
-        {:ok, user_id} = FlakeId.Ecto.CompatType.dump(user_id)
-        [user_id | acc]
-      end)
+      |> User.binary_id()
 
     conversation_subquery =
       __MODULE__
       |> group_by([p], p.conversation_id)
       |> having(
         [p],
-        count(p.user_id) == ^length(user_ids) and
-          fragment("array_agg(?) @> ?", p.user_id, ^user_ids)
+        count(p.user_id) == ^length(user_binary_ids) and
+          fragment("array_agg(?) @> ?", p.user_id, ^user_binary_ids)
       )
       |> select([p], %{id: p.conversation_id})
 
diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex
index dd1696136..624bddfe4 100644
--- a/lib/pleroma/following_relationship.ex
+++ b/lib/pleroma/following_relationship.ex
@@ -135,12 +135,8 @@ defmodule Pleroma.FollowingRelationship do
         target_users
       )
       when is_list(source_users) and is_list(target_users) do
-    get_bin_ids = fn user ->
-      with {:ok, bin_id} <- CompatType.dump(user.id), do: bin_id
-    end
-
-    source_user_ids = Enum.map(source_users, &get_bin_ids.(&1))
-    target_user_ids = Enum.map(target_users, &get_bin_ids.(&1))
+    source_user_ids = User.binary_id(source_users)
+    target_user_ids = User.binary_id(target_users)
 
     __MODULE__
     |> where(
diff --git a/lib/pleroma/thread_mute.ex b/lib/pleroma/thread_mute.ex
index cc815430a..f657758aa 100644
--- a/lib/pleroma/thread_mute.ex
+++ b/lib/pleroma/thread_mute.ex
@@ -24,10 +24,10 @@ defmodule Pleroma.ThreadMute do
   end
 
   def query(user_id, context) do
-    {:ok, user_id} = FlakeId.Ecto.CompatType.dump(user_id)
+    user_binary_id = User.binary_id(user_id)
 
     ThreadMute
-    |> Ecto.Query.where(user_id: ^user_id)
+    |> Ecto.Query.where(user_id: ^user_binary_id)
     |> Ecto.Query.where(context: ^context)
   end
 
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index f74e43cce..699256a3b 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -218,6 +218,21 @@ defmodule Pleroma.User do
     end
   end
 
+  @doc "Dumps id to SQL-compatible format"
+  def binary_id(source_id) when is_binary(source_id) do
+    with {:ok, dumped_id} <- FlakeId.Ecto.CompatType.dump(source_id) do
+      dumped_id
+    else
+      _ -> source_id
+    end
+  end
+
+  def binary_id(source_ids) when is_list(source_ids) do
+    Enum.map(source_ids, &binary_id/1)
+  end
+
+  def binary_id(%User{} = user), do: binary_id(user.id)
+
   @doc "Returns status account"
   @spec account_status(User.t()) :: account_status()
   def account_status(%User{deactivated: true}), do: :deactivated
diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex
index 9423e3a42..519d2998d 100644
--- a/lib/pleroma/user_relationship.ex
+++ b/lib/pleroma/user_relationship.ex
@@ -8,7 +8,6 @@ defmodule Pleroma.UserRelationship do
   import Ecto.Changeset
   import Ecto.Query
 
-  alias FlakeId.Ecto.CompatType
   alias Pleroma.Repo
   alias Pleroma.User
   alias Pleroma.UserRelationship
@@ -84,12 +83,8 @@ defmodule Pleroma.UserRelationship do
         target_to_source_rel_types \\ nil
       )
       when is_list(source_users) and is_list(target_users) do
-    get_bin_ids = fn user ->
-      with {:ok, bin_id} <- CompatType.dump(user.id), do: bin_id
-    end
-
-    source_user_ids = Enum.map(source_users, &get_bin_ids.(&1))
-    target_user_ids = Enum.map(target_users, &get_bin_ids.(&1))
+    source_user_ids = User.binary_id(source_users)
+    target_user_ids = User.binary_id(target_users)
 
     get_rel_type_codes = fn rel_type -> user_relationship_mappings()[rel_type] end
 
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 702d9e658..6b2eca1f3 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
   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
@@ -129,7 +130,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
   end
 
   def render("relationships.json", %{user: user, targets: targets}) do
-    render_many(targets, AccountView, "relationship.json", user: user, as: :target)
+    relationships_opts = StatusView.relationships_opts(user, targets)
+    opts = %{as: :target, user: user, relationships: relationships_opts}
+
+    render_many(targets, AccountView, "relationship.json", opts)
   end
 
   defp do_render("show.json", %{user: user} = opts) do

From 460e41585c2cd3f137c0f80173da60167fb318bf Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov <ivantashkinov@gmail.com>
Date: Wed, 25 Mar 2020 20:33:34 +0300
Subject: [PATCH 6/8] Further preloading (more endpoints), refactoring, tests.

---
 lib/pleroma/following_relationship.ex         |   6 +
 lib/pleroma/user.ex                           |   5 +-
 lib/pleroma/user_relationship.ex              |  20 ++++
 .../web/mastodon_api/views/account_view.ex    |  36 +++---
 .../mastodon_api/views/notification_view.ex   |  42 ++++---
 .../web/mastodon_api/views/status_view.ex     |  29 ++---
 .../mastodon_api/views/account_view_test.exs  | 109 ++++++++++--------
 .../views/notification_view_test.exs          |  42 +++----
 .../mastodon_api/views/status_view_test.exs   |  15 ++-
 9 files changed, 179 insertions(+), 125 deletions(-)

diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex
index 624bddfe4..a9538ea4e 100644
--- a/lib/pleroma/following_relationship.ex
+++ b/lib/pleroma/following_relationship.ex
@@ -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
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 699256a3b..8ccb9242d 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -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
diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex
index 519d2998d..011cf6822 100644
--- a/lib/pleroma/user_relationship.ex
+++ b/lib/pleroma/user_relationship.ex
@@ -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 ->
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 6b2eca1f3..2cdfac7af 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -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
diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex
index e9c618496..db434271c 100644
--- a/lib/pleroma/web/mastodon_api/views/notification_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex
@@ -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"]))
+    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)
+        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)
 
         _ ->
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index 0ef65b352..7b1cb7bf8 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -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
diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs
index 983886c6b..ede62903f 100644
--- a/test/web/mastodon_api/views/account_view_test.exs
+++ b/test/web/mastodon_api/views/account_view_test.exs
@@ -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
-      }
+      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)
+          }
+        )
 
-      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 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
 
diff --git a/test/web/mastodon_api/views/notification_view_test.exs b/test/web/mastodon_api/views/notification_view_test.exs
index d04c3022f..7965af00a 100644
--- a/test/web/mastodon_api/views/notification_view_test.exs
+++ b/test/web/mastodon_api/views/notification_view_test.exs
@@ -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
diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs
index 191895c6f..9191730cd 100644
--- a/test/web/mastodon_api/views/status_view_test.exs
+++ b/test/web/mastodon_api/views/status_view_test.exs
@@ -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
 

From 6b793d3f8336fcba5cac596f9e76d0274633f98d Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov <ivantashkinov@gmail.com>
Date: Thu, 26 Mar 2020 21:54:01 +0300
Subject: [PATCH 7/8] Ensured no auxiliary computations (actors list
 preparation etc.) related to relationships preloading if no user is present
 (for statuses / accounts / relationships rendering).

---
 .../web/mastodon_api/views/account_view.ex    | 26 +++++++++++-----
 .../mastodon_api/views/notification_view.ex   | 31 +++++++++++--------
 .../web/mastodon_api/views/status_view.ex     | 16 +++++++---
 3 files changed, 47 insertions(+), 26 deletions(-)

diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 2cdfac7af..0efcabc01 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -14,10 +14,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
 
   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)
+      cond do
+        Map.has_key?(opts, :relationships) ->
+          opts[:relationships]
+
+        is_nil(opts[:for]) ->
+          UserRelationship.view_relationships_option(nil, [])
+
+        true ->
+          UserRelationship.view_relationships_option(opts[:for], users)
       end
 
     opts = Map.put(opts, :relationships, relationships_opt)
@@ -134,10 +139,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
 
   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)
+      cond do
+        Map.has_key?(opts, :relationships) ->
+          opts[:relationships]
+
+        is_nil(opts[:for]) ->
+          UserRelationship.view_relationships_option(nil, [])
+
+        true ->
+          UserRelationship.view_relationships_option(user, targets)
       end
 
     render_opts = %{as: :target, user: user, relationships: relationships_opt}
diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex
index db434271c..a809080fd 100644
--- a/lib/pleroma/web/mastodon_api/views/notification_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex
@@ -32,21 +32,26 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
       |> Pleroma.Repo.all()
 
     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"]))
+      cond do
+        Map.has_key?(opts, :relationships) ->
+          opts[:relationships]
 
-        actors =
-          activities
-          |> Enum.map(fn a -> User.get_cached_by_ap_id(a.data["actor"]) end)
-          |> Enum.filter(& &1)
-          |> Kernel.++(move_activities_targets)
+        is_nil(opts[:for]) ->
+          UserRelationship.view_relationships_option(nil, [])
 
-        UserRelationship.view_relationships_option(reading_user, actors)
+        true ->
+          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 = %{
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index 7b1cb7bf8..d36b9ee5c 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -87,11 +87,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
       |> Repo.all()
 
     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)
+      cond do
+        Map.has_key?(opts, :relationships) ->
+          opts[:relationships]
+
+        is_nil(opts[:for]) ->
+          UserRelationship.view_relationships_option(nil, [])
+
+        true ->
+          actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"]))
+
+          UserRelationship.view_relationships_option(opts[:for], actors)
       end
 
     opts =

From dfbc05d4965a04a82d4c4c5b8842f4117757f30e Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov <ivantashkinov@gmail.com>
Date: Fri, 27 Mar 2020 08:01:03 +0300
Subject: [PATCH 8/8] Misc refactoring / tweaks (`ThreadMute.exists?/2`).

---
 lib/pleroma/thread_mute.ex                           |  4 ++--
 lib/pleroma/web/common_api/common_api.ex             |  2 +-
 .../web/mastodon_api/views/notification_view.ex      | 12 ++++++------
 lib/pleroma/web/mastodon_api/views/status_view.ex    |  7 ++++---
 test/web/mastodon_api/views/account_view_test.exs    |  2 +-
 5 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/lib/pleroma/thread_mute.ex b/lib/pleroma/thread_mute.ex
index 5768e7711..be01d541d 100644
--- a/lib/pleroma/thread_mute.ex
+++ b/lib/pleroma/thread_mute.ex
@@ -68,8 +68,8 @@ defmodule Pleroma.ThreadMute do
     |> Repo.delete_all()
   end
 
-  def check_muted(user_id, context) do
+  def exists?(user_id, context) do
     query(user_id, context)
-    |> Repo.all()
+    |> Repo.exists?()
   end
 end
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 091011c6b..2646b9f7b 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -358,7 +358,7 @@ defmodule Pleroma.Web.CommonAPI do
   def thread_muted?(%{id: nil} = _user, _activity), do: false
 
   def thread_muted?(user, activity) do
-    ThreadMute.check_muted(user.id, activity.data["context"]) != []
+    ThreadMute.exists?(user.id, activity.data["context"])
   end
 
   def report(user, %{"account_id" => account_id} = data) do
diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex
index a809080fd..89f5734ff 100644
--- a/lib/pleroma/web/mastodon_api/views/notification_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex
@@ -98,27 +98,27 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
         }
       }
 
-      relationships_opt = %{relationships: opts[:relationships]}
+      render_opts = %{relationships: opts[:relationships]}
 
       case mastodon_type do
         "mention" ->
-          put_status(response, activity, reading_user, relationships_opt)
+          put_status(response, activity, reading_user, render_opts)
 
         "favourite" ->
-          put_status(response, parent_activity_fn.(), reading_user, relationships_opt)
+          put_status(response, parent_activity_fn.(), reading_user, render_opts)
 
         "reblog" ->
-          put_status(response, parent_activity_fn.(), reading_user, relationships_opt)
+          put_status(response, parent_activity_fn.(), reading_user, render_opts)
 
         "move" ->
-          put_target(response, activity, reading_user, relationships_opt)
+          put_target(response, activity, reading_user, render_opts)
 
         "follow" ->
           response
 
         "pleroma:emoji_reaction" ->
           response
-          |> put_status(parent_activity_fn.(), reading_user, relationships_opt)
+          |> put_status(parent_activity_fn.(), reading_user, render_opts)
           |> put_emoji(activity)
 
         _ ->
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index d36b9ee5c..440eef4ba 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -228,9 +228,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
       end
 
     thread_muted? =
-      case activity.thread_muted? do
-        thread_muted? when is_boolean(thread_muted?) -> thread_muted?
-        nil -> (opts[:for] && CommonAPI.thread_muted?(opts[:for], activity)) || false
+      cond do
+        is_nil(opts[:for]) -> false
+        is_boolean(activity.thread_muted?) -> activity.thread_muted?
+        true -> CommonAPI.thread_muted?(opts[:for], activity)
       end
 
     attachment_data = object.data["attachment"] || []
diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs
index ede62903f..0d1c3ecb3 100644
--- a/test/web/mastodon_api/views/account_view_test.exs
+++ b/test/web/mastodon_api/views/account_view_test.exs
@@ -186,7 +186,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
 
   describe "relationship" do
     defp test_relationship_rendering(user, other_user, expected_result) do
-      opts = %{user: user, target: other_user}
+      opts = %{user: user, target: other_user, relationships: nil}
       assert expected_result == AccountView.render("relationship.json", opts)
 
       relationships_opt = UserRelationship.view_relationships_option(user, [other_user])