numerous fixes, blog index, paging, tag index, post show - all liveview

This commit is contained in:
Adam Piontek 2021-04-05 21:40:43 -04:00
parent 2218a678b1
commit 27a8c22e9f
10 changed files with 178 additions and 177 deletions

View file

@ -47,12 +47,21 @@ body {
height: 100%; height: 100%;
} }
a { a {
color: $secondary; color: $gray-100;
border-bottom: $secondary 2px solid;
text-decoration: none;
&.navbar-brand {
border-bottom: none;
}
&:visited { &:visited {
color: $info; color: $info;
.post-title & {
color: $gray-100;
}
} }
&:hover { &:hover {
color: $primary; color: $primary;
text-decoration: none;
} }
} }
.border-gray-900 { .border-gray-900 {
@ -94,6 +103,7 @@ a {
/* social icons */ /* social icons */
#social-icons .link-light { #social-icons .link-light {
border-bottom: none;
color: $gray-100; color: $gray-100;
&:hover { &:hover {
color: $primary; color: $primary;
@ -128,9 +138,10 @@ a {
.post-title a { .post-title a {
color: $gray-100; color: $gray-100;
text-decoration: none; text-decoration: none;
border-bottom: none;
&:hover { &:hover {
color: $primary; color: $primary;
text-decoration: underline; border-bottom: $secondary 3px solid;
} }
} }
.post-lede, .post-lede,

View file

@ -3,21 +3,29 @@ defmodule Home73k.Blog do
Application.ensure_all_started(:earmark) Application.ensure_all_started(:earmark)
posts_paths = "#{Home73k.app_blog_content()}/**/*.md" |> Path.wildcard() post_paths = "#{Home73k.app_blog_content()}/**/*.md" |> Path.wildcard()
post_paths_hash = :erlang.md5(post_paths)
posts = posts =
for post_path <- posts_paths do for post_path <- post_paths do
@external_resource Path.relative_to_cwd(post_path) @external_resource Path.relative_to_cwd(post_path)
Post.parse!(post_path) Post.parse!(post_path)
end end
def __mix_recompile__?() do
Path.wildcard("#{Home73k.app_blog_content()}/**/*.md") |> :erlang.md5() != unquote(post_paths_hash)
end
@posts Enum.sort_by(posts, & &1.date, {:desc, Date}) @posts Enum.sort_by(posts, & &1.date, {:desc, Date})
@post_count length(@posts)
@tags posts |> Stream.flat_map(& &1.tags) |> Stream.uniq() |> Enum.sort() @tags posts |> Stream.flat_map(& &1.tags) |> Stream.uniq() |> Enum.sort()
def list_posts, do: @posts def list_posts, do: @posts
def list_tags, do: @tags def list_tags, do: @tags
def post_count, do: @post_count
defmodule NotFoundError do defmodule NotFoundError do
defexception [:message, plug_status: 404] defexception [:message, plug_status: 404]
end end
@ -29,10 +37,10 @@ defmodule Home73k.Blog do
end end
end end
# def get_posts_by_tag!(tag) do def get_posts_by_tag!(tag) do
# case Enum.filter(list_posts(), &(tag in &1.tags)) do case Enum.filter(list_posts(), &(tag in &1.tags)) do
# [] -> raise NotFoundError, "posts with tag=#{tag} not found" [] -> raise NotFoundError, "posts with tag=#{tag} not found"
# posts -> posts posts -> posts
# end end
# end end
end end

View file

@ -93,9 +93,6 @@ defmodule Home73kWeb do
# Import SVG Icon helper # Import SVG Icon helper
import Home73kWeb.IconHelpers import Home73kWeb.IconHelpers
# Import Date formatter helper
import Home73kWeb.DateHelpers
import Home73kWeb.ErrorHelpers import Home73kWeb.ErrorHelpers
import Home73kWeb.Gettext import Home73kWeb.Gettext
alias Home73kWeb.Router.Helpers, as: Routes alias Home73kWeb.Router.Helpers, as: Routes

View file

@ -3,49 +3,96 @@ defmodule Home73kWeb.BlogLive do
alias Home73k.Blog alias Home73k.Blog
@page_size 7
@impl true @impl true
def mount(_params, _session, socket) do def mount(_params, _session, socket) do
socket {:ok, socket}
|> live_okreply()
end end
@impl true @impl true
def handle_params(_params, _url, socket) do def handle_params(params, _url, socket) do
socket socket.assigns.live_action
|> assign(:page_title, "Blog") |> init_per_live_action(socket, params)
|> assign(:posts, Blog.list_posts())
|> live_noreply() |> live_noreply()
end end
# @impl true defp page_param_as_int(page) do
# def handle_event("suggest", %{"q" => query}, socket) do try do
# {:noreply, assign(socket, results: search(query), query: query)} String.to_integer(page)
# end rescue
_ -> nil
# @impl true end
# def handle_event("search", %{"q" => query}, socket) do end
# case search(query) do
# %{^query => vsn} -> defp raise_not_found(msg), do: raise Home73k.Blog.NotFoundError, msg
# {:noreply, redirect(socket, external: "https://hexdocs.pm/#{query}/#{vsn}")}
defp init_per_live_action(:index, socket, _params) do
# _ -> socket
# {:noreply, |> assign(:page_title, "Blog")
# socket |> assign(:posts, get_posts_for_page!(1))
# |> put_flash(:error, "No dependencies found matching \"#{query}\"") |> assign(:page_count, get_page_count())
# |> assign(results: %{}, query: query)} |> assign_prev_next(1)
# end end
# end
defp init_per_live_action(:page, socket, %{"page" => page}) do
# defp search(query) do page_int = page_param_as_int(page)
# if not Home73kWeb.Endpoint.config(:code_reloader) do page_count = get_page_count()
# raise "action disabled when not in development"
# end cond do
is_nil(page_int) || page_int <= 1 ->
# for {app, desc, vsn} <- Application.started_applications(), push_patch(socket, to: Routes.blog_path(socket, :index))
# app = to_string(app),
# String.starts_with?(app, query) and not List.starts_with?(desc, ~c"ERTS"), page_int > page_count ->
# into: %{}, raise_not_found("there are only #{page_count} pages of posts")
# do: {app, vsn}
# end true ->
posts = get_posts_for_page!(page_int)
socket
|> assign(:page_title, "Blog \\ Page #{page}")
|> assign(:posts, posts)
|> assign(:page_count, page_count)
|> assign_prev_next(page_int)
end
end
defp init_per_live_action(:show, socket, %{"id" => id}) do
post = Blog.get_post_by_id!(id)
socket
|> assign(:page_title, "Blog \\ post.title")
|> assign(:posts, [post])
|> assign(:page_count, nil)
|> assign_prev_next(0)
end
defp init_per_live_action(:tag, socket, %{"tag" => tag}) do
socket
|> assign(:page_title, "Blog \\ ##{tag}")
|> assign(:posts, Blog.get_posts_by_tag!(tag))
|> assign(:page_count, get_page_count())
|> assign_prev_next(1)
end
defp get_posts_for_page!(1), do: Blog.list_posts() |> Enum.take(@page_size)
defp get_posts_for_page!(page_int) do
Blog.list_posts()
|> Stream.chunk_every(@page_size)
|> Enum.at(page_int - 1)
end
defp get_page_count, do: Integer.floor_div(Blog.post_count(), @page_size) + rem(Blog.post_count(), @page_size)
defp assign_prev_next(socket, page_int) do
socket
|> assign(:page_prev, page_int < socket.assigns.page_count && page_int + 1 || nil)
|> assign(:page_next, page_int > 1 && page_int - 1 || nil)
end
def format_date(date) do
Calendar.strftime(date, "%B %-d, %Y")
end
end end

View file

@ -2,24 +2,34 @@
<div class="col-12 col-md-10 col-lg-9 col-xl-8 col-xxl-7 pb-2 mb-4 mt-3"> <div class="col-12 col-md-10 col-lg-9 col-xl-8 col-xxl-7 pb-2 mb-4 mt-3">
<%= if is_nil(@posts) do %>
<div class="post border-bottom border-gray pb-4 mb-3">
<h2 class="post-title fs-2 fw-600 mb-2">Nothing found.</h2>
</div>
<% else %>
<%= for post <- @posts do %> <%= for post <- @posts do %>
<div class="post border-bottom border-gray pb-4 mb-3"> <div class="post border-bottom border-gray pb-4 mb-3">
<h2 class="post-title fs-2 fw-600 mb-2"> <h2 class="post-title fs-2 fw-600 mb-2">
<%= live_redirect "#{post.title}", to: Routes.post_path(@socket, :show, post) %> <%= live_redirect "#{post.title}", to: Routes.blog_path(@socket, :show, post) %>
</h2> </h2>
<div class="post-date font-monospace text-gray-400 <%= if length(post.tags) == 0, do: "mb-3" %>"> <div class="post-date font-monospace text-gray-400 <%= if length(post.tags) == 0, do: "mb-3" %>">
<%= icon_div @socket, "mdi-calendar-clock", [class: "icon baseline me-2"] %><%= post.date |> format_date() %> <%= icon_div @socket, "mdi-calendar-clock", [class: "icon baseline me-2"] %><%= format_date(post.date) %>
by <%= icon_div @socket, "mdi-account", [class: "icon baseline me-1"] %>Adam Piontek by <%= icon_div @socket, "mdi-account", [class: "icon baseline me-1"] %>Adam Piontek
</div> </div>
<%= if length(post.tags) > 0 do %> <%= if length(post.tags) > 0 do %>
<div class="post-tags fs-smaller mb-3"> <div class="post-tags fs-smaller mb-3">
<%= icon_div @socket, "mdi-tag-multiple", [class: "icon baseline"] %> <%= icon_div @socket, "mdi-tag-multiple", [class: "icon baseline text-gray-400"] %>
<%= for {tag, i} <- Enum.with_index(post.tags) do %> <%= for {tag, i} <- Enum.with_index(post.tags) do %>
#<%= tag %><%= i < (length(post.tags) - 1) && "," || "" %> <span class="text-gray-400">#</span><%= live_redirect tag, to: Routes.blog_path(@socket, :tag, tag) %><%= i < (length(post.tags) - 1) && "," || "" %>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
@ -28,14 +38,40 @@
<%= raw post.lede %> <%= raw post.lede %>
</div> </div>
<%= if @live_action == :show do %>
<div class="post-body">
<%= raw post.body %>
</div>
<% else %>
<p> <p>
<%= live_redirect raw("Read more&hellip;"), to: Routes.post_path(@socket, :show, post), class: "fs-6" %> <%= live_redirect raw("Read more&hellip;"), to: Routes.blog_path(@socket, :show, post), class: "fs-6" %>
</p> </p>
<% end %>
</div> </div>
<% end %> <% end %>
<% end %>
<%= if @live_action in [:index, :page] do %>
<nav class="d-flex justify-content-between" aria-label="Page navigation">
<%= if @page_prev do %>
<%= live_patch to: Routes.blog_path(@socket, :page, @page_prev) do %>
&larr; Older
<% end %>
<% else %>
<div class="d-block"></div>
<% end %>
<%= if @page_next do %>
<%= live_patch to: @page_next == 1 && Routes.blog_path(@socket, :index) || Routes.blog_path(@socket, :page, @page_next) do %>
Newer &rarr;
<% end %>
<% end %>
</nav>
<% end %>
</div> </div>
</main> </main>

View file

@ -1,58 +0,0 @@
defmodule Home73kWeb.PostLive do
use Home73kWeb, :live_view
alias Home73k.Blog
@impl true
def mount(%{"id" => id}, session, socket) do
# IO.inspect(params, label: "postlive params")
IO.inspect(session, label: "postlive session")
post = Blog.get_post_by_id!(id)
socket
|> assign(:page_title, "Blog \\ #{post.title}")
|> assign(:post, post)
|> live_okreply()
end
# @impl true
# def handle_params(params, _url, socket) do
# socket
# |> assign(:page_title, "Blog")
# |> assign(:posts, Blog.list_posts())
# |> live_noreply()
# end
# @impl true
# def handle_event("suggest", %{"q" => query}, socket) do
# {:noreply, assign(socket, results: search(query), query: query)}
# end
# @impl true
# def handle_event("search", %{"q" => query}, socket) do
# case search(query) do
# %{^query => vsn} ->
# {:noreply, redirect(socket, external: "https://hexdocs.pm/#{query}/#{vsn}")}
# _ ->
# {:noreply,
# socket
# |> put_flash(:error, "No dependencies found matching \"#{query}\"")
# |> assign(results: %{}, query: query)}
# end
# end
# defp search(query) do
# if not Home73kWeb.Endpoint.config(:code_reloader) do
# raise "action disabled when not in development"
# end
# for {app, desc, vsn} <- Application.started_applications(),
# app = to_string(app),
# String.starts_with?(app, query) and not List.starts_with?(desc, ~c"ERTS"),
# into: %{},
# do: {app, vsn}
# end
end

View file

@ -1,36 +0,0 @@
<main class="container d-flex justify-content-center">
<div class="col-12 col-md-10 col-lg-9 col-xl-8 col-xxl-7 pb-2 mb-4 mt-3">
<div class="post border-bottom border-gray pb-4 mb-3">
<h2 class="post-title fs-2 fw-normal mb-2"><%= raw @post.title %></h2>
<div class="post-date font-monospace text-gray-400 <%= if length(@post.tags) == 0, do: "mb-3" %>">
<%= icon_div @socket, "mdi-calendar-clock", [class: "icon baseline me-2"] %><%= @post.date |> format_date() %>
by <%= icon_div @socket, "mdi-account", [class: "icon baseline me-1"] %>Adam Piontek
</div>
<%= if length(@post.tags) > 0 do %>
<div class="post-tags fs-smaller mb-3">
<%= icon_div @socket, "mdi-tag-multiple", [class: "icon baseline"] %>
<%= for {tag, i} <- Enum.with_index(@post.tags) do %>
#<%= tag %><%= i < (length(@post.tags) - 1) && "," || "" %>
<% end %>
</div>
<% end %>
<div class="post-lede">
<%= raw @post.lede %>
</div>
<div class="post-body">
<%= raw @post.body %>
</div>
</div>
</div>
</main>

View file

@ -17,13 +17,17 @@ defmodule Home73kWeb.Router do
scope "/", Home73kWeb do scope "/", Home73kWeb do
pipe_through :browser pipe_through :browser
# Pages
get "/", HomeController, :index get "/", HomeController, :index
get "/about", HomeController, :about get "/about", HomeController, :about
get "/resume", HomeController, :resume get "/resume", HomeController, :resume
get "/folio", HomeController, :folio get "/folio", HomeController, :folio
# Blog
live "/blog", BlogLive, :index live "/blog", BlogLive, :index
# live "/blog/page/:page", BlogLive, :older live "/blog/page/:page", BlogLive, :page
live "/blog/:id", PostLive, :show live "/blog/tag/:tag", BlogLive, :tag
live "/blog/:id", BlogLive, :show
end end
# Other scopes may use custom stacks. # Other scopes may use custom stacks.

View file

@ -1,9 +0,0 @@
defmodule Home73kWeb.DateHelpers do
@moduledoc """
Formatters for dates
"""
def format_date(date) do
Calendar.strftime(date, "%B %-d, %Y")
end
end

View file

@ -21,10 +21,11 @@ Naturally I ended up writing some alternate lyrics. There's only two; I never go
- ["Florida Cargo" (to the tune of "Florida Kilos")](#florida-cargo) - ["Florida Cargo" (to the tune of "Florida Kilos")](#florida-cargo)
- ["The Other Schooner" (to the tune of "The Other Woman")](#other-schooner) - ["The Other Schooner" (to the tune of "The Other Woman")](#other-schooner)
#### "Florida Cargo" (to the tune of "Florida Kilos")
<a name="florida-cargo"></a> <a name="florida-cargo"></a>
#### "Florida Cargo" (to the tune of "Florida Kilos")
White shine, royal sailor, doubloons, White shine, royal sailor, doubloons,
Don't you see them gleam, Don't you see them gleam,
They're special, just for you. They're special, just for you.
@ -103,10 +104,10 @@ White shine, royal sailor,
Gold teeth, royal sailor, yeah, Gold teeth, royal sailor, yeah,
Drink the night away. Drink the night away.
#### "The Other Schooner" (to the tune of "The Other Woman")
<a name="other-schooner"></a> <a name="other-schooner"></a>
#### "The Other Schooner" (to the tune of "The Other Woman")
The other schooner has time to trim and furl her sails The other schooner has time to trim and furl her sails
The other schooner is perfect where her rival fails The other schooner is perfect where her rival fails
And she's never seen with caulking in her hull anywhere And she's never seen with caulking in her hull anywhere