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

View file

@ -3,21 +3,29 @@ defmodule Home73k.Blog do
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 =
for post_path <- posts_paths do
for post_path <- post_paths do
@external_resource Path.relative_to_cwd(post_path)
Post.parse!(post_path)
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})
@post_count length(@posts)
@tags posts |> Stream.flat_map(& &1.tags) |> Stream.uniq() |> Enum.sort()
def list_posts, do: @posts
def list_tags, do: @tags
def post_count, do: @post_count
defmodule NotFoundError do
defexception [:message, plug_status: 404]
end
@ -29,10 +37,10 @@ defmodule Home73k.Blog do
end
end
# def get_posts_by_tag!(tag) do
# case Enum.filter(list_posts(), &(tag in &1.tags)) do
# [] -> raise NotFoundError, "posts with tag=#{tag} not found"
# posts -> posts
# end
# end
def get_posts_by_tag!(tag) do
case Enum.filter(list_posts(), &(tag in &1.tags)) do
[] -> raise NotFoundError, "posts with tag=#{tag} not found"
posts -> posts
end
end
end

View file

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

View file

@ -3,49 +3,96 @@ defmodule Home73kWeb.BlogLive do
alias Home73k.Blog
@page_size 7
@impl true
def mount(_params, _session, socket) do
socket
|> live_okreply()
{:ok, socket}
end
@impl true
def handle_params(_params, _url, socket) do
socket
|> assign(:page_title, "Blog")
|> assign(:posts, Blog.list_posts())
def handle_params(params, _url, socket) do
socket.assigns.live_action
|> init_per_live_action(socket, params)
|> live_noreply()
end
# @impl true
# def handle_event("suggest", %{"q" => query}, socket) do
# {:noreply, assign(socket, results: search(query), query: query)}
# end
defp page_param_as_int(page) do
try do
String.to_integer(page)
rescue
_ -> nil
end
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}")}
defp raise_not_found(msg), do: raise Home73k.Blog.NotFoundError, msg
# _ ->
# {:noreply,
# socket
# |> put_flash(:error, "No dependencies found matching \"#{query}\"")
# |> assign(results: %{}, query: query)}
# end
# end
defp init_per_live_action(:index, socket, _params) do
socket
|> assign(:page_title, "Blog")
|> assign(:posts, get_posts_for_page!(1))
|> assign(:page_count, get_page_count())
|> assign_prev_next(1)
end
# defp search(query) do
# if not Home73kWeb.Endpoint.config(:code_reloader) do
# raise "action disabled when not in development"
# end
defp init_per_live_action(:page, socket, %{"page" => page}) do
page_int = page_param_as_int(page)
page_count = get_page_count()
# 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
cond do
is_nil(page_int) || page_int <= 1 ->
push_patch(socket, to: Routes.blog_path(socket, :index))
page_int > page_count ->
raise_not_found("there are only #{page_count} pages of posts")
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

View file

@ -2,38 +2,74 @@
<div class="col-12 col-md-10 col-lg-9 col-xl-8 col-xxl-7 pb-2 mb-4 mt-3">
<%= for post <- @posts do %>
<%= if is_nil(@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">
<%= live_redirect "#{post.title}", to: Routes.post_path(@socket, :show, post) %>
</h2>
<h2 class="post-title fs-2 fw-600 mb-2">Nothing found.</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 %>
<% else %>
<%= for post <- @posts do %>
<div class="post border-bottom border-gray pb-4 mb-3">
<h2 class="post-title fs-2 fw-600 mb-2">
<%= live_redirect "#{post.title}", to: Routes.blog_path(@socket, :show, post) %>
</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"] %><%= format_date(post.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 text-gray-400"] %>
<%= for {tag, i} <- Enum.with_index(post.tags) do %>
<span class="text-gray-400">#</span><%= live_redirect tag, to: Routes.blog_path(@socket, :tag, tag) %><%= i < (length(post.tags) - 1) && "," || "" %>
<% end %>
</div>
<% end %>
<div class="post-lede">
<%= raw post.lede %>
</div>
<%= if @live_action == :show do %>
<div class="post-body">
<%= raw post.body %>
</div>
<% else %>
<p>
<%= live_redirect raw("Read more&hellip;"), to: Routes.blog_path(@socket, :show, post), class: "fs-6" %>
</p>
<% end %>
</div>
<% 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 %>
<div class="post-lede">
<%= raw post.lede %>
</div>
<p>
<%= live_redirect raw("Read more&hellip;"), to: Routes.post_path(@socket, :show, post), class: "fs-6" %>
</p>
</div>
<%= 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>

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
pipe_through :browser
# Pages
get "/", HomeController, :index
get "/about", HomeController, :about
get "/resume", HomeController, :resume
get "/folio", HomeController, :folio
# Blog
live "/blog", BlogLive, :index
# live "/blog/page/:page", BlogLive, :older
live "/blog/:id", PostLive, :show
live "/blog/page/:page", BlogLive, :page
live "/blog/tag/:tag", BlogLive, :tag
live "/blog/:id", BlogLive, :show
end
# 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)
- ["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>
#### "Florida Cargo" (to the tune of "Florida Kilos")
White shine, royal sailor, doubloons,
Don't you see them gleam,
They're special, just for you.
@ -103,10 +104,10 @@ White shine, royal sailor,
Gold teeth, royal sailor, yeah,
Drink the night away.
#### "The Other Schooner" (to the tune of "The Other Woman")
<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 is perfect where her rival fails
And she's never seen with caulking in her hull anywhere