philomena/lib/philomena/notifications/category.ex
2024-07-29 08:54:42 -04:00

166 lines
4.3 KiB
Elixir

defmodule Philomena.Notifications.Category do
@moduledoc """
Notification category querying.
"""
import Ecto.Query, warn: false
alias Philomena.Repo
alias Philomena.Notifications.ChannelLiveNotification
alias Philomena.Notifications.ForumPostNotification
alias Philomena.Notifications.ForumTopicNotification
alias Philomena.Notifications.GalleryImageNotification
alias Philomena.Notifications.ImageCommentNotification
alias Philomena.Notifications.ImageMergeNotification
@type t ::
:channel_live
| :forum_post
| :forum_topic
| :gallery_image
| :image_comment
| :image_merge
@doc """
Return a list of all supported categories.
"""
def categories do
[
:channel_live,
:forum_post,
:forum_topic,
:gallery_image,
:image_comment,
:image_merge
]
end
@doc """
Return the count of all currently unread notifications for the user in all categories.
## Examples
iex> total_unread_notification_count(user)
15
"""
def total_unread_notification_count(user) do
categories()
|> Enum.map(fn category ->
category
|> query_for_category_and_user(user)
|> exclude(:preload)
|> select([_], %{one: 1})
end)
|> union_all_queries()
|> Repo.aggregate(:count)
end
defp union_all_queries([query | rest]) do
Enum.reduce(rest, query, fn q, acc -> union_all(acc, ^q) end)
end
@doc """
Gather up and return the top N notifications for the user, for each category of
unread notification currently existing.
## Examples
iex> unread_notifications_for_user(user, page_size: 10)
[
channel_live: [],
forum_post: [%ForumPostNotification{...}, ...],
forum_topic: [%ForumTopicNotification{...}, ...],
gallery_image: [],
image_comment: [%ImageCommentNotification{...}, ...],
image_merge: []
]
"""
def unread_notifications_for_user(user, pagination) do
Enum.map(categories(), fn category ->
results =
category
|> query_for_category_and_user(user)
|> order_by(desc: :updated_at)
|> Repo.paginate(pagination)
{category, results}
end)
end
@doc """
Returns paginated unread notifications for the user, given the category.
## Examples
iex> unread_notifications_for_user_and_category(user, :image_comment)
[%ImageCommentNotification{...}]
"""
def unread_notifications_for_user_and_category(user, category, pagination) do
category
|> query_for_category_and_user(user)
|> order_by(desc: :updated_at)
|> Repo.paginate(pagination)
end
@doc """
Determine the category of a notification.
## Examples
iex> notification_category(%ImageCommentNotification{})
:image_comment
"""
def notification_category(n) do
case n.__struct__ do
ChannelLiveNotification -> :channel_live
GalleryImageNotification -> :gallery_image
ImageCommentNotification -> :image_comment
ImageMergeNotification -> :image_merge
ForumPostNotification -> :forum_post
ForumTopicNotification -> :forum_topic
end
end
@doc """
Returns an `m:Ecto.Query` that finds unread notifications for the given category,
for the given user, with preloads applied.
## Examples
iex> query_for_category_and_user(:channel_live, user)
#Ecto.Query<from c0 in ChannelLiveNotification, where: c0.user_id == ^1, preload: [:channel]>
"""
def query_for_category_and_user(category, user) do
query =
case category do
:channel_live ->
from(n in ChannelLiveNotification, preload: :channel)
:gallery_image ->
from(n in GalleryImageNotification, preload: [gallery: :creator])
:image_comment ->
from(n in ImageCommentNotification,
preload: [image: [:sources, tags: :aliases], comment: :user]
)
:image_merge ->
from(n in ImageMergeNotification,
preload: [:source, target: [:sources, tags: :aliases]]
)
:forum_topic ->
from(n in ForumTopicNotification, preload: [topic: [:forum, :user]])
:forum_post ->
from(n in ForumPostNotification, preload: [topic: :forum, post: :user])
end
where(query, user_id: ^user.id)
end
end