From 8131782a204cfd7264796a2235ac49ac008b4049 Mon Sep 17 00:00:00 2001 From: "byte[]" Date: Sun, 17 Nov 2019 12:50:42 -0500 Subject: [PATCH] add image subscriptions --- assets/js/boorujs.js | 4 +- assets/js/notifications.js | 22 +++--- lib/philomena/images.ex | 76 +++---------------- .../image/subscription_controller.ex | 35 +++++++++ .../controllers/image_controller.ex | 8 +- lib/philomena_web/router.ex | 1 + .../templates/image/_image_meta.html.slime | 4 +- .../templates/image/show.html.slime | 2 +- .../image/subscription/_error.html.slime | 2 + .../subscription/_subscription.html.slime | 23 ++++++ .../views/image/subscription_view.ex | 3 + 11 files changed, 98 insertions(+), 82 deletions(-) create mode 100644 lib/philomena_web/controllers/image/subscription_controller.ex create mode 100644 lib/philomena_web/templates/image/subscription/_error.html.slime create mode 100644 lib/philomena_web/templates/image/subscription/_subscription.html.slime create mode 100644 lib/philomena_web/views/image/subscription_view.ex diff --git a/assets/js/boorujs.js b/assets/js/boorujs.js index e5e4841e..52a62934 100644 --- a/assets/js/boorujs.js +++ b/assets/js/boorujs.js @@ -8,7 +8,7 @@ import { $ } from './utils/dom'; import { fetchHtml, handleError } from './utils/requests'; import { showBlock } from './utils/image'; import { addTag } from './tagsinput'; -import { toggleSubscription, markRead } from './notifications'; +import { markRead } from './notifications'; // Event types and any qualifying conditions - return true to not run action const types = { @@ -74,8 +74,6 @@ const actions = { unfilter(data) { showBlock(data.el.closest('.image-show-container')); }, - toggleSubscription, - markRead, }; diff --git a/assets/js/notifications.js b/assets/js/notifications.js index 7105b9ca..4926c739 100644 --- a/assets/js/notifications.js +++ b/assets/js/notifications.js @@ -4,6 +4,7 @@ import { fetchJson, handleError } from './utils/requests'; import { $, $$, hideEl, toggleEl } from './utils/dom'; +import { delegate } from './utils/events'; import store from './utils/store'; const NOTIFICATION_INTERVAL = 600000, @@ -14,16 +15,17 @@ function makeRequest(verb, action, body) { } -function toggleSubscription(data) { - const { subscriptionId, subscriptionType } = data.el.dataset; - const subscriptionElements = $$(`.js-notification-${subscriptionType + subscriptionId}`); - - makeRequest('PUT', data.value, { id: subscriptionId, actor_class: subscriptionType }) // eslint-disable-line camelcase - .then(() => toggleEl(subscriptionElements)) - .catch(() => data.el.textContent = 'Error!'); +function bindSubscriptionLinks() { + delegate(document, 'fetchcomplete', { + '.js-subscription-link': event => { + const target = $("#js-subscription-target"); + event.detail.text().then(text => { + target.outerHTML = text; + }); + } + }); } - function markRead(data) { const notificationId = data.value; const notification = $(`.js-notification-id-${notificationId}`); @@ -72,6 +74,8 @@ function setupNotifications() { // Update ticker when the stored value changes - this will occur in all open tabs store.watch('notificationCount', updateNotificationTicker); + + bindSubscriptionLinks(); } -export { setupNotifications, toggleSubscription, markRead }; +export { setupNotifications, markRead }; diff --git a/lib/philomena/images.ex b/lib/philomena/images.ex index d7f2134e..4b22c2a0 100644 --- a/lib/philomena/images.ex +++ b/lib/philomena/images.ex @@ -122,35 +122,13 @@ defmodule Philomena.Images do alias Philomena.Images.Subscription - @doc """ - Returns the list of image_subscriptions. - - ## Examples - - iex> list_image_subscriptions() - [%Subscription{}, ...] - - """ - def list_image_subscriptions do - Repo.all(Subscription) + def subscribed?(image, nil), do: false + def subscribed?(image, user) do + Subscription + |> where(image_id: ^image.id, user_id: ^user.id) + |> Repo.exists?() end - @doc """ - Gets a single subscription. - - Raises `Ecto.NoResultsError` if the Subscription does not exist. - - ## Examples - - iex> get_subscription!(123) - %Subscription{} - - iex> get_subscription!(456) - ** (Ecto.NoResultsError) - - """ - def get_subscription!(id), do: Repo.get!(Subscription, id) - @doc """ Creates a subscription. @@ -163,28 +141,10 @@ defmodule Philomena.Images do {:error, %Ecto.Changeset{}} """ - def create_subscription(attrs \\ %{}) do - %Subscription{} - |> Subscription.changeset(attrs) - |> Repo.insert() - end - - @doc """ - Updates a subscription. - - ## Examples - - iex> update_subscription(subscription, %{field: new_value}) - {:ok, %Subscription{}} - - iex> update_subscription(subscription, %{field: bad_value}) - {:error, %Ecto.Changeset{}} - - """ - def update_subscription(%Subscription{} = subscription, attrs) do - subscription - |> Subscription.changeset(attrs) - |> Repo.update() + def create_subscription(image, user) do + %Subscription{image_id: image.id, user_id: user.id} + |> Subscription.changeset(%{}) + |> Repo.insert(on_conflict: :nothing) end @doc """ @@ -199,20 +159,8 @@ defmodule Philomena.Images do {:error, %Ecto.Changeset{}} """ - def delete_subscription(%Subscription{} = subscription) do - Repo.delete(subscription) - end - - @doc """ - Returns an `%Ecto.Changeset{}` for tracking subscription changes. - - ## Examples - - iex> change_subscription(subscription) - %Ecto.Changeset{source: %Subscription{}} - - """ - def change_subscription(%Subscription{} = subscription) do - Subscription.changeset(subscription, %{}) + def delete_subscription(image, user) do + %Subscription{image_id: image.id, user_id: user.id} + |> Repo.delete() end end diff --git a/lib/philomena_web/controllers/image/subscription_controller.ex b/lib/philomena_web/controllers/image/subscription_controller.ex new file mode 100644 index 00000000..5cc33e06 --- /dev/null +++ b/lib/philomena_web/controllers/image/subscription_controller.ex @@ -0,0 +1,35 @@ +defmodule PhilomenaWeb.Image.SubscriptionController do + use PhilomenaWeb, :controller + + alias Philomena.Images.Image + alias Philomena.Images + + plug PhilomenaWeb.Plugs.CanaryMapPlug, create: :show, delete: :show + plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true + + def create(conn, _params) do + image = conn.assigns.image + user = conn.assigns.current_user + + case Images.create_subscription(image, user) do + {:ok, _subscription} -> + render(conn, "_subscription.html", image: image, watching: true, layout: false) + + {:error, _changeset} -> + render(conn, "_error.html", layout: false) + end + end + + def delete(conn, _params) do + image = conn.assigns.image + user = conn.assigns.current_user + + case Images.delete_subscription(image, user) do + {:ok, _subscription} -> + render(conn, "_subscription.html", image: image, watching: false, layout: false) + + {:error, _changeset} -> + render(conn, "_error.html", layout: false) + end + end +end \ No newline at end of file diff --git a/lib/philomena_web/controllers/image_controller.ex b/lib/philomena_web/controllers/image_controller.ex index 75f1bd52..90787481 100644 --- a/lib/philomena_web/controllers/image_controller.ex +++ b/lib/philomena_web/controllers/image_controller.ex @@ -1,7 +1,7 @@ defmodule PhilomenaWeb.ImageController do use PhilomenaWeb, :controller - alias Philomena.{Images.Image, Comments.Comment, Textile.Renderer} + alias Philomena.{Images, Images.Image, Comments.Comment, Textile.Renderer} alias Philomena.Interactions alias Philomena.Comments alias Philomena.Repo @@ -57,6 +57,9 @@ defmodule PhilomenaWeb.ImageController do %Comment{} |> Comments.change_comment() + watching = + Images.subscribed?(image, conn.assigns.current_user) + render( conn, "show.html", @@ -64,7 +67,8 @@ defmodule PhilomenaWeb.ImageController do comments: comments, comment_changeset: comment_changeset, description: description, - interactions: interactions + interactions: interactions, + watching: watching ) end end diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index 140efc1a..8a7b3619 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -58,6 +58,7 @@ defmodule PhilomenaWeb.Router do resources "/vote", Image.VoteController, only: [:create, :delete], singleton: true resources "/fave", Image.FaveController, only: [:create, :delete], singleton: true resources "/hide", Image.HideController, only: [:create, :delete], singleton: true + resources "/subscription", Image.SubscriptionController, only: [:create, :delete], singleton: true end end diff --git a/lib/philomena_web/templates/image/_image_meta.html.slime b/lib/philomena_web/templates/image/_image_meta.html.slime index b8a717a3..cc91a5f2 100644 --- a/lib/philomena_web/templates/image/_image_meta.html.slime +++ b/lib/philomena_web/templates/image/_image_meta.html.slime @@ -30,9 +30,7 @@ span.hide-span title="Hide" i.fa.fa-eye-slash .stretched-mobile-links - a href="/" title="Related Images" - i.fa.fa-sitemap> - span.hide-limited-desktop.hide-mobile Related + = render PhilomenaWeb.Image.SubscriptionView, "_subscription.html", watching: @watching, image: @image, conn: @conn .stretched-mobile-links a href="#{pretty_url(@image, false, false)}" rel="nofollow" title="View (tags in filename)" i.fa.fa-eye> diff --git a/lib/philomena_web/templates/image/show.html.slime b/lib/philomena_web/templates/image/show.html.slime index 5744116a..e57f7bdd 100644 --- a/lib/philomena_web/templates/image/show.html.slime +++ b/lib/philomena_web/templates/image/show.html.slime @@ -1,4 +1,4 @@ -= render PhilomenaWeb.ImageView, "_image_meta.html", image: @image, conn: @conn += render PhilomenaWeb.ImageView, "_image_meta.html", image: @image, watching: @watching, conn: @conn = render PhilomenaWeb.ImageView, "_image_page.html", image: @image, conn: @conn .layout--narrow diff --git a/lib/philomena_web/templates/image/subscription/_error.html.slime b/lib/philomena_web/templates/image/subscription/_error.html.slime new file mode 100644 index 00000000..b0c027de --- /dev/null +++ b/lib/philomena_web/templates/image/subscription/_error.html.slime @@ -0,0 +1,2 @@ +#js-subscription-target + ' Error! \ No newline at end of file diff --git a/lib/philomena_web/templates/image/subscription/_subscription.html.slime b/lib/philomena_web/templates/image/subscription/_subscription.html.slime new file mode 100644 index 00000000..9386e7db --- /dev/null +++ b/lib/philomena_web/templates/image/subscription/_subscription.html.slime @@ -0,0 +1,23 @@ +elixir: + watch_path = Routes.image_subscription_path(@conn, :create, @image) + watch_class = if @watching, do: "hidden", else: "" + + unwatch_path = Routes.image_subscription_path(@conn, :delete, @image) + unwatch_class = if @watching, do: "", else: "hidden" + += if @conn.assigns.current_user do + #js-subscription-target + a.js-subscription-link href=watch_path class=watch_class data-remote="true" data-method="post" + i.fa.fa-bell> + span.hide-mobile + ' Subscribe + + a.js-subscription-link href=unwatch_path class=unwatch_class data-remote="true" data-method="delete" + i.fa.fa-bell-slash> + span.hide-mobile + ' Unsubscribe +- else + a href=Routes.pow_session_path(@conn, :new) + i.fa.fa-bell> + span.hide-mobile + ' Subscribe \ No newline at end of file diff --git a/lib/philomena_web/views/image/subscription_view.ex b/lib/philomena_web/views/image/subscription_view.ex new file mode 100644 index 00000000..ed5da40f --- /dev/null +++ b/lib/philomena_web/views/image/subscription_view.ex @@ -0,0 +1,3 @@ +defmodule PhilomenaWeb.Image.SubscriptionView do + use PhilomenaWeb, :view +end \ No newline at end of file