Search v1 (#19)

* Adds description to media items; hooks it up to indexing/media downloading

* Added a search method using postgres fulltext search

* Hooked up search functionality to the search form

* Added persistence to the search form when on search page
This commit is contained in:
Kieran
2024-02-13 17:46:14 -08:00
committed by GitHub
parent 850cf2db73
commit 58771ee37b
25 changed files with 261 additions and 25 deletions
@@ -2,7 +2,7 @@
<.sidebar />
<div class="relative flex flex-1 flex-col overflow-y-auto overflow-x-hidden">
<.header />
<.header params={@conn.params} />
<main>
<div class="mx-auto max-w-screen-2xl p-4 md:p-6 2xl:p-10">
<.flash_group flash={@flash} />
@@ -1,5 +1,5 @@
<header class="sticky top-0 z-999 flex w-full bg-white drop-shadow-1 dark:bg-boxdark dark:drop-shadow-none">
<div class="flex flex-grow items-center justify-end px-4 py-4 shadow-2 md:px-6 2xl:px-11">
<div class="flex flex-grow items-center justify-between lg:justify-end px-4 py-4 shadow-2 md:px-6 2xl:px-11">
<div class="flex items-center gap-2 sm:gap-4 lg:hidden">
<button
class="z-99999 block rounded-sm border border-stroke bg-white p-1.5 shadow-sm dark:border-strokedark dark:bg-boxdark lg:hidden"
@@ -7,22 +7,24 @@
>
<.icon name="hero-bars-3" />
</button>
<a class="block flex-shrink-0 lg:hidden" href="#">
<a class="hidden sm:block flex-shrink-0 lg:hidden" href="#">
<h2 class="text-title-md2 font-bold text-white">Pinchflat</h2>
</a>
</div>
<div class="hidden sm:block bg-meta-4 rounded-md">
<%!-- Aspirational (for now) --%>
<div class="bg-meta-4 rounded-md">
<div class="relative">
<span class="absolute left-2 top-1/2 -translate-y-1/2 flex">
<.icon name="hero-magnifying-glass" />
</span>
<input
type="text"
placeholder="Type to search..."
class="w-full bg-transparent pl-9 pr-4 border-0 focus:ring-0 focus:outline-none xl:w-125"
/>
<form action={~p"/search"}>
<input
type="text"
name="q"
value={@params["q"]}
placeholder="Type to search..."
class="w-full bg-transparent pl-9 pr-4 border-0 focus:ring-0 focus:outline-none xl:w-125"
/>
</form>
</div>
</div>
</div>
@@ -16,7 +16,7 @@
</.link>
</nav>
</div>
<div class="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
<div class="rounded-sm border border-stroke bg-white px-5 py-5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5">
<div class="max-w-full overflow-x-auto">
<div class="flex flex-col gap-10 dark:text-white">
<h3 class="mt-14 font-bold text-xl">Relationships</h3>
@@ -35,10 +35,10 @@
<.list_items_from_map map={Map.from_struct(@source)} />
<h3 class="font-bold text-xl">Downloaded Media</h3>
<%= if length(@downloaded_media) > 0 do %>
<%= if match?([_|_], @downloaded_media) do %>
<.table rows={@downloaded_media} table_class="text-black dark:text-white">
<:col :let={media_item} label="Title">
<%= String.slice(media_item.title, 0..50) %>...
<%= StringUtils.truncate(media_item.title, 50) %>
</:col>
<:col :let={media_item} label="" class="flex place-content-evenly">
<.link
@@ -54,10 +54,10 @@
<% end %>
<h3 class="font-bold text-xl">Pending Media</h3>
<%= if length(@pending_media) > 0 do %>
<%= if match?([_|_], @pending_media) do %>
<.table rows={@pending_media} table_class="text-black dark:text-white">
<:col :let={media_item} label="Title">
<%= String.slice(media_item.title, 0..50) %>...
<%= StringUtils.truncate(media_item.title, 50) %>
</:col>
<:col :let={media_item} label="" class="flex place-content-evenly">
<.link
@@ -0,0 +1,12 @@
defmodule PinchflatWeb.Searches.SearchController do
use PinchflatWeb, :controller
alias Pinchflat.Media
def show(conn, params) do
search_term = Map.get(params, "q", "")
search_results = Media.search(search_term)
render(conn, :show, search_term: search_term, search_results: search_results)
end
end
@@ -0,0 +1,25 @@
defmodule PinchflatWeb.Searches.SearchHTML do
use PinchflatWeb, :html
embed_templates "search_html/*"
@doc """
Highlight search terms in a string of text based on `[PF_HIGHLIGHT]` and `[/PF_HIGHLIGHT]` tags
"""
attr :text, :string, required: true
def highlight_search_terms(assigns) do
split_string = String.split(assigns.text, ~r{\[PF_HIGHLIGHT\]|\[/PF_HIGHLIGHT\]}, include_captures: true)
assigns = assign(assigns, split_string: split_string)
~H"""
<%= for fragment <- @split_string do %>
<%= render_fragment(fragment) %>
<% end %>
"""
end
defp render_fragment("[PF_HIGHLIGHT]"), do: raw(~s(<span class="font-bold italic">))
defp render_fragment("[/PF_HIGHLIGHT]"), do: raw("</span>")
defp render_fragment(text), do: text
end
@@ -0,0 +1,29 @@
<div class="mb-6 flex gap-3 flex-row items-center justify-between">
<h2 class="text-title-md2 font-bold text-black dark:text-white">
<span class="hidden sm:inline">Search </span>Results for "<%= StringUtils.truncate(@search_term, 50) %>"
</h2>
</div>
<div class="rounded-sm border border-stroke bg-white px-5 py-5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5">
<div class="max-w-full overflow-x-auto">
<div class="flex flex-col gap-10 dark:text-white">
<%= if match?([_|_], @search_results) do %>
<.table rows={@search_results} table_class="text-black dark:text-white">
<:col :let={result} label="Title">
<%= StringUtils.truncate(result.title, 40) %>
</:col>
<:col :let={result} label="Excerpt">
<.highlight_search_terms text={result.matching_search_term} />
</:col>
<:col :let={result} label="" class="flex place-content-evenly">
<.link navigate={~p"/media/#{result.id}"} class="hover:text-secondary duration-200 ease-in-out mx-0.5">
<.icon name="hero-eye" />
</.link>
</:col>
</.table>
<% else %>
<p class="font-bold text-lg text-center text-black dark:text-white">No results found</p>
<% end %>
</div>
</div>
</div>
+1
View File
@@ -21,6 +21,7 @@ defmodule PinchflatWeb.Router do
resources "/media_profiles", MediaProfiles.MediaProfileController
resources "/media", Media.MediaItemController, only: [:show]
resources "/search", Searches.SearchController, only: [:show], singleton: true
resources "/sources", MediaSources.SourceController do
resources "/media", Media.MediaItemController, only: [:show]