3 Commits

  1. 13
      CHANGELOG.md
  2. 25
      lib/mirage/notes.ex
  3. 49
      lib/mirage/search.ex
  4. 15
      lib/mirage_web/live/home_live.ex
  5. 4
      lib/mirage_web/live/live_helpers.ex
  6. 22
      lib/mirage_web/live/search_live.ex
  7. 40
      lib/mirage_web/live/search_live.html.leex
  8. 1
      lib/mirage_web/router.ex
  9. 2
      lib/mirage_web/templates/layout/root.html.leex
  10. 15
      lib/mirage_web/templates/topic/show.html.eex
  11. 2
      mix.exs

13
CHANGELOG.md

@ -5,6 +5,19 @@ See [Conventional Commits](Https://conventionalcommits.org) for commit guideline
<!-- changelog -->
## [v0.82.0](https://git.inhji.de/inhji/mirage/compare/v0.81.0...v0.82.0) (2021-03-20)
### Features:
* add search, closes #39, closes #35
### Bug Fixes:
* remove duplicate link on topic show
## [v0.81.0](https://git.inhji.de/inhji/mirage/compare/v0.80.0...v0.81.0) (2021-03-20)

25
lib/mirage/notes.ex

@ -10,31 +10,6 @@ defmodule Mirage.Notes do
@note_preloads [:links, :topics, :backlinks, :list, :images]
defmacro contains(content, search_term) do
quote do
fragment(
"? %> ?",
unquote(content),
unquote(search_term)
)
end
end
def search_notes(query) do
query = String.downcase(query)
q =
from n in Note,
limit: 5,
select: n,
preload: ^@note_preloads,
where: contains(n.content, ^query),
or_where: contains(n.title, ^query),
order_by: [:views]
Repo.all(q)
end
@doc """
Returns the list of notes.

49
lib/mirage/search.ex

@ -0,0 +1,49 @@
defmodule Mirage.Search do
import Ecto.Query, warn: false
alias Mirage.Repo
defmacro contains(content, search_term) do
quote do
fragment(
"LOWER(?) %> LOWER(?)",
unquote(content),
unquote(search_term)
)
end
end
@doc """
Searches notes and links for `search_string`
## Note
type CANNOT be an atom, otherwise it gets assigned wrongly!
"""
def search_notes(search_string) do
search_string = String.downcase(search_string)
link_query =
from l in Mirage.Links.Link,
where: contains(l.title, ^search_string),
or_where: contains(l.url, ^search_string),
or_where: contains(l.content, ^search_string),
select: %{
id: l.id,
title: l.title,
type: "link"
}
note_query =
from n in Mirage.Notes.Note,
where: contains(n.content, ^search_string),
or_where: contains(n.title, ^search_string),
select: %{
id: n.id,
title: n.title,
type: "note"
},
union_all: ^link_query
Repo.all(note_query)
end
end

15
lib/mirage_web/live/home_live.ex

@ -2,16 +2,15 @@ defmodule MirageWeb.HomeLive do
use MirageWeb, :live_view
@impl true
def mount(_params, %{"user_id" => user_id}, socket) do
current_user = user_id && Mirage.Accounts.get_user!(user_id)
def mount(_params, session, socket) do
home_note = Mirage.Notes.get_note_by_title("@home")
settings = Mirage.Settings.list_settings_as_map()
{:ok,
assign(socket, %{
_s: settings,
home_note: home_note,
current_user: current_user
})}
socket
|> assign(%{
home_note: home_note
})
|> with_user(session)
|> with_settings()}
end
end

4
lib/mirage_web/live/live_helpers.ex

@ -28,4 +28,8 @@ defmodule MirageWeb.LiveHelpers do
end
def with_user(socket, _), do: socket |> assign(:current_user, nil)
def with_settings(socket) do
socket |> assign(:_s, Mirage.Settings.list_settings_as_map())
end
end

22
lib/mirage_web/live/search_live.ex

@ -0,0 +1,22 @@
defmodule MirageWeb.SearchLive do
use MirageWeb, :live_view
@impl true
def mount(_params, session, socket) do
{:ok,
socket
|> assign(%{results: []})
|> with_user(session)
|> with_settings()}
end
@impl true
def handle_event("search", %{"key" => _, "value" => query}, socket) do
results = Mirage.Search.search_notes(query)
{:noreply,
socket
|> assign(:results, results)
|> assign(:selection, 0)}
end
end

40
lib/mirage_web/live/search_live.html.leex

@ -0,0 +1,40 @@
<header class="hero">
<h1>Search</h1>
<form action="#">
<input id="query"
type="text"
placeholder="Search for anything..."
autocomplete="off"
phx-keydown="search"
phx-debounce="10">
</form>
<div class="results">
<%= if Enum.empty?(@results) do %>
<div class="card result">
<strong>No results..</strong>
<div>yet</div>
</div>
<% else %>
<%= for {result, index} <- @results |> Enum.with_index() do %>
<%= if result.type == :note do %>
<article>
<%= live_redirect to: Routes.note_show_path(@socket, :show, result.id) do %>
<h2><strong><%= result.title %></strong></h2>
<% end %>
</article>
<% end %>
<%= if result.type == :link do %>
<article>
<%= live_redirect to: Routes.link_show_path(@socket, :show, result.id) do %>
<h2><strong><%= result.title %></strong></h2>
<% end %>
</article>
<% end %>
<% end %>
<% end %>
</div>
</header>

1
lib/mirage_web/router.ex

@ -28,6 +28,7 @@ defmodule MirageWeb.Router do
live "/", HomeLive, :index
live "/blog", BlogLive, :index
live "/search", SearchLive, :index
resources "/topics", TopicController

2
lib/mirage_web/templates/layout/root.html.leex

@ -22,7 +22,7 @@
<header>
<nav role="navigation">
<%= live_patch(@_s.header_title.value, to: "/") %> / <%= live_patch("Blog", to: "/blog") %> / <%= live_patch("Links", to: "/links") %> / <%= live_patch("Topics", to: "/topics") %>
<%= live_patch(@_s.header_title.value, to: "/") %> / <%= live_patch("Blog", to: "/blog") %> / <%= live_patch("Links", to: "/links") %> / <%= live_patch("Search", to: "/search") %>
<%= if not Mirage.Accounts.has_user? do %>
/ <%= live_patch "Register", to: Routes.user_registration_path(@conn, :new) %>
<% end %>

15
lib/mirage_web/templates/topic/show.html.eex

@ -21,21 +21,6 @@
<time><%= link.updated_at |> Timex.from_now() %></time>
</h4>
</article>
<article class="link single" id="link-<%= link.id %>">
<%= live_patch to: Routes.link_show_path(MirageWeb.Endpoint, :show, link) do %>
<header class="width-full">
<h2 class="title">
<%= link.title || link.url %>
</h2>
<p class="tags">
<%= for topic <- link.topics do %>
<span class="tag"><%= topic.text %></span>
<% end %>
</p>
</header>
<% end %>
</article>
<% end %>
</section>

2
mix.exs

@ -1,7 +1,7 @@
defmodule Mirage.MixProject do
use Mix.Project
@version "0.81.0"
@version "0.82.0"
def project do
[

Loading…
Cancel
Save