X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fnotification.ex;h=b7ecf51e4681fc9a25b8d1cd9d596c928f930bba;hb=17c237ba808d4356bb1e202e459680563b79ef99;hp=118560d3491d6234c59bc455ad1cd60317033bee;hpb=657277ffc0d3d25be4376ed629057a2d2cefb2e1;p=akkoma diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 118560d34..b7ecf51e4 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -11,20 +11,20 @@ defmodule Pleroma.Notification do alias Pleroma.Pagination alias Pleroma.Repo alias Pleroma.User - alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.Push alias Pleroma.Web.Streamer import Ecto.Query import Ecto.Changeset + require Logger @type t :: %__MODULE__{} schema "notifications" do field(:seen, :boolean, default: false) - belongs_to(:user, User, type: Pleroma.FlakeId) - belongs_to(:activity, Activity, type: Pleroma.FlakeId) + belongs_to(:user, User, type: FlakeId.Ecto.CompatType) + belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType) timestamps() end @@ -34,13 +34,13 @@ defmodule Pleroma.Notification do |> cast(attrs, [:seen]) end - def for_user_query(user) do + def for_user_query(user, opts \\ []) do Notification |> where(user_id: ^user.id) |> where( [n, a], fragment( - "? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')", + "? not in (SELECT ap_id FROM users WHERE deactivated = 'true')", a.actor ) ) @@ -54,11 +54,81 @@ defmodule Pleroma.Notification do ) ) |> preload([n, a, o], activity: {a, object: o}) + |> exclude_muted(user, opts) + |> exclude_blocked(user) + |> exclude_visibility(opts) end + defp exclude_blocked(query, user) do + query + |> where([n, a], a.actor not in ^user.blocks) + |> where( + [n, a], + fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.domain_blocks + ) + 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.muted_notifications) + |> 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 + + @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 + |> where( + [n, a], + not fragment( + "activity_visibility(?, ?, ?) = ANY (?)", + a.actor, + a.recipients, + a.data, + ^visibility + ) + ) + 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() + |> for_user_query(opts) |> Pagination.fetch_paginated(opts) end @@ -87,15 +157,33 @@ defmodule Pleroma.Notification do n in Notification, where: n.user_id == ^user_id, where: n.id <= ^id, + where: n.seen == false, update: [ set: [ seen: true, updated_at: ^NaiveDateTime.utc_now() ] - ] + ], + # Ideally we would preload object and activities here + # but Ecto does not support preloads in update_all + select: n.id ) - Repo.update_all(query, []) + {_, notification_ids} = Repo.update_all(query, []) + + Notification + |> where([n], n.id in ^notification_ids) + |> 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}) + |> Repo.all() end def read_one(%User{} = user, notification_id) do @@ -177,8 +265,10 @@ defmodule Pleroma.Notification do unless skip?(activity, user) do notification = %Notification{user_id: user.id, activity: activity} {:ok, notification} = Repo.insert(notification) - Streamer.stream("user", notification) - Streamer.stream("user:notification", notification) + + ["user", "user:notification"] + |> Streamer.stream(notification) + Push.send(notification) notification end @@ -203,11 +293,10 @@ defmodule Pleroma.Notification do def get_notified_from_activity(_, _local_only), do: [] + @spec skip?(Activity.t(), User.t()) :: boolean() def skip?(activity, user) do [ :self, - :blocked, - :muted, :followers, :follows, :non_followers, @@ -217,25 +306,15 @@ defmodule Pleroma.Notification do |> Enum.any?(&skip?(&1, activity, user)) end + @spec skip?(atom(), Activity.t(), User.t()) :: boolean() def skip?(:self, activity, user) do activity.data["actor"] == user.ap_id end - def skip?(:blocked, activity, user) do - actor = activity.data["actor"] - User.blocks?(user, %{ap_id: actor}) - end - - def skip?(:muted, activity, user) do - actor = activity.data["actor"] - - User.mutes?(user, %{ap_id: actor}) or CommonAPI.thread_muted?(user, activity) - end - def skip?( :followers, activity, - %{info: %{notification_settings: %{"followers" => false}}} = user + %{notification_settings: %{"followers" => false}} = user ) do actor = activity.data["actor"] follower = User.get_cached_by_ap_id(actor) @@ -245,14 +324,14 @@ defmodule Pleroma.Notification do def skip?( :non_followers, activity, - %{info: %{notification_settings: %{"non_followers" => false}}} = user + %{notification_settings: %{"non_followers" => false}} = user ) do actor = activity.data["actor"] follower = User.get_cached_by_ap_id(actor) !User.following?(follower, user) end - def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do + def skip?(:follows, activity, %{notification_settings: %{"follows" => false}} = user) do actor = activity.data["actor"] followed = User.get_cached_by_ap_id(actor) User.following?(user, followed) @@ -261,7 +340,7 @@ defmodule Pleroma.Notification do def skip?( :non_follows, activity, - %{info: %{notification_settings: %{"non_follows" => false}}} = user + %{notification_settings: %{"non_follows" => false}} = user ) do actor = activity.data["actor"] followed = User.get_cached_by_ap_id(actor)