Mastodon API: Add `exclude_visibilities` parameter to the timeline and notification...
[akkoma] / lib / pleroma / notification.ex
index d94ae5971c5a7e8159ed3361d8bdcd402f43ea14..d145f8d5b5a42c8ba3b685196754e45a1eaf682e 100644 (file)
@@ -17,6 +17,7 @@ defmodule Pleroma.Notification do
 
   import Ecto.Query
   import Ecto.Changeset
+  require Logger
 
   @type t :: %__MODULE__{}
 
@@ -34,43 +35,92 @@ defmodule Pleroma.Notification do
   end
 
   def for_user_query(user, opts \\ []) do
-    query =
-      Notification
-      |> where(user_id: ^user.id)
-      |> where(
-        [n, a],
+    Notification
+    |> where(user_id: ^user.id)
+    |> where(
+      [n, a],
+      fragment(
+        "? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
+        a.actor
+      )
+    )
+    |> join(:inner, [n], activity in assoc(n, :activity))
+    |> join(:left, [n, a], object in Object,
+      on:
         fragment(
-          "? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
-          a.actor
+          "(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
+          object.data,
+          a.data
         )
-      )
-      |> join(:inner, [n], activity in assoc(n, :activity))
-      |> join(:left, [n, a], object in Object,
-        on:
-          fragment(
-            "(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
-            object.data,
-            a.data
-          )
-      )
-      |> preload([n, a, o], activity: {a, object: o})
+    )
+    |> preload([n, a, o], activity: {a, object: o})
+    |> exclude_muted(user, opts)
+    |> exclude_visibility(opts)
+  end
+
+  defp exclude_muted(query, _, %{with_muted: true}) do
+    query
+  end
+
+  defp exclude_muted(query, user, _opts) do
+    query
+    |> where([n, a], a.actor not in ^user.info.muted_notifications)
+    |> where([n, a], a.actor not in ^user.info.blocks)
+    |> where(
+      [n, a],
+      fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks
+    )
+    |> join(:left, [n, a], tm in Pleroma.ThreadMute,
+      on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
+    )
+    |> where([n, a, o, tm], is_nil(tm.user_id))
+  end
 
-    if opts[:with_muted] do
+  @valid_visibilities ~w[direct unlisted public private]
+
+  defp exclude_visibility(query, %{exclude_visibilities: visibility})
+       when is_list(visibility) do
+    if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
       query
-    else
-      where(query, [n, a], a.actor not in ^user.info.muted_notifications)
-      |> where([n, a], a.actor not in ^user.info.blocks)
       |> where(
         [n, a],
-        fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks
-      )
-      |> join(:left, [n, a], tm in Pleroma.ThreadMute,
-        on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
+        not fragment(
+          "activity_visibility(?, ?, ?) = ANY (?)",
+          a.actor,
+          a.recipients,
+          a.data,
+          ^visibility
+        )
       )
-      |> where([n, a, o, tm], is_nil(tm.user_id))
+    else
+      Logger.error("Could not exclude visibility to #{visibility}")
+      query
     end
   end
 
+  defp exclude_visibility(query, %{exclude_visibilities: visibility})
+       when visibility in @valid_visibilities do
+    query
+    |> where(
+      [n, a],
+      not fragment(
+        "activity_visibility(?, ?, ?) = (?)",
+        a.actor,
+        a.recipients,
+        a.data,
+        ^visibility
+      )
+    )
+  end
+
+  defp exclude_visibility(query, %{exclude_visibilities: visibility})
+       when visibility not in @valid_visibilities do
+    Logger.error("Could not exclude visibility to #{visibility}")
+    query
+  end
+
+  defp exclude_visibility(query, _visibility), do: query
+
   def for_user(user, opts \\ %{}) do
     user
     |> for_user_query(opts)