diff --git a/lib/memex/notes.ex b/lib/memex/notes.ex index 8a3602d..a44bea7 100644 --- a/lib/memex/notes.ex +++ b/lib/memex/notes.ex @@ -17,10 +17,34 @@ defmodule Memex.Notes do """ @spec list_notes(User.t() | nil) :: [Note.t()] - def list_notes(%{id: user_id}) do + @spec list_notes(search :: String.t() | nil, User.t() | nil) :: [Note.t()] + def list_notes(search \\ nil, user) + + def list_notes(search, %{id: user_id}) when search |> is_nil() or search == "" do Repo.all(from n in Note, where: n.user_id == ^user_id, order_by: n.title) end + def list_notes(search, %{id: user_id}) when search |> is_binary() do + trimmed_search = String.trim(search) + + Repo.all( + from n in Note, + where: n.user_id == ^user_id, + where: + fragment( + "search @@ to_tsquery(websearch_to_tsquery(?)::text || ':*')", + ^trimmed_search + ), + order_by: { + :desc, + fragment( + "ts_rank_cd(search, to_tsquery(websearch_to_tsquery(?)::text || ':*'), 4)", + ^trimmed_search + ) + } + ) + end + @doc """ Returns the list of public notes for viewing @@ -30,10 +54,34 @@ defmodule Memex.Notes do [%Note{}, ...] """ @spec list_public_notes() :: [Note.t()] - def list_public_notes do + @spec list_public_notes(search :: String.t() | nil) :: [Note.t()] + def list_public_notes(search \\ nil) + + def list_public_notes(search) when search |> is_nil() or search == "" do Repo.all(from n in Note, where: n.visibility == :public, order_by: n.title) end + def list_public_notes(search) when search |> is_binary() do + trimmed_search = String.trim(search) + + Repo.all( + from n in Note, + where: n.visibility == :public, + where: + fragment( + "search @@ to_tsquery(websearch_to_tsquery(?)::text || ':*')", + ^trimmed_search + ), + order_by: { + :desc, + fragment( + "ts_rank_cd(search, to_tsquery(websearch_to_tsquery(?)::text || ':*'), 4)", + ^trimmed_search + ) + } + ) + end + @doc """ Gets a single note. diff --git a/lib/memex_web/live/note_live/index.ex b/lib/memex_web/live/note_live/index.ex index 706f804..4209771 100644 --- a/lib/memex_web/live/note_live/index.ex +++ b/lib/memex_web/live/note_live/index.ex @@ -3,13 +3,8 @@ defmodule MemexWeb.NoteLive.Index do alias Memex.{Notes, Notes.Note} @impl true - def mount(_params, _session, %{assigns: %{current_user: current_user}} = socket) - when not (current_user |> is_nil()) do - {:ok, socket |> assign(notes: Notes.list_notes(current_user))} - end - - def mount(_params, _session, socket) do - {:ok, socket |> assign(notes: Notes.list_public_notes())} + def mount(params, _session, socket) do + {:ok, socket |> assign(search: nil) |> display_notes()} end @impl true @@ -34,7 +29,17 @@ defmodule MemexWeb.NoteLive.Index do defp apply_action(socket, :index, _params) do socket |> assign(page_title: "notes") + |> assign(search: nil) |> assign(note: nil) + |> display_notes() + end + + defp apply_action(socket, :search, %{"search" => search}) do + socket + |> assign(page_title: "notes") + |> assign(search: search) + |> assign(note: nil) + |> display_notes() end @impl true @@ -49,4 +54,22 @@ defmodule MemexWeb.NoteLive.Index do {:noreply, socket} end + + @impl true + def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do + {:noreply, socket |> push_patch(to: Routes.note_index_path(Endpoint, :index))} + end + + def handle_event("search", %{"search" => %{"search_term" => search_term}}, socket) do + {:noreply, socket |> push_patch(to: Routes.note_index_path(Endpoint, :search, search_term))} + end + + defp display_notes(%{assigns: %{current_user: current_user, search: search}} = socket) + when not (current_user |> is_nil()) do + socket |> assign(notes: Notes.list_notes(search, current_user)) + end + + defp display_notes(%{assigns: %{search: search}} = socket) do + socket |> assign(notes: Notes.list_public_notes(search)) + end end diff --git a/lib/memex_web/live/note_live/index.html.heex b/lib/memex_web/live/note_live/index.html.heex index ceb95e2..66aca4f 100644 --- a/lib/memex_web/live/note_live/index.html.heex +++ b/lib/memex_web/live/note_live/index.html.heex @@ -3,6 +3,17 @@ <%= gettext("notes") %> + <.form + :let={f} + for={:search} + phx-change="search" + phx-submit="search" + phx-debounce="500" + class="self-stretch flex flex-col items-stretch" + > + <%= text_input(f, :search_term, class: "input input-primary") %> + + <%= if @notes |> Enum.empty?() do %>