Skip to content

Commit

Permalink
Implement landing page search
Browse files Browse the repository at this point in the history
  • Loading branch information
tomkonidas committed Aug 2, 2024
1 parent cd726be commit 329632a
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 21 deletions.
22 changes: 22 additions & 0 deletions lib/plexus/apps.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ defmodule Plexus.Apps do
App
|> with_scores(opts)
|> QueryHelpers.merge_opts(opts)
|> filter(opts)
|> Repo.paginate(page_opts)
end

Expand Down Expand Up @@ -120,6 +121,27 @@ defmodule Plexus.Apps do
})
end

defp filter(query, opts) do
Enum.reduce(opts, query, fn
{_, ""}, query ->
query

{:search_term, search_term}, query ->
pattern = "%#{search_term}%"

from q in query,
where:
fragment("SIMILARITY(?, ?) > .30", q.name, ^search_term) or
ilike(q.name, ^pattern) or
fragment("SIMILARITY(?, ?) > .30", q.package, ^search_term) or
ilike(q.package, ^pattern),
order_by: fragment("LEVENSHTEIN(?, ?)", q.name, ^search_term)

_, query ->
query
end)
end

@spec subscribe :: :ok
def subscribe do
Phoenix.PubSub.subscribe(Plexus.PubSub, "apps")
Expand Down
2 changes: 1 addition & 1 deletion lib/plexus_web/components/core_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ defmodule PlexusWeb.CoreComponents do
def simple_form(assigns) do
~H"""
<.form :let={f} for={@for} as={@as} {@rest}>
<div class="mt-10 space-y-8 bg-white">
<div class="mt-10 space-y-8">
<%= render_slot(@inner_block, f) %>
<div :for={action <- @actions} class="mt-2 flex items-center justify-between gap-6">
<%= render_slot(action, f) %>
Expand Down
4 changes: 2 additions & 2 deletions lib/plexus_web/components/layouts/app.html.heex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<header class="px-4 sm:px-6 lg:px-8">
<header class="bg-white px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between border-b border-zinc-100 py-3 text-sm">
<div class="flex items-center gap-4">
<a href="/" class="flex items-center">
Expand All @@ -20,7 +20,7 @@
</div>
</div>
</header>
<main class="px-4 py-20 sm:px-6 lg:px-8">
<main class="bg-slate-100 px-4 py-20 sm:px-6 lg:px-8">
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<.flash_group flash={@flash} />
<%= @inner_content %>
Expand Down
2 changes: 1 addition & 1 deletion lib/plexus_web/components/layouts/root.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
</script>
</head>
<body class="bg-white antialiased">
<body class="bg-slate-100 antialiased">
<%= @inner_content %>
</body>
</html>
60 changes: 46 additions & 14 deletions lib/plexus_web/live/app_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,75 @@ defmodule PlexusWeb.AppLive.Index do
def mount(_params, _session, socket) do
{:ok,
socket
|> assign(:page_title, "Crowdsourced de-Googled Android apps status ratings")
|> assign(:page, 1)
|> stream_configure(:apps, dom_id: & &1.package)
|> paginate_apps(1)}
|> assign(:form, to_form(changeset(), as: :form))
|> assign(:no_results?, false)
|> assign(:end_of_timeline?, false)
|> stream_configure(:apps, dom_id: & &1.package)}
end

defp changeset(params \\ %{}) do
types = %{search: :string}
data = %{}
Ecto.Changeset.cast({data, types}, params, Map.keys(types))
end

defp paginate_apps(socket, new_page) when new_page >= 1 do
%Scrivener.Page{
total_entries: total_entries,
total_pages: total_pages,
entries: apps
} = Apps.list_apps(page: new_page, scores: true, order_by: :name)
} =
Apps.list_apps(
search_term: socket.assigns.search_term,
page: new_page,
scores: true,
order_by: :name,
page_size: 50
)

case apps do
[] ->
case {apps, new_page} do
{[], page} when page != 1 ->
assign(socket, end_of_timeline?: total_pages == new_page)

[_ | _] = apps ->
{apps, _} ->
opts = if new_page == 1, do: [reset: true], else: []
end_of_timeline? = new_page >= total_pages

socket
|> assign(end_of_timeline?: false)
|> assign(end_of_timeline?: end_of_timeline?)
|> assign(no_results?: apps == [])
|> assign(:page, new_page)
|> assign(:total_entries, total_entries)
|> stream(:apps, apps)
|> stream(:apps, apps, opts)
end
end

@impl Phoenix.LiveView
def handle_params(params, _url, socket) do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
{:noreply,
socket
|> assign(:search_term, params["q"])
|> assign(:form, to_form(changeset(%{search: params["q"]}), as: :form))
|> paginate_apps(1)}
end

defp apply_action(socket, :index, _params) do
socket
|> assign(:page_title, "Crowdsourced de-Googled Android apps status ratings")
|> assign(:app, nil)
@impl Phoenix.LiveView
def handle_event("search", %{"form" => form}, socket) do
params =
form
|> Map.get("search", "")
|> String.trim()
|> case do
"" -> %{}
"*" -> %{}
term -> %{q: term}
end

{:noreply, push_patch(socket, to: ~p"/?#{params}")}
end

@impl Phoenix.LiveView
def handle_event("next-page", _, socket) do
{:noreply, paginate_apps(socket, socket.assigns.page + 1)}
end
Expand Down
16 changes: 13 additions & 3 deletions lib/plexus_web/live/app_live/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@
Apps (<%= @total_entries %> entries)
</.header>

<.simple_form for={@form} id="search-form" phx-change="search" phx-submit="search">
<.focus_wrap id="focus-first-search">
<.input field={@form[:search]} label="Search" phx-debounce="300" />
</.focus_wrap>
</.simple_form>

<ul
id="apps"
phx-update="stream"
phx-viewport-top={@page > 1 && "prev-page"}
phx-viewport-bottom={!@end_of_timeline? && "next-page"}
phx-page-loading
class={[
"mt-3 grid grid-cols-1 gap-4 sm:gap-5 sm:grid-cols-2 lg:grid-cols-3",
"mt-1 grid grid-cols-1 gap-4 sm:gap-5 sm:grid-cols-2 lg:grid-cols-3",
if(@end_of_timeline?, do: "pb-10", else: "pb-[calc(200vh)]"),
if(@page == 1, do: "pt-10", else: "pt-[calc(200vh)]")
]}
Expand All @@ -19,6 +25,10 @@
</li>
</ul>

<div :if={@end_of_timeline?} class="mt-5 text-[50px] text-center">
🎉 You made it to the end of the list 🎉
<div :if={@no_results?} class="mt-5 text-[50px] text-center">
No apps found<br />😭
</div>

<div :if={@end_of_timeline? and not @no_results?} class="mt-5 text-[50px] text-center">
End of list<br />🤭
</div>

0 comments on commit 329632a

Please sign in to comment.