a3254609488cfc3136b1d2b9ecad3c335be6a4fa
[akkoma] / lib / pleroma / marker.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Marker do
6 use Ecto.Schema
7
8 import Ecto.Changeset
9 import Ecto.Query
10
11 alias Ecto.Multi
12 alias Pleroma.Notification
13 alias Pleroma.Repo
14 alias Pleroma.User
15 alias __MODULE__
16
17 @timelines ["notifications"]
18
19 schema "markers" do
20 field(:last_read_id, :string, default: "")
21 field(:timeline, :string, default: "")
22 field(:lock_version, :integer, default: 0)
23 field(:unread_count, :integer, default: 0)
24
25 belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
26 timestamps()
27 end
28
29 def get_markers(user, timelines \\ []) do
30 Repo.all(get_query(user, timelines))
31 end
32
33 def upsert(%User{} = user, attrs) do
34 attrs
35 |> Map.take(@timelines)
36 |> Enum.reduce(Multi.new(), fn {timeline, timeline_attrs}, multi ->
37 marker =
38 user
39 |> get_marker(timeline)
40 |> changeset(timeline_attrs)
41
42 Multi.insert(multi, timeline, marker,
43 returning: true,
44 on_conflict: {:replace, [:last_read_id, :unread_count]},
45 conflict_target: [:user_id, :timeline]
46 )
47 end)
48 |> Repo.transaction()
49 end
50
51 @spec multi_set_unread_count(Multi.t(), User.t(), String.t()) :: Multi.t()
52 def multi_set_unread_count(multi, %User{} = user, "notifications") do
53 multi
54 |> Multi.run(:counters, fn _repo, _changes ->
55 {:ok,
56 %{
57 unread_count: Repo.aggregate(Notification.unread_count_query(user), :count, :id),
58 last_read_id: Repo.one(Notification.last_read_query(user))
59 }}
60 end)
61 |> Multi.insert(
62 :marker,
63 fn %{counters: attrs} ->
64 %Marker{timeline: "notifications", user_id: user.id}
65 |> struct(attrs)
66 |> Ecto.Changeset.change()
67 end,
68 returning: true,
69 on_conflict: {:replace, [:last_read_id, :unread_count]},
70 conflict_target: [:user_id, :timeline]
71 )
72 end
73
74 def multi_set_unread_count(multi, _, _), do: multi
75
76 defp get_marker(user, timeline) do
77 case Repo.find_resource(get_query(user, timeline)) do
78 {:ok, marker} -> %__MODULE__{marker | user: user}
79 _ -> %__MODULE__{timeline: timeline, user_id: user.id}
80 end
81 end
82
83 @doc false
84 defp changeset(marker, attrs) do
85 marker
86 |> cast(attrs, [:last_read_id, :unread_count])
87 |> validate_required([:user_id, :timeline, :last_read_id])
88 |> validate_inclusion(:timeline, @timelines)
89 end
90
91 defp by_timeline(query, timeline) do
92 from(m in query, where: m.timeline in ^List.wrap(timeline))
93 end
94
95 defp by_user_id(query, id), do: from(m in query, where: m.user_id == ^id)
96
97 defp get_query(user, timelines) do
98 __MODULE__
99 |> by_user_id(user.id)
100 |> by_timeline(timelines)
101 end
102 end