diff --git a/lib/philomena/polymorphic.ex b/lib/philomena/polymorphic.ex index b1592ced..29eee2b3 100644 --- a/lib/philomena/polymorphic.ex +++ b/lib/philomena/polymorphic.ex @@ -17,12 +17,20 @@ defmodule Philomena.Polymorphic do "User" => Philomena.Users.User } + @preloads %{ + "Comment" => [:user], + "Gallery" => [:creator], + "Image" => [:user], + "Post" => [:user], + "Topic" => [:forum, :user] + } + # Deal with Rails polymorphism BS def load_polymorphic(structs, associations) when is_list(associations) do Enum.reduce(associations, structs, fn asc, acc -> load_polymorphic(acc, asc) end) end - def load_polymorphic(structs, {name, {id, type}}) do + def load_polymorphic(structs, {name, [{id, type}]}) do modules_and_ids = structs |> Enum.group_by(&Map.get(&1, type), &Map.get(&1, id)) @@ -34,9 +42,11 @@ defmodule Philomena.Polymorphic do {nil, []} {type, ids} -> + pre = Map.get(@preloads, type, []) rows = @classes[type] |> where([m], m.id in ^ids) + |> preload(^pre) |> Repo.all() |> Map.new(fn r -> {r.id, r} end) diff --git a/lib/philomena/users/user.ex b/lib/philomena/users/user.ex index 48177bbc..f3b33adf 100644 --- a/lib/philomena/users/user.ex +++ b/lib/philomena/users/user.ex @@ -21,6 +21,8 @@ defmodule Philomena.Users.User do has_many :public_links, Philomena.UserLinks.UserLink, where: [public: true, aasm_state: "verified"] has_many :galleries, Philomena.Galleries.Gallery, foreign_key: :creator_id has_many :awards, Philomena.Badges.Award + has_many :unread_notifications, Philomena.Notifications.UnreadNotification + has_many :notifications, through: [:unread_notifications, :notification] belongs_to :current_filter, Philomena.Filters.Filter belongs_to :deleted_by_user, Philomena.Users.User diff --git a/lib/philomena_web/controllers/notification_controller.ex b/lib/philomena_web/controllers/notification_controller.ex new file mode 100644 index 00000000..bef8cd3f --- /dev/null +++ b/lib/philomena_web/controllers/notification_controller.ex @@ -0,0 +1,33 @@ +defmodule PhilomenaWeb.NotificationController do + use PhilomenaWeb, :controller + + alias Philomena.Notifications.{UnreadNotification, Notification} + alias Philomena.Polymorphic + alias Philomena.Repo + import Ecto.Query + + def index(conn, _params) do + user = conn.assigns.current_user + + notifications = + from n in Notification, + join: un in UnreadNotification, on: un.notification_id == n.id, + where: un.user_id == ^user.id + + notifications = + notifications + |> Repo.paginate(conn.assigns.scrivener) + + entries = + notifications.entries + |> Polymorphic.load_polymorphic( + actor: [actor_id: :actor_type], + actor_child: [actor_child_id: :actor_child_type] + ) + + notifications = + %{notifications | entries: entries} + + render(conn, "index.html", notifications: notifications) + end +end \ No newline at end of file diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index 3a371a9c..f4afaea7 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -49,6 +49,12 @@ defmodule PhilomenaWeb.Router do end end + scope "/", PhilomenaWeb do + pipe_through [:browser, :ensure_totp, :protected] + + resources "/notifications", NotificationController, only: [:index, :delete] + end + scope "/", PhilomenaWeb do pipe_through [:browser, :ensure_totp] diff --git a/lib/philomena_web/templates/notification/_channel.html.slime b/lib/philomena_web/templates/notification/_channel.html.slime new file mode 100644 index 00000000..18c01f84 --- /dev/null +++ b/lib/philomena_web/templates/notification/_channel.html.slime @@ -0,0 +1,4 @@ +strong> + = link @notification.actor.title, to: "#" + /= link @notification.actor.title, to: Routes.channel_path(@conn, :show, notification.actor) +=<> @notification.action diff --git a/lib/philomena_web/templates/notification/_forum.html.slime b/lib/philomena_web/templates/notification/_forum.html.slime new file mode 100644 index 00000000..81fcfcfa --- /dev/null +++ b/lib/philomena_web/templates/notification/_forum.html.slime @@ -0,0 +1,14 @@ +- forum = @notification.actor +- topic = @notification.actor_child + +=> render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: topic +=> @notification.action + +' titled + +strong> + = link topic.title, to: Routes.forum_topic_path(@conn, :show, forum, topic) + +' in + +=> link forum.name, to: Routes.forum_path(@conn, :show, forum) \ No newline at end of file diff --git a/lib/philomena_web/templates/notification/_gallery.html.slime b/lib/philomena_web/templates/notification/_gallery.html.slime new file mode 100644 index 00000000..cc25b25d --- /dev/null +++ b/lib/philomena_web/templates/notification/_gallery.html.slime @@ -0,0 +1,6 @@ +=> render PhilomenaWeb.UserAttributionView, "_user.html", object: %{user: @notification.actor.creator} +=> @notification.action + +strong> + = link @notification.actor.title, to: "#" + /= link @notification.actor.title, to: Routes.gallery_path(@conn, :show, @notification.actor) \ No newline at end of file diff --git a/lib/philomena_web/templates/notification/_image.html.slime b/lib/philomena_web/templates/notification/_image.html.slime new file mode 100644 index 00000000..7ab04e72 --- /dev/null +++ b/lib/philomena_web/templates/notification/_image.html.slime @@ -0,0 +1,5 @@ +=> render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @notification.actor_child +=> @notification.action + +strong + = link "##{@notification.actor_id}", to: Routes.image_path(@conn, :show, @notification.actor) <> "#comments" \ No newline at end of file diff --git a/lib/philomena_web/templates/notification/_notification.html.slime b/lib/philomena_web/templates/notification/_notification.html.slime index e69de29b..d076099e 100644 --- a/lib/philomena_web/templates/notification/_notification.html.slime +++ b/lib/philomena_web/templates/notification/_notification.html.slime @@ -0,0 +1,14 @@ += if @notification.actor do + .block.block--fixed.flex class="js-notification-id-#{@notification.id}" + = if @notification.actor_type == "Image" and @notification.actor do + .flex.flex--centered.flex__fixed.thumb-tiny-container.spacing-right + = render PhilomenaWeb.ImageView, "_image_container.html", image: @notification.actor, size: :thumb_tiny, conn: @conn + .flex.flex--centered.flex__grow + div + => render PhilomenaWeb.NotificationView, notification_template_path(@notification.actor_type), notification: @notification + => pretty_time @notification.updated_at + .flex.flex--centered.flex--no-wrap + a.button.button--separate-right title="Delete" data-click-markread=@notification.id + i.fa.fa-trash + a.button title="Unsubscribe" data-subscription-id=@notification.actor_id data-subscription-type=@notification.actor_type data-click-togglesubscription="unwatch" data-click-hide=".js-notification-id-#{@notification.id}" + .fa.fa-bell-slash \ No newline at end of file diff --git a/lib/philomena_web/templates/notification/_topic.html.slime b/lib/philomena_web/templates/notification/_topic.html.slime new file mode 100644 index 00000000..d2edd3bc --- /dev/null +++ b/lib/philomena_web/templates/notification/_topic.html.slime @@ -0,0 +1,8 @@ +- topic = @notification.actor +- post = @notification.actor_child + +=> render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: post +=> @notification.action + +strong + = link topic.title, to: Routes.forum_topic_path(@conn, :show, topic.forum, topic, post_id: post.id) <> "#post_#{post.id}" \ No newline at end of file diff --git a/lib/philomena_web/templates/notification/index.html.slime b/lib/philomena_web/templates/notification/index.html.slime new file mode 100644 index 00000000..508a13cc --- /dev/null +++ b/lib/philomena_web/templates/notification/index.html.slime @@ -0,0 +1,12 @@ +- route = fn p -> Routes.notification_path(@conn, :index, p) end + +h1 Notification Area +.walloftext + .block__header + = render PhilomenaWeb.PaginationView, "_pagination.html", page: @notifications, route: route + + = for notification <- @notifications do + = render PhilomenaWeb.NotificationView, "_notification.html", notification: notification + +p To get notifications on new comments and forum posts, click the 'Subscribe' button in the bar at the top of an image or forum topic. You'll get notifications here for any new posts or comments. +p By default you'll be subscribed to any images or topics you reply to. You can configure this in your user settings page. diff --git a/lib/philomena_web/views/notification_view.ex b/lib/philomena_web/views/notification_view.ex new file mode 100644 index 00000000..b3ef40ce --- /dev/null +++ b/lib/philomena_web/views/notification_view.ex @@ -0,0 +1,16 @@ +defmodule PhilomenaWeb.NotificationView do + use PhilomenaWeb, :view + + @template_paths %{ + "Channel" => "_channel.html", + "Forum" => "_forum.html", + "Gallery" => "_gallery.html", + "Image" => "_image.html", + "LivestreamChannel" => "_channel.html", + "Topic" => "_topic.html" + } + + def notification_template_path(actor_type) do + @template_paths[actor_type] + end +end \ No newline at end of file