From bd8b92ea5e1bb6a97b02e2335fbcaf389ded2c1e Mon Sep 17 00:00:00 2001
From: Ashlynn Anderson <pea@pea.sh>
Date: Mon, 5 Aug 2019 15:35:34 -0400
Subject: [PATCH 1/9] Remove dynamic config as default, add healthcheck

---
 config/docker.exs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config/docker.exs b/config/docker.exs
index 63ab4cdee..f9f27d141 100644
--- a/config/docker.exs
+++ b/config/docker.exs
@@ -10,7 +10,7 @@ config :pleroma, :instance,
   notify_email: System.get_env("NOTIFY_EMAIL"),
   limit: 5000,
   registrations_open: false,
-  dynamic_configuration: true
+  healthcheck: true
 
 config :pleroma, Pleroma.Repo,
   adapter: Ecto.Adapters.Postgres,

From 4b422b54699ac55a1bc32d2b42c0d55d0b68b4fb Mon Sep 17 00:00:00 2001
From: Ashlynn Anderson <pea@pea.sh>
Date: Tue, 3 Sep 2019 11:44:57 -0400
Subject: [PATCH 2/9] Switch to official elixir:1.9-alpine image for build

---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index 268ec61dc..59a352bbc 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM rinpatch/elixir:1.9.0-rc.0-alpine as build
+FROM elixir:1.9-alpine as build
 
 COPY . .
 

From c2b6c1b089a813cf8c7cbd54c0f80bee4985522c Mon Sep 17 00:00:00 2001
From: rinpatch <rinpatch@sdf.org>
Date: Wed, 4 Sep 2019 11:33:08 +0300
Subject: [PATCH 3/9] Extend `/api/pleroma/notifications/read` to mark multiple
 notifications as read and make it respond with Mastoapi entities

---
 CHANGELOG.md                                  |  1 +
 docs/api/pleroma_api.md                       | 11 ++--
 lib/pleroma/notification.ex                   | 21 ++++++-
 .../web/pleroma_api/pleroma_api_controller.ex | 25 +++++++++
 lib/pleroma/web/router.ex                     |  7 +--
 .../pleroma_api_controller_test.exs           | 56 +++++++++++++++++++
 test/web/twitter_api/util_controller_test.exs | 32 -----------
 7 files changed, 108 insertions(+), 45 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8264688d6..40f4580f7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 ### Changed
 - **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
 - **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired
+- **Breaking:** `/api/pleroma/notifications/read` is moved to `/api/v1/pleroma/notifications/read` and now supports `max_id` and responds with Mastodon API entities.
 - Configuration: OpenGraph and TwitterCard providers enabled by default
 - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
 - Federation: Return 403 errors when trying to request pages from a user's follower/following collections if they have `hide_followers`/`hide_follows` set
diff --git a/docs/api/pleroma_api.md b/docs/api/pleroma_api.md
index b134b31a8..e76a35b3b 100644
--- a/docs/api/pleroma_api.md
+++ b/docs/api/pleroma_api.md
@@ -126,13 +126,14 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi
 ## `/api/pleroma/admin/`…
 See [Admin-API](Admin-API.md)
 
-## `/api/pleroma/notifications/read`
-### Mark a single notification as read
+## `/api/pleroma/v1/notifications/read`
+### Mark notifications as read
 * Method `POST`
 * Authentication: required
-* Params:
-    * `id`: notification's id
-* Response: JSON. Returns `{"status": "success"}` if the reading was successful, otherwise returns `{"error": "error_msg"}`
+* Params (mutually exclusive):
+    * `id`: a single notification id to read
+    * `max_id`: read all notifications up to this id
+* Response: Notification entity/Array of Notification entities. In case of `max_id`, only the first 80 notifications will be returned.
 
 ## `/api/v1/pleroma/accounts/:id/subscribe`
 ### Subscribe to receive notifications for all statuses posted by a user
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index 5d29af853..d7e232992 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -102,15 +102,32 @@ 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, [])
+
+    from(n in Notification, where: 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
diff --git a/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex
index b6d2bf86b..f4df3b024 100644
--- a/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex
+++ b/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex
@@ -8,8 +8,10 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
   import Pleroma.Web.ControllerHelper, only: [add_link_headers: 7]
 
   alias Pleroma.Conversation.Participation
+  alias Pleroma.Notification
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.MastodonAPI.ConversationView
+  alias Pleroma.Web.MastodonAPI.NotificationView
   alias Pleroma.Web.MastodonAPI.StatusView
 
   def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
@@ -70,4 +72,27 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
       |> render("participation.json", %{participation: participation, for: user})
     end
   end
+
+  def read_notification(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
+    with {:ok, notification} <- Notification.read_one(user, notification_id) do
+      conn
+      |> put_view(NotificationView)
+      |> render("show.json", %{notification: notification, for: user})
+    else
+      {:error, message} ->
+        conn
+        |> put_status(:bad_request)
+        |> json(%{"error" => message})
+    end
+  end
+
+  def read_notification(%{assigns: %{user: user}} = conn, %{"max_id" => max_id}) do
+    with notifications <- Notification.set_read_up_to(user, max_id) do
+      notifications = Enum.take(notifications, 80)
+
+      conn
+      |> put_view(NotificationView)
+      |> render("index.json", %{notifications: notifications, for: user})
+    end
+  end
 end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 969dc66fd..44a4279f7 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -236,12 +236,6 @@ defmodule Pleroma.Web.Router do
       post("/blocks_import", UtilController, :blocks_import)
       post("/follow_import", UtilController, :follow_import)
     end
-
-    scope [] do
-      pipe_through(:oauth_read)
-
-      post("/notifications/read", UtilController, :notifications_read)
-    end
   end
 
   scope "/oauth", Pleroma.Web.OAuth do
@@ -277,6 +271,7 @@ defmodule Pleroma.Web.Router do
     scope [] do
       pipe_through(:oauth_write)
       patch("/conversations/:id", PleromaAPIController, :update_conversation)
+      post("/notifications/read", PleromaAPIController, :read_notification)
     end
   end
 
diff --git a/test/web/pleroma_api/pleroma_api_controller_test.exs b/test/web/pleroma_api/pleroma_api_controller_test.exs
index ed6b79727..7eaeda4a0 100644
--- a/test/web/pleroma_api/pleroma_api_controller_test.exs
+++ b/test/web/pleroma_api/pleroma_api_controller_test.exs
@@ -6,6 +6,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
   use Pleroma.Web.ConnCase
 
   alias Pleroma.Conversation.Participation
+  alias Pleroma.Notification
   alias Pleroma.Repo
   alias Pleroma.Web.CommonAPI
 
@@ -91,4 +92,59 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
     assert user in participation.recipients
     assert other_user in participation.recipients
   end
+
+  describe "POST /api/v1/pleroma/notifications/read" do
+    test "it marks a single notification as read", %{conn: conn} do
+      user1 = insert(:user)
+      user2 = insert(:user)
+      {:ok, activity1} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
+      {:ok, activity2} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
+      {:ok, [notification1]} = Notification.create_notifications(activity1)
+      {:ok, [notification2]} = Notification.create_notifications(activity2)
+
+      response =
+        conn
+        |> assign(:user, user1)
+        |> post("/api/v1/pleroma/notifications/read", %{"id" => "#{notification1.id}"})
+        |> json_response(:ok)
+
+      assert %{"pleroma" => %{"is_seen" => true}} = response
+      assert Repo.get(Notification, notification1.id).seen
+      refute Repo.get(Notification, notification2.id).seen
+    end
+
+    test "it marks multiple notifications as read", %{conn: conn} do
+      user1 = insert(:user)
+      user2 = insert(:user)
+      {:ok, _activity1} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
+      {:ok, _activity2} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
+      {:ok, _activity3} = CommonAPI.post(user2, %{"status" => "HIE @#{user1.nickname}"})
+
+      [notification3, notification2, notification1] = Notification.for_user(user1, %{limit: 3})
+
+      [response1, response2] =
+        conn
+        |> assign(:user, user1)
+        |> post("/api/v1/pleroma/notifications/read", %{"max_id" => "#{notification2.id}"})
+        |> json_response(:ok)
+
+      assert %{"pleroma" => %{"is_seen" => true}} = response1
+      assert %{"pleroma" => %{"is_seen" => true}} = response2
+      assert Repo.get(Notification, notification1.id).seen
+      assert Repo.get(Notification, notification2.id).seen
+      refute Repo.get(Notification, notification3.id).seen
+    end
+
+    test "it returns error when notification not found", %{conn: conn} do
+      user1 = insert(:user)
+
+      response =
+        conn
+        |> assign(:user, user1)
+        |> post("/api/v1/pleroma/notifications/read", %{"id" => "22222222222222"})
+        |> json_response(:bad_request)
+
+      assert response == %{"error" => "Cannot get notification"}
+    end
+  end
 end
diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs
index fe4ffdb59..cf8e69d2b 100644
--- a/test/web/twitter_api/util_controller_test.exs
+++ b/test/web/twitter_api/util_controller_test.exs
@@ -5,7 +5,6 @@
 defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
   use Pleroma.Web.ConnCase
 
-  alias Pleroma.Notification
   alias Pleroma.Repo
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI
@@ -141,37 +140,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
     end
   end
 
-  describe "POST /api/pleroma/notifications/read" do
-    test "it marks a single notification as read", %{conn: conn} do
-      user1 = insert(:user)
-      user2 = insert(:user)
-      {:ok, activity1} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
-      {:ok, activity2} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
-      {:ok, [notification1]} = Notification.create_notifications(activity1)
-      {:ok, [notification2]} = Notification.create_notifications(activity2)
-
-      conn
-      |> assign(:user, user1)
-      |> post("/api/pleroma/notifications/read", %{"id" => "#{notification1.id}"})
-      |> json_response(:ok)
-
-      assert Repo.get(Notification, notification1.id).seen
-      refute Repo.get(Notification, notification2.id).seen
-    end
-
-    test "it returns error when notification not found", %{conn: conn} do
-      user1 = insert(:user)
-
-      response =
-        conn
-        |> assign(:user, user1)
-        |> post("/api/pleroma/notifications/read", %{"id" => "22222222222222"})
-        |> json_response(403)
-
-      assert response == %{"error" => "Cannot get notification"}
-    end
-  end
-
   describe "PUT /api/pleroma/notification_settings" do
     test "it updates notification settings", %{conn: conn} do
       user = insert(:user)

From 7c3838090f86fbfdbf4e45fcfbabc21c19f26924 Mon Sep 17 00:00:00 2001
From: rinpatch <rinpatch@sdf.org>
Date: Wed, 4 Sep 2019 10:14:15 +0000
Subject: [PATCH 4/9] Apply suggestion to lib/pleroma/notification.ex

---
 lib/pleroma/notification.ex | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index d7e232992..b7c880c51 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -116,7 +116,8 @@ defmodule Pleroma.Notification do
 
     {_, notification_ids} = Repo.update_all(query, [])
 
-    from(n in Notification, where: n.id in ^notification_ids)
+    Notification
+    |> where([n], n.id in ^notification_ids)
     |> join(:inner, [n], activity in assoc(n, :activity))
     |> join(:left, [n, a], object in Object,
       on:

From 377aa9fb90ff1c8537112f23bfc329f1ac0696b4 Mon Sep 17 00:00:00 2001
From: rinpatch <rinpatch@sdf.org>
Date: Wed, 4 Sep 2019 10:37:43 +0000
Subject: [PATCH 5/9] Apply suggestion to docs/api/pleroma_api.md

---
 docs/api/pleroma_api.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/api/pleroma_api.md b/docs/api/pleroma_api.md
index e76a35b3b..c08ee9ecd 100644
--- a/docs/api/pleroma_api.md
+++ b/docs/api/pleroma_api.md
@@ -126,7 +126,7 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi
 ## `/api/pleroma/admin/`…
 See [Admin-API](Admin-API.md)
 
-## `/api/pleroma/v1/notifications/read`
+## `/api/v1/pleroma/notifications/read`
 ### Mark notifications as read
 * Method `POST`
 * Authentication: required

From 328b2612cd957aa3ad810101a20037e4e9843bb0 Mon Sep 17 00:00:00 2001
From: rinpatch <rinpatch@sdf.org>
Date: Wed, 4 Sep 2019 13:39:39 +0300
Subject: [PATCH 6/9] Clarify that read notifications are returned

---
 docs/api/pleroma_api.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/api/pleroma_api.md b/docs/api/pleroma_api.md
index c08ee9ecd..7d343e97a 100644
--- a/docs/api/pleroma_api.md
+++ b/docs/api/pleroma_api.md
@@ -133,7 +133,7 @@ See [Admin-API](Admin-API.md)
 * Params (mutually exclusive):
     * `id`: a single notification id to read
     * `max_id`: read all notifications up to this id
-* Response: Notification entity/Array of Notification entities. In case of `max_id`, only the first 80 notifications will be returned.
+* Response: Notification entity/Array of Notification entities that were read. In case of `max_id`, only the first 80 read notifications will be returned.
 
 ## `/api/v1/pleroma/accounts/:id/subscribe`
 ### Subscribe to receive notifications for all statuses posted by a user

From 3face454671bfdf2b850daf9dcb05468eb909e95 Mon Sep 17 00:00:00 2001
From: rinpatch <rinpatch@sdf.org>
Date: Wed, 4 Sep 2019 14:16:56 +0300
Subject: [PATCH 7/9] Mastodon API: Add `pleroma.thread_muted` to Status entity

Needed for pleroma-fe!941
---
 CHANGELOG.md                                  |  1 +
 docs/api/differences_in_mastoapi_responses.md |  1 +
 .../web/mastodon_api/views/status_view.ex     |  3 ++-
 .../mastodon_api/views/status_view_test.exs   | 21 ++++++++++++++++++-
 4 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40f4580f7..a414ba5e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - **Breaking:** `/api/pleroma/notifications/read` is moved to `/api/v1/pleroma/notifications/read` and now supports `max_id` and responds with Mastodon API entities.
 - Configuration: OpenGraph and TwitterCard providers enabled by default
 - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
+- Mastodon API: `pleroma.thread_muted` key in the Status entity
 - Federation: Return 403 errors when trying to request pages from a user's follower/following collections if they have `hide_followers`/`hide_follows` set
 - NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
 - NodeInfo: Return `mailerEnabled` in `metadata`
diff --git a/docs/api/differences_in_mastoapi_responses.md b/docs/api/differences_in_mastoapi_responses.md
index f34e3dd72..02f90f3e8 100644
--- a/docs/api/differences_in_mastoapi_responses.md
+++ b/docs/api/differences_in_mastoapi_responses.md
@@ -26,6 +26,7 @@ Has these additional fields under the `pleroma` object:
 - `content`: a map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
 - `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
 - `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire
+- `thread_muted`: true if the thread the post belongs to is muted
 
 ## Attachments
 
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index a4ee0b5dd..4c3c8c564 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -299,7 +299,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
         content: %{"text/plain" => content_plaintext},
         spoiler_text: %{"text/plain" => summary_plaintext},
         expires_at: expires_at,
-        direct_conversation_id: direct_conversation_id
+        direct_conversation_id: direct_conversation_id,
+        thread_muted: thread_muted?
       }
     }
   end
diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs
index 1b6beb6d2..90451cbdc 100644
--- a/test/web/mastodon_api/views/status_view_test.exs
+++ b/test/web/mastodon_api/views/status_view_test.exs
@@ -150,7 +150,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
         content: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["content"])},
         spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["summary"])},
         expires_at: nil,
-        direct_conversation_id: nil
+        direct_conversation_id: nil,
+        thread_muted: false
       }
     }
 
@@ -173,6 +174,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
     assert status.muted == true
   end
 
+  test "tells if the message is thread muted" do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    {:ok, user} = User.mute(user, other_user)
+
+    {:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"})
+    status = StatusView.render("status.json", %{activity: activity, for: user})
+
+    assert status.pleroma.thread_muted == false
+
+    {:ok, activity} = CommonAPI.add_mute(user, activity)
+
+    status = StatusView.render("status.json", %{activity: activity, for: user})
+
+    assert status.pleroma.thread_muted == true
+  end
+
   test "tells if the status is bookmarked" do
     user = insert(:user)
 

From 053b17f57ecd9e1c3f82118e2a5e5c3b2937969d Mon Sep 17 00:00:00 2001
From: Ashlynn Anderson <pea@pea.sh>
Date: Wed, 4 Sep 2019 14:56:26 -0400
Subject: [PATCH 8/9] Switch to alpine:3.9 to avoid dlsym errors

---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index 59a352bbc..c61dcfde9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,7 +12,7 @@ RUN apk add git gcc g++ musl-dev make &&\
 	mkdir release &&\
 	mix release --path release
 
-FROM alpine:latest
+FROM alpine:3.9
 
 ARG HOME=/opt/pleroma
 ARG DATA=/var/lib/pleroma

From 558969a0fd7f64387e59a54b5733d63d3a46a031 Mon Sep 17 00:00:00 2001
From: rinpatch <rinpatch@sdf.org>
Date: Thu, 5 Sep 2019 08:32:49 +0300
Subject: [PATCH 9/9] Do not crash if one notification failed to render

---
 CHANGELOG.md                                            | 1 +
 lib/pleroma/web/mastodon_api/views/notification_view.ex | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40f4580f7..80aed3491 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Federation/MediaProxy not working with instances that have wrong certificate order
 - Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`)
 - Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity
+- Mastodon API: Notifications endpoint crashing if one notification failed to render
 - Mastodon API: follower/following counters not being nullified, when `hide_follows`/`hide_followers` is set
 - Mastodon API: `muted` in the Status entity, using author's account to determine if the tread was muted
 - Mastodon API: Add `account_id`, `type`, `offset`, and `limit` to search API (`/api/v1/search` and `/api/v2/search`)
diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex
index 27e9cab06..ec8eadcaa 100644
--- a/lib/pleroma/web/mastodon_api/views/notification_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex
@@ -14,7 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
   alias Pleroma.Web.MastodonAPI.StatusView
 
   def render("index.json", %{notifications: notifications, for: user}) do
-    render_many(notifications, NotificationView, "show.json", %{for: user})
+    safe_render_many(notifications, NotificationView, "show.json", %{for: user})
   end
 
   def render("show.json", %{