From a8fef6d03cf25365c87bb654799cd95e7f565008 Mon Sep 17 00:00:00 2001
From: "byte[]" <byteslice@airmail.cc>
Date: Sun, 17 Nov 2019 14:47:01 -0500
Subject: [PATCH] add filter selection

---
 lib/philomena/filters.ex                      | 26 ++++++++++
 lib/philomena/users.ex                        |  6 +++
 lib/philomena/users/user.ex                   | 13 ++++-
 .../controllers/filter/current_controller.ex  |  2 +-
 .../filter/spoiler_type_controller.ex         | 21 +++++++++
 lib/philomena_web/plugs/filter_select_plug.ex | 47 +++++++++++++++++++
 lib/philomena_web/router.ex                   |  5 ++
 .../templates/layout/_header.html.slime       |  7 +++
 8 files changed, 125 insertions(+), 2 deletions(-)
 create mode 100644 lib/philomena_web/controllers/filter/spoiler_type_controller.ex
 create mode 100644 lib/philomena_web/plugs/filter_select_plug.ex

diff --git a/lib/philomena/filters.ex b/lib/philomena/filters.ex
index 81ff15ca..70affae8 100644
--- a/lib/philomena/filters.ex
+++ b/lib/philomena/filters.ex
@@ -116,4 +116,30 @@ defmodule Philomena.Filters do
   def change_filter(%Filter{} = filter) do
     Filter.changeset(filter, %{})
   end
+
+  def recent_and_user_filters(user) do
+    user_filters = 
+      Filter
+      |> select([f], %{id: f.id, name: f.name, recent: ^"f"})
+      |> where(user_id: ^user.id)
+      |> limit(10)
+
+    recent_filters =
+      Filter
+      |> select([f], %{id: f.id, name: f.name, recent: ^"t"})
+      |> where([f], f.id in ^user.recent_filter_ids)
+      |> limit(10)
+
+    union(recent_filters, ^user_filters)
+    |> Repo.all()
+    |> Enum.group_by(
+      fn
+        %{recent: "t"}  -> "Recent Filters"
+        _user           -> "Your Filters"
+      end,
+      fn %{id: id, name: name} ->
+        [key: name, value: id]
+      end
+    )
+  end
 end
diff --git a/lib/philomena/users.ex b/lib/philomena/users.ex
index c2dca60e..6288171a 100644
--- a/lib/philomena/users.ex
+++ b/lib/philomena/users.ex
@@ -77,6 +77,12 @@ defmodule Philomena.Users do
     |> Repo.update()
   end
 
+  def update_spoiler_type(%User{} = user, attrs) do
+    user
+    |> User.spoiler_type_changeset(attrs)
+    |> Repo.update()
+  end
+
   @doc """
   Returns an `%Ecto.Changeset{}` for tracking user changes.
 
diff --git a/lib/philomena/users/user.ex b/lib/philomena/users/user.ex
index df2e77f4..c0672aed 100644
--- a/lib/philomena/users/user.ex
+++ b/lib/philomena/users/user.ex
@@ -143,8 +143,19 @@ defmodule Philomena.Users.User do
   end
 
   def filter_changeset(user, filter) do
-    change(user)
+    changeset = change(user)
+    user = changeset.data
+
+    changeset
     |> put_change(:current_filter_id, filter.id)
+    |> put_change(:recent_filter_ids, Enum.take([filter.id | user.recent_filter_ids], 10))
+  end
+
+  def spoiler_type_changeset(user, attrs) do
+    user
+    |> cast(attrs, [:spoiler_type])
+    |> validate_required([:spoiler_type])
+    |> validate_inclusion(:spoiler_type, ~W(static click hover off))
   end
 
   def create_totp_secret_changeset(user) do
diff --git a/lib/philomena_web/controllers/filter/current_controller.ex b/lib/philomena_web/controllers/filter/current_controller.ex
index 45acee2e..646e9161 100644
--- a/lib/philomena_web/controllers/filter/current_controller.ex
+++ b/lib/philomena_web/controllers/filter/current_controller.ex
@@ -20,7 +20,7 @@ defmodule PhilomenaWeb.Filter.CurrentController do
     conn
     |> update_filter(user, filter)
     |> put_flash(:info, "Switched to filter #{filter.name}")
-    |> redirect(to: Routes.filter_path(conn, :index))
+    |> redirect(external: conn.assigns.referrer)
   end
 
   defp update_filter(conn, nil, filter) do
diff --git a/lib/philomena_web/controllers/filter/spoiler_type_controller.ex b/lib/philomena_web/controllers/filter/spoiler_type_controller.ex
new file mode 100644
index 00000000..b3d0f9e5
--- /dev/null
+++ b/lib/philomena_web/controllers/filter/spoiler_type_controller.ex
@@ -0,0 +1,21 @@
+defmodule PhilomenaWeb.Filter.SpoilerTypeController do
+  use PhilomenaWeb, :controller
+
+  alias Philomena.Users
+
+  plug PhilomenaWeb.RequireUserPlug
+
+  def update(conn, %{"user" => user_params}) do
+    case Users.update_spoiler_type(conn.assigns.current_user, user_params) do
+      {:ok, user} ->
+        conn
+        |> put_flash(:info, "Changed spoiler type to #{user.spoiler_type}")
+        |> redirect(external: conn.assigns.referrer)
+
+      {:error, _changeset} ->
+        conn
+        |> put_flash(:error, "Failed to set spoiler type")
+        |> redirect(external: conn.assigns.referrer)
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/philomena_web/plugs/filter_select_plug.ex b/lib/philomena_web/plugs/filter_select_plug.ex
new file mode 100644
index 00000000..64471837
--- /dev/null
+++ b/lib/philomena_web/plugs/filter_select_plug.ex
@@ -0,0 +1,47 @@
+defmodule PhilomenaWeb.FilterSelectPlug do
+  @moduledoc """
+  This plug sets up the filter menu for the layout if there is a
+  user currently signed in.
+
+  ## Example
+
+      plug PhilomenaWeb.FilterSelectPlug
+  """
+
+  alias Philomena.Filters
+  alias Philomena.Users
+  alias Plug.Conn
+
+  @spoiler_types %{
+    "Spoilers" => [
+      static: "static",
+      click: "click",
+      hover: "hover",
+      off: "off"
+    ]
+  }
+
+  @doc false
+  @spec init(any()) :: any()
+  def init(opts), do: opts
+
+  @doc false
+  @spec call(Conn.t(), any()) :: Conn.t()
+  def call(conn, _opts) do
+    user = Pow.Plug.current_user(conn)
+
+    conn
+    |> maybe_assign_filters(user)
+  end
+
+  defp maybe_assign_filters(conn, nil), do: conn
+  defp maybe_assign_filters(conn, user) do
+    filters = Filters.recent_and_user_filters(user)
+    user = Users.change_user(user)
+
+    conn
+    |> Conn.assign(:user_changeset, user)
+    |> Conn.assign(:available_filters, filters)
+    |> Conn.assign(:spoiler_types, @spoiler_types)
+  end
+end
\ No newline at end of file
diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex
index e1c181a9..0faaf97b 100644
--- a/lib/philomena_web/router.ex
+++ b/lib/philomena_web/router.ex
@@ -15,6 +15,7 @@ defmodule PhilomenaWeb.Router do
     plug PhilomenaWeb.EnsureUserEnabledPlug
     plug PhilomenaWeb.CurrentBanPlug
     plug PhilomenaWeb.NotificationCountPlug
+    plug PhilomenaWeb.FilterSelectPlug
   end
 
   pipeline :api do
@@ -67,6 +68,10 @@ defmodule PhilomenaWeb.Router do
         resources "/subscription", Topic.SubscriptionController, only: [:create, :delete], singleton: true
       end
     end
+
+    scope "/filters", Filter, as: :filter do
+      resources "/spoiler_type", SpoilerTypeController, only: [:update], singleton: true
+    end
   end
 
   scope "/", PhilomenaWeb do
diff --git a/lib/philomena_web/templates/layout/_header.html.slime b/lib/philomena_web/templates/layout/_header.html.slime
index 2f2197f4..16540fe1 100644
--- a/lib/philomena_web/templates/layout/_header.html.slime
+++ b/lib/philomena_web/templates/layout/_header.html.slime
@@ -43,6 +43,13 @@ header.header
         a.header__link.hide-mobile href="/filters" title="Filters"
           i.fa.fa-filter
           span.hide-limited-desktop< Filters
+
+        = form_for @conn.assigns.user_changeset, Routes.filter_current_path(@conn, :update), [class: "header__filter-form", id: "filter-quick-form"], fn f ->
+          = select f, :current_filter_id, @conn.assigns.available_filters, name: "id", id: "filter-quick-menu", class: "input header__input", data: [change_submit: "#filter-quick-form"], autocomplete: "off"
+
+        = form_for @conn.assigns.user_changeset, Routes.filter_spoiler_type_path(@conn, :update), [class: "header__filter-form hide-mobile hide-limited-desktop", id: "spoiler-quick-form"], fn f ->
+          = select f, :spoiler_type, @conn.assigns.spoiler_types, id: "spoiler-quick-menu", class: "input header__input", data: [change_submit: "#spoiler-quick-form"], autocomplete: "off"
+
         .dropdown.header__dropdown
           a.header__link.header__link-user href="/"
             = render PhilomenaWeb.UserAttributionView, "_user_avatar.html", object: %{user: @current_user}, class: "avatar--28px"