From d4270397dcb2aebde8ed14fd89998ab57aaae545 Mon Sep 17 00:00:00 2001
From: Maksim Pechnikov <parallel588@gmail.com>
Date: Tue, 22 Oct 2019 13:42:59 +0300
Subject: [PATCH] Marker: added unread_count field

---
 lib/pleroma/marker.ex                         |  5 +-
 .../web/mastodon_api/views/marker_view.ex     |  1 +
 .../20191021113356_add_unread_to_marker.exs   | 49 +++++++++++++++++++
 .../controllers/marker_controller_test.exs    |  7 ++-
 .../mastodon_api/views/marker_view_test.exs   |  4 +-
 5 files changed, 61 insertions(+), 5 deletions(-)
 create mode 100644 priv/repo/migrations/20191021113356_add_unread_to_marker.exs

diff --git a/lib/pleroma/marker.ex b/lib/pleroma/marker.ex
index 7f87c86c3..c4d554980 100644
--- a/lib/pleroma/marker.ex
+++ b/lib/pleroma/marker.ex
@@ -18,6 +18,7 @@ defmodule Pleroma.Marker do
     field(:last_read_id, :string, default: "")
     field(:timeline, :string, default: "")
     field(:lock_version, :integer, default: 0)
+    field(:unread_count, :integer, default: 0)
 
     belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
     timestamps()
@@ -38,7 +39,7 @@ defmodule Pleroma.Marker do
 
       Multi.insert(multi, timeline, marker,
         returning: true,
-        on_conflict: {:replace, [:last_read_id]},
+        on_conflict: {:replace, [:last_read_id, :unread_count]},
         conflict_target: [:user_id, :timeline]
       )
     end)
@@ -55,7 +56,7 @@ defmodule Pleroma.Marker do
   @doc false
   defp changeset(marker, attrs) do
     marker
-    |> cast(attrs, [:last_read_id])
+    |> cast(attrs, [:last_read_id, :unread_count])
     |> validate_required([:user_id, :timeline, :last_read_id])
     |> validate_inclusion(:timeline, @timelines)
   end
diff --git a/lib/pleroma/web/mastodon_api/views/marker_view.ex b/lib/pleroma/web/mastodon_api/views/marker_view.ex
index 38fbeed5f..1501c2a30 100644
--- a/lib/pleroma/web/mastodon_api/views/marker_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/marker_view.ex
@@ -10,6 +10,7 @@ defmodule Pleroma.Web.MastodonAPI.MarkerView do
       Map.put_new(acc, m.timeline, %{
         last_read_id: m.last_read_id,
         version: m.lock_version,
+        unread_count: m.unread_count,
         updated_at: NaiveDateTime.to_iso8601(m.updated_at)
       })
     end)
diff --git a/priv/repo/migrations/20191021113356_add_unread_to_marker.exs b/priv/repo/migrations/20191021113356_add_unread_to_marker.exs
new file mode 100644
index 000000000..32789b7f9
--- /dev/null
+++ b/priv/repo/migrations/20191021113356_add_unread_to_marker.exs
@@ -0,0 +1,49 @@
+defmodule Pleroma.Repo.Migrations.AddUnreadToMarker do
+  use Ecto.Migration
+  import Ecto.Query
+  alias Pleroma.Repo
+  alias Pleroma.Notification
+
+  def up do
+    alter table(:markers) do
+      add_if_not_exists(:unread_count, :integer, default: 0)
+    end
+
+    flush()
+
+    update_markers()
+  end
+
+  def down do
+    alter table(:markers) do
+      remove_if_exists(:unread_count, :integer)
+    end
+  end
+
+  def update_markers do
+    from(q in Notification,
+      select: %{
+        timeline: "notifications",
+        user_id: q.user_id,
+        unread_count: fragment("COUNT(*) FILTER (WHERE seen = false) as unread_count"),
+        last_read_id: fragment("(MAX(id) FILTER (WHERE seen = true)::text) as last_read_id ")
+      },
+      group_by: [q.user_id]
+    )
+    |> Repo.all()
+    |> Enum.reduce(Ecto.Multi.new(), fn attrs, multi ->
+      marker =
+        Pleroma.Marker
+        |> struct(attrs)
+        |> Ecto.Changeset.change()
+
+      multi
+      |> Ecto.Multi.insert(attrs[:user_id], marker,
+        returning: true,
+        on_conflict: {:replace, [:last_read_id, :unread_count]},
+        conflict_target: [:user_id, :timeline]
+      )
+    end)
+    |> Pleroma.Repo.transaction()
+  end
+end
diff --git a/test/web/mastodon_api/controllers/marker_controller_test.exs b/test/web/mastodon_api/controllers/marker_controller_test.exs
index 1fcad873d..5e7b4001f 100644
--- a/test/web/mastodon_api/controllers/marker_controller_test.exs
+++ b/test/web/mastodon_api/controllers/marker_controller_test.exs
@@ -15,7 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do
       {:ok, %{"notifications" => marker}} =
         Pleroma.Marker.upsert(
           user,
-          %{"notifications" => %{"last_read_id" => "69420"}}
+          %{"notifications" => %{"last_read_id" => "69420", "unread_count" => 7}}
         )
 
       response =
@@ -28,6 +28,7 @@ defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do
       assert response == %{
                "notifications" => %{
                  "last_read_id" => "69420",
+                 "unread_count" => 7,
                  "updated_at" => NaiveDateTime.to_iso8601(marker.updated_at),
                  "version" => 0
                }
@@ -70,7 +71,8 @@ defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do
                "notifications" => %{
                  "last_read_id" => "69420",
                  "updated_at" => _,
-                 "version" => 0
+                 "version" => 0,
+                 "unread_count" => 0
                }
              } = response
     end
@@ -98,6 +100,7 @@ defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do
       assert response == %{
                "notifications" => %{
                  "last_read_id" => "69888",
+                 "unread_count" => 0,
                  "updated_at" => NaiveDateTime.to_iso8601(marker.updated_at),
                  "version" => 0
                }
diff --git a/test/web/mastodon_api/views/marker_view_test.exs b/test/web/mastodon_api/views/marker_view_test.exs
index 8a5c89d56..3ce794617 100644
--- a/test/web/mastodon_api/views/marker_view_test.exs
+++ b/test/web/mastodon_api/views/marker_view_test.exs
@@ -8,17 +8,19 @@ defmodule Pleroma.Web.MastodonAPI.MarkerViewTest do
   import Pleroma.Factory
 
   test "returns markers" do
-    marker1 = insert(:marker, timeline: "notifications", last_read_id: "17")
+    marker1 = insert(:marker, timeline: "notifications", last_read_id: "17", unread_count: 5)
     marker2 = insert(:marker, timeline: "home", last_read_id: "42")
 
     assert MarkerView.render("markers.json", %{markers: [marker1, marker2]}) == %{
              "home" => %{
                last_read_id: "42",
+               unread_count: 0,
                updated_at: NaiveDateTime.to_iso8601(marker2.updated_at),
                version: 0
              },
              "notifications" => %{
                last_read_id: "17",
+               unread_count: 5,
                updated_at: NaiveDateTime.to_iso8601(marker1.updated_at),
                version: 0
              }
-- 
2.49.0