X-Git-Url: https://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fnotification.ex;h=b7c880c51584da65c62d87e194ee6dbc3bdc1b1d;hb=c623b4324deaf236334a0f77a81435b5bffadf3c;hp=e256920068147f8450799e520b60311c9aecd68b;hpb=efa445a75b242787a30ffbc2eb16bd165260f66c;p=akkoma diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index e25692006..b7c880c51 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -11,7 +11,6 @@ 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 @@ -19,6 +18,8 @@ defmodule Pleroma.Notification do import Ecto.Query import Ecto.Changeset + @type t :: %__MODULE__{} + schema "notifications" do field(:seen, :boolean, default: false) belongs_to(:user, User, type: Pleroma.FlakeId) @@ -32,46 +33,102 @@ defmodule Pleroma.Notification do |> cast(attrs, [:seen]) end - def for_user_query(user) do - 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: + def for_user_query(user, opts \\ []) do + query = + Notification + |> where(user_id: ^user.id) + |> where( + [n, a], fragment( - "(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)", - object.data, - a.data + "? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')", + a.actor ) - ) - |> preload([n, a, o], activity: {a, object: o}) + ) + |> 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}) + + if opts[:with_muted] 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) + ) + |> where([n, a, o, tm], is_nil(tm.user_id)) + end end def for_user(user, opts \\ %{}) do user - |> for_user_query() + |> for_user_query(opts) |> Pagination.fetch_paginated(opts) end + @doc """ + Returns notifications for user received since given date. + + ## Examples + + iex> Pleroma.Notification.for_user_since(%Pleroma.User{}, ~N[2019-04-13 11:22:33]) + [%Pleroma.Notification{}, %Pleroma.Notification{}] + + iex> Pleroma.Notification.for_user_since(%Pleroma.User{}, ~N[2019-04-15 11:22:33]) + [] + """ + @spec for_user_since(Pleroma.User.t(), NaiveDateTime.t()) :: [t()] + def for_user_since(user, date) do + from(n in for_user_query(user), + where: n.updated_at > ^date + ) + |> Repo.all() + end + def set_read_up_to(%{id: user_id} = _user, id) do query = from( n in Notification, where: n.user_id == ^user_id, where: n.id <= ^id, + where: n.seen == false, update: [ - set: [seen: true] - ] + 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 @@ -127,8 +184,7 @@ defmodule Pleroma.Notification do end end - def create_notifications(%Activity{data: %{"to" => _, "type" => type}} = activity) - when type in ["Create", "Like", "Announce", "Follow"] do + def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = activity) do object = Object.normalize(activity) unless object && object.data["type"] == "Answer" do @@ -140,6 +196,13 @@ defmodule Pleroma.Notification do end end + def create_notifications(%Activity{data: %{"to" => _, "type" => type}} = activity) + when type in ["Like", "Announce", "Follow"] do + users = get_notified_from_activity(activity) + notifications = Enum.map(users, fn user -> create_notification(activity, user) end) + {:ok, notifications} + end + def create_notifications(_), do: {:ok, []} # TODO move to sql, too. @@ -173,11 +236,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, @@ -187,21 +249,11 @@ 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,