aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/pleroma/marker.ex45
-rw-r--r--lib/pleroma/notification.ex47
-rw-r--r--lib/pleroma/web/mastodon_api/views/marker_view.ex5
3 files changed, 82 insertions, 15 deletions
diff --git a/lib/pleroma/marker.ex b/lib/pleroma/marker.ex
index 443927392..4d82860f5 100644
--- a/lib/pleroma/marker.ex
+++ b/lib/pleroma/marker.ex
@@ -9,24 +9,34 @@ defmodule Pleroma.Marker do
import Ecto.Query
alias Ecto.Multi
+ alias Pleroma.Notification
alias Pleroma.Repo
alias Pleroma.User
+ alias __MODULE__
@timelines ["notifications"]
+ @type t :: %__MODULE__{}
schema "markers" do
field(:last_read_id, :string, default: "")
field(:timeline, :string, default: "")
field(:lock_version, :integer, default: 0)
+ field(:unread_count, :integer, default: 0, virtual: true)
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
timestamps()
end
+ @doc "Gets markers by user and timeline."
+ @spec get_markers(User.t(), list(String)) :: list(t())
def get_markers(user, timelines \\ []) do
- Repo.all(get_query(user, timelines))
+ user
+ |> get_query(timelines)
+ |> unread_count_query()
+ |> Repo.all()
end
+ @spec upsert(User.t(), map()) :: {:ok | :error, any()}
def upsert(%User{} = user, attrs) do
attrs
|> Map.take(@timelines)
@@ -45,6 +55,27 @@ defmodule Pleroma.Marker do
|> Repo.transaction()
end
+ @spec multi_set_last_read_id(Multi.t(), User.t(), String.t()) :: Multi.t()
+ def multi_set_last_read_id(multi, %User{} = user, "notifications") do
+ multi
+ |> Multi.run(:counters, fn _repo, _changes ->
+ {:ok, %{last_read_id: Repo.one(Notification.last_read_query(user))}}
+ end)
+ |> Multi.insert(
+ :marker,
+ fn %{counters: attrs} ->
+ %Marker{timeline: "notifications", user_id: user.id}
+ |> struct(attrs)
+ |> Ecto.Changeset.change()
+ end,
+ returning: true,
+ on_conflict: {:replace, [:last_read_id]},
+ conflict_target: [:user_id, :timeline]
+ )
+ end
+
+ def multi_set_last_read_id(multi, _, _), do: multi
+
defp get_marker(user, timeline) do
case Repo.find_resource(get_query(user, timeline)) do
{:ok, marker} -> %__MODULE__{marker | user: user}
@@ -71,4 +102,16 @@ defmodule Pleroma.Marker do
|> by_user_id(user.id)
|> by_timeline(timelines)
end
+
+ defp unread_count_query(query) do
+ from(
+ q in query,
+ left_join: n in "notifications",
+ on: n.user_id == q.user_id and n.seen == false,
+ group_by: [:id],
+ select_merge: %{
+ unread_count: fragment("count(?)", n.id)
+ }
+ )
+ end
end
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index 04ee510b9..3084bac3b 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -5,7 +5,9 @@
defmodule Pleroma.Notification do
use Ecto.Schema
+ alias Ecto.Multi
alias Pleroma.Activity
+ alias Pleroma.Marker
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Pagination
@@ -38,6 +40,17 @@ defmodule Pleroma.Notification do
|> cast(attrs, [:seen])
end
+ @spec last_read_query(User.t()) :: Ecto.Queryable.t()
+ def last_read_query(user) do
+ from(q in Pleroma.Notification,
+ where: q.user_id == ^user.id,
+ where: q.seen == true,
+ select: type(q.id, :string),
+ limit: 1,
+ order_by: [desc: :id]
+ )
+ end
+
defp for_user_query_ap_id_opts(user, opts) do
ap_id_relationships =
[:block] ++
@@ -186,25 +199,23 @@ defmodule Pleroma.Notification do
|> Repo.all()
end
- def set_read_up_to(%{id: user_id} = _user, id) do
+ 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,
- 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
)
- {_, notification_ids} = Repo.update_all(query, [])
+ {:ok, %{ids: {_, notification_ids}}} =
+ Multi.new()
+ |> Multi.update_all(:ids, query, set: [seen: true, updated_at: NaiveDateTime.utc_now()])
+ |> Marker.multi_set_last_read_id(user, "notifications")
+ |> Repo.transaction()
Notification
|> where([n], n.id in ^notification_ids)
@@ -221,11 +232,18 @@ defmodule Pleroma.Notification do
|> Repo.all()
end
+ @spec read_one(User.t(), String.t()) ::
+ {:ok, Notification.t()} | {:error, Ecto.Changeset.t()} | nil
def read_one(%User{} = user, notification_id) do
with {:ok, %Notification{} = notification} <- get(user, notification_id) do
- notification
- |> changeset(%{seen: true})
- |> Repo.update()
+ Multi.new()
+ |> Multi.update(:update, changeset(notification, %{seen: true}))
+ |> Marker.multi_set_last_read_id(user, "notifications")
+ |> Repo.transaction()
+ |> case do
+ {:ok, %{update: notification}} -> {:ok, notification}
+ {:error, :update, changeset, _} -> {:error, changeset}
+ end
end
end
@@ -307,8 +325,11 @@ defmodule Pleroma.Notification do
# TODO move to sql, too.
def create_notification(%Activity{} = activity, %User{} = user, do_send \\ true) do
unless skip?(activity, user) do
- notification = %Notification{user_id: user.id, activity: activity}
- {:ok, notification} = Repo.insert(notification)
+ {:ok, %{notification: notification}} =
+ Multi.new()
+ |> Multi.insert(:notification, %Notification{user_id: user.id, activity: activity})
+ |> Marker.multi_set_last_read_id(user, "notifications")
+ |> Repo.transaction()
if do_send do
Streamer.stream(["user", "user:notification"], notification)
diff --git a/lib/pleroma/web/mastodon_api/views/marker_view.ex b/lib/pleroma/web/mastodon_api/views/marker_view.ex
index 985368fe5..415dae93b 100644
--- a/lib/pleroma/web/mastodon_api/views/marker_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/marker_view.ex
@@ -10,7 +10,10 @@ defmodule Pleroma.Web.MastodonAPI.MarkerView do
Map.put_new(acc, m.timeline, %{
last_read_id: m.last_read_id,
version: m.lock_version,
- updated_at: NaiveDateTime.to_iso8601(m.updated_at)
+ updated_at: NaiveDateTime.to_iso8601(m.updated_at),
+ pleroma: %{
+ unread_count: m.unread_count
+ }
})
end)
end