From a6186ba63960a3527251c975ecced00b6316b9ae Mon Sep 17 00:00:00 2001 From: Adam Piontek Date: Sat, 3 Apr 2021 23:22:35 -0400 Subject: [PATCH] added content, post display --- assets/css/_bs-colors.scss | 2 +- assets/css/_bs-custom.scss | 7 +- assets/css/_fonts.scss | 33 +++-- assets/css/app.scss | 89 ++++++++++--- assets/js/app.js | 3 + assets/package-lock.json | 22 +-- assets/package.json | 2 +- config/config.exs | 1 + config/dev.exs | 3 +- lib/home73k/blog.ex | 13 +- lib/home73k/blog/post.ex | 56 ++++---- .../controllers/home_controller.ex | 4 + lib/home73k_web/endpoint.ex | 2 +- lib/home73k_web/live/blog_live.ex | 54 ++++++++ lib/home73k_web/live/blog_live.html.leex | 41 ++++++ lib/home73k_web/live/page_live.ex | 48 ------- lib/home73k_web/live/page_live.html.leex | 48 ------- lib/home73k_web/live/post_live.ex | 61 +++++++++ lib/home73k_web/live/post_live.html.leex | 36 +++++ lib/home73k_web/router.ex | 5 +- lib/home73k_web/templates/home/about.html.eex | 48 +++++++ lib/home73k_web/templates/home/folio.html.eex | 4 +- lib/home73k_web/templates/home/index.html.eex | 4 +- .../templates/home/resume.html.eex | 16 +-- .../templates/layout/_navbar.html.eex | 12 +- lib/home73k_web/views/layout_view.ex | 5 + mix.exs | 2 +- mix.lock | 6 + .../2016/2016-05-01_initial-test-post.md | 15 --- .../content/2018/03/2018-03-24_pihole-love.md | 40 ++++++ ...-17_if-no-one-will-do-it-neednt-be-done.md | 75 +++++++++++ ...lana-del-arr-ultrapirates-parody-lyrics.md | 126 ++++++++++++++++++ .../05/2020-05-23_creating-gif-from-video.md | 63 +++++++++ ... => 2020-08-01_enable-vs-cli-env-in-ps.md} | 0 .../2020/{ => 12}/2020-12-29_moms-meatloaf.md | 2 + ...{page_live_test.exs => blog_live_test.exs} | 2 +- 36 files changed, 751 insertions(+), 199 deletions(-) create mode 100644 lib/home73k_web/live/blog_live.ex create mode 100644 lib/home73k_web/live/blog_live.html.leex delete mode 100644 lib/home73k_web/live/page_live.ex delete mode 100644 lib/home73k_web/live/page_live.html.leex create mode 100644 lib/home73k_web/live/post_live.ex create mode 100644 lib/home73k_web/live/post_live.html.leex create mode 100644 lib/home73k_web/templates/home/about.html.eex delete mode 100644 priv/content/2016/2016-05-01_initial-test-post.md create mode 100644 priv/content/2018/03/2018-03-24_pihole-love.md create mode 100644 priv/content/2018/05/2018-05-17_if-no-one-will-do-it-neednt-be-done.md create mode 100644 priv/content/2018/11/2018-11-09_lana-del-arr-ultrapirates-parody-lyrics.md create mode 100644 priv/content/2020/05/2020-05-23_creating-gif-from-video.md rename priv/content/2020/08/{2020-08-01_enable_vs_cli_env_in_ps.md => 2020-08-01_enable-vs-cli-env-in-ps.md} (100%) rename priv/content/2020/{ => 12}/2020-12-29_moms-meatloaf.md (99%) rename test/home73k_web/live/{page_live_test.exs => blog_live_test.exs} (88%) diff --git a/assets/css/_bs-colors.scss b/assets/css/_bs-colors.scss index 7679571..61bff40 100644 --- a/assets/css/_bs-colors.scss +++ b/assets/css/_bs-colors.scss @@ -2,7 +2,7 @@ $primary: #e48663; $secondary: #00b0b0; $success: #99c24d; -$info: #a67db8; +$info: #b489c7; $warning: #f4d35e; $white: #fff; diff --git a/assets/css/_bs-custom.scss b/assets/css/_bs-custom.scss index 972a56f..a211a99 100644 --- a/assets/css/_bs-custom.scss +++ b/assets/css/_bs-custom.scss @@ -5,12 +5,15 @@ // Font, line-height, and color for body text, headings, and more. // stylelint-disable value-keyword-case -$font-family-sans-serif: Lato, system-ui, -apple-system, "Segoe UI", Roboto, +$font-family-sans-serif: "Work Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !important; -$font-family-secondary: Righteous, system-ui, -apple-system, "Segoe UI", Roboto, +$font-family-base: $font-family-sans-serif; + +$font-family-brand: Righteous, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !important; + $font-family-monospace: "Fira Mono", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; $font-family-code: "Fira Code", "Fira Mono", SFMono-Regular, Menlo, Monaco, diff --git a/assets/css/_fonts.scss b/assets/css/_fonts.scss index ff553bf..145e8d2 100644 --- a/assets/css/_fonts.scss +++ b/assets/css/_fonts.scss @@ -1,21 +1,30 @@ -/* Fontsource Lato */ -// @import "../node_modules/@fontsource/lato/100.css"; /* thin | normal */ -// @import "../node_modules/@fontsource/lato/100-italic.css"; /* thin | italic */ -@import "../node_modules/@fontsource/lato/300.css"; /* light | normal */ -// @import "../node_modules/@fontsource/lato/300-italic.css"; /* light | italic */ -@import "../node_modules/@fontsource/lato/400.css"; /* normal | normal */ -// @import "../node_modules/@fontsource/lato/400-italic.css"; /* normal | italic */ -@import "../node_modules/@fontsource/lato/700.css"; /* bold | normal */ -// @import "../node_modules/@fontsource/lato/700-italic.css"; /* bold | italic */ -// @import "../node_modules/@fontsource/lato/900.css"; /* black | normal */ -// @import "../node_modules/@fontsource/lato/900-italic.css"; /* black | italic */ +/* Fontsource Work Sans */ +@import "../node_modules/@fontsource/work-sans/100.css"; /* thin | normal */ +@import "../node_modules/@fontsource/work-sans/100-italic.css"; /* thin | italic */ +// @import "../node_modules/@fontsource/work-sans/200.css"; /* thin-light | normal */ +// @import "../node_modules/@fontsource/work-sans/200-italic.css"; /* thin-light | italic */ +@import "../node_modules/@fontsource/work-sans/300.css"; /* light | normal */ +@import "../node_modules/@fontsource/work-sans/300-italic.css"; /* light | italic */ +@import "../node_modules/@fontsource/work-sans/400.css"; /* normal | normal */ +@import "../node_modules/@fontsource/work-sans/400-italic.css"; /* normal | italic */ +// @import "../node_modules/@fontsource/work-sans/500.css"; /* heavier | normal */ +// @import "../node_modules/@fontsource/work-sans/500-italic.css"; /* heavier | italic */ +@import "../node_modules/@fontsource/work-sans/600.css"; /* heavier? | normal */ +@import "../node_modules/@fontsource/work-sans/600-italic.css"; /* heavier | italic */ +@import "../node_modules/@fontsource/work-sans/700.css"; /* bold | normal */ +@import "../node_modules/@fontsource/work-sans/700-italic.css"; /* bold | italic */ +// @import "../node_modules/@fontsource/work-sans/800.css"; /* bolder? | normal */ +// @import "../node_modules/@fontsource/work-sans/800-italic.css"; /* bolder? | italic */ +// @import "../node_modules/@fontsource/work-sans/900.css"; /* black | normal */ +// @import "../node_modules/@fontsource/work-sans/900-italic.css"; /* black | italic */ + /* Fontsource Righteous */ @import "../node_modules/@fontsource/righteous/400.css"; /* normal | normal */ /* Fontsource Fira Mono */ @import "../node_modules/@fontsource/fira-mono/400.css"; /* normal | normal */ -// @import "../node_modules/@fontsource/fira-mono/500.css"; /* heavier normal? */ +@import "../node_modules/@fontsource/fira-mono/500.css"; /* heavier normal? */ // @import "../node_modules/@fontsource/fira-mono/700.css"; /* bold | normal */ /* Fontsource Fira Code */ diff --git a/assets/css/app.scss b/assets/css/app.scss index 8ef090f..54ad96b 100644 --- a/assets/css/app.scss +++ b/assets/css/app.scss @@ -23,25 +23,64 @@ body { background-color: $gray-800; height: 100%; } +a { + color: $primary; + &:visited { + color: $info; + } + &:hover { + color: $secondary; + } +} .border-gray-900 { border-color: $gray-900 !important; } +.text-gray-200 { + color: $gray-200; +} .text-gray-300 { color: $gray-300; } +.text-gray-400 { + color: $gray-400; +} +.text-gray-500 { + color: $gray-500; +} .border-10 { border-width: 10px !important; } .border-20 { border-width: 20px !important; } +.fw-500 { + font-weight: 500; +} .fw-600 { font-weight: 600; } +.font-sans-serif { + font-family: $font-family-sans-serif; +} +.font-brand { + font-family: $font-family-brand; +} +.font-code { + font-family: $font-family-code; +} +.fs-larger { + font-size: larger; +} +.fs-smaller { + font-size: smaller; +} /* social icons */ -#social-icons .link-light:hover { - color: $primary; +#social-icons .link-light { + color: $gray-100; + &:hover { + color: $primary; + } } /* resume separators */ @@ -49,26 +88,46 @@ body { display: flex; align-items: center; text-align: center; - color: $gray-300; + color: $gray-500; font-family: $font-family-monospace; font-size: smaller; text-transform: uppercase; - font-weight: 600; + font-weight: 700; + &::before, + &::after { + content: ""; + flex: 1; + border-bottom: 1px solid $secondary; + } + &:not(:empty)::before { + margin-right: 0.5em; + } + &:not(:empty)::after { + margin-left: 0.33em; + } } -.separator::before, -.separator::after { - content: ""; - flex: 1; - border-bottom: 1px solid $secondary; +/* blog */ +.post-title a { + color: $gray-100; + text-decoration: none; + &:hover { + color: $primary; + text-decoration: underline; + } } - -.separator:not(:empty)::before { - margin-right: 0.5em; +.post-lede, +.post-body { + color: $gray-200; + h2, h3, h4, h5, h6 { + color: $gray-300; + margin-top: 2rem; + } } - -.separator:not(:empty)::after { - margin-left: 0.33em; +.post-lede.lead { + font-size: 1.25rem; + font-weight: 300; + color: $gray-300; } /* extra */ diff --git a/assets/js/app.js b/assets/js/app.js index 5bbfc63..3a2ff03 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -8,10 +8,13 @@ import "../css/app.scss"; import "../node_modules/@mdi/svg/svg/desktop-classic.svg"; // brand // other:/// import "../node_modules/@mdi/svg/svg/home.svg"; +import "../node_modules/@mdi/svg/svg/information.svg"; import "../node_modules/@mdi/svg/svg/account.svg"; import "../node_modules/@mdi/svg/svg/briefcase-account.svg"; import "../node_modules/@mdi/svg/svg/zip-disk.svg"; import "../node_modules/@mdi/svg/svg/typewriter.svg"; +import "../node_modules/@mdi/svg/svg/calendar-clock.svg"; +import "../node_modules/@mdi/svg/svg/tag-multiple.svg"; import "../node_modules/@mdi/svg/svg/rss.svg"; import "../node_modules/@mdi/svg/svg/account-hard-hat.svg"; // social diff --git a/assets/package-lock.json b/assets/package-lock.json index 72f6127..6237e00 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -8,8 +8,8 @@ "dependencies": { "@fontsource/fira-code": "^4.x", "@fontsource/fira-mono": "^4.x", - "@fontsource/lato": "^4.x", "@fontsource/righteous": "^4.x", + "@fontsource/work-sans": "^4.2.2", "@mdi/svg": "^5.x", "@popperjs/core": "^2.x", "bootstrap": "^5.0.0-beta3", @@ -1292,16 +1292,16 @@ "resolved": "https://registry.npmjs.org/@fontsource/fira-mono/-/fira-mono-4.2.2.tgz", "integrity": "sha512-t2WRThg+eLkQNQCtPG2sCCq40lz3xeb7nsL7P8l4+wfSRbdLQXAY5IebMftI2YEZR4MRRhdgrg0p5fi/2yXypA==" }, - "node_modules/@fontsource/lato": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@fontsource/lato/-/lato-4.2.2.tgz", - "integrity": "sha512-ZE5WvqZQZinXpH8MaEiM9klDsUOfCHVQJ/tZKpNVQhi8mHt9WqPCROu500oI5jC3s6jaJuWsM7LfJ1zyEeW+XA==" - }, "node_modules/@fontsource/righteous": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@fontsource/righteous/-/righteous-4.2.2.tgz", "integrity": "sha512-mUjFblfCV6eWZj+lkrXFZsER8pq/3LOCoT3ezKPcerYH7StXQ8Gflcs0uMqacZP7CVLyzVUkPvSgLMQJTQvypg==" }, + "node_modules/@fontsource/work-sans": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@fontsource/work-sans/-/work-sans-4.2.2.tgz", + "integrity": "sha512-fFm8a1TbE+qDnRNsf4R6Z/yZP2f3mbj54zSoWUMIWVkgTHM0RfpcOTqoIp7Aj5jon6na0Oynlq5yXvMA5p3pKA==" + }, "node_modules/@mdi/svg": { "version": "5.9.55", "resolved": "https://registry.npmjs.org/@mdi/svg/-/svg-5.9.55.tgz", @@ -10919,16 +10919,16 @@ "resolved": "https://registry.npmjs.org/@fontsource/fira-mono/-/fira-mono-4.2.2.tgz", "integrity": "sha512-t2WRThg+eLkQNQCtPG2sCCq40lz3xeb7nsL7P8l4+wfSRbdLQXAY5IebMftI2YEZR4MRRhdgrg0p5fi/2yXypA==" }, - "@fontsource/lato": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@fontsource/lato/-/lato-4.2.2.tgz", - "integrity": "sha512-ZE5WvqZQZinXpH8MaEiM9klDsUOfCHVQJ/tZKpNVQhi8mHt9WqPCROu500oI5jC3s6jaJuWsM7LfJ1zyEeW+XA==" - }, "@fontsource/righteous": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@fontsource/righteous/-/righteous-4.2.2.tgz", "integrity": "sha512-mUjFblfCV6eWZj+lkrXFZsER8pq/3LOCoT3ezKPcerYH7StXQ8Gflcs0uMqacZP7CVLyzVUkPvSgLMQJTQvypg==" }, + "@fontsource/work-sans": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@fontsource/work-sans/-/work-sans-4.2.2.tgz", + "integrity": "sha512-fFm8a1TbE+qDnRNsf4R6Z/yZP2f3mbj54zSoWUMIWVkgTHM0RfpcOTqoIp7Aj5jon6na0Oynlq5yXvMA5p3pKA==" + }, "@mdi/svg": { "version": "5.9.55", "resolved": "https://registry.npmjs.org/@mdi/svg/-/svg-5.9.55.tgz", diff --git a/assets/package.json b/assets/package.json index 9c049b3..98bc41b 100644 --- a/assets/package.json +++ b/assets/package.json @@ -9,8 +9,8 @@ "dependencies": { "@fontsource/fira-code": "^4.x", "@fontsource/fira-mono": "^4.x", - "@fontsource/lato": "^4.x", "@fontsource/righteous": "^4.x", + "@fontsource/work-sans": "^4.2.2", "@mdi/svg": "^5.x", "@popperjs/core": "^2.x", "bootstrap": "^5.0.0-beta3", diff --git a/config/config.exs b/config/config.exs index 2b8c7eb..2b6d096 100644 --- a/config/config.exs +++ b/config/config.exs @@ -7,6 +7,7 @@ # General application configuration use Mix.Config +# Tzdata db config config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase # Custom application global variables diff --git a/config/dev.exs b/config/dev.exs index 568e29b..bf56af1 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -53,7 +53,8 @@ config :home73k, Home73kWeb.Endpoint, ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$", ~r"priv/gettext/.*(po)$", ~r"lib/home73k_web/(live|views)/.*(ex)$", - ~r"lib/home73k_web/templates/.*(eex)$" + ~r"lib/home73k_web/templates/.*(eex)$", + ~r"priv/content/*/.*(md)$" ] ] diff --git a/lib/home73k/blog.ex b/lib/home73k/blog.ex index fcaa806..806d291 100644 --- a/lib/home73k/blog.ex +++ b/lib/home73k/blog.ex @@ -18,9 +18,16 @@ defmodule Home73k.Blog do def list_posts, do: @posts def list_tags, do: @tags - # defmodule NotFoundError do - # defexception [:message, plug_status: 404] - # end + defmodule NotFoundError do + defexception [:message, plug_status: 404] + end + + def get_post_by_id!(id) do + case Enum.find(list_posts(), nil, &(&1.id == id)) do + %Post{} = post -> post + nil -> raise NotFoundError, "post with id=#{id} not found" + end + end # def get_posts_by_tag!(tag) do # case Enum.filter(list_posts(), &(tag in &1.tags)) do diff --git a/lib/home73k/blog/post.ex b/lib/home73k/blog/post.ex index 1716890..1a5d1a6 100644 --- a/lib/home73k/blog/post.ex +++ b/lib/home73k/blog/post.ex @@ -1,8 +1,8 @@ defmodule Home73k.Blog.Post do - @enforce_keys [:title, :slug, :date, :author, :tags, :lede, :body, :corpus] - defstruct [:title, :slug, :date, :author, :tags, :lede, :body, :corpus] + @enforce_keys [:title, :id, :date, :author, :tags, :lede, :body, :corpus] + defstruct [:title, :id, :date, :author, :tags, :lede, :body, :corpus] - @strip_words ~w(the and are for not but had has was all any too one you his her can that with have this will your from they want been much some very them into which then now get its youll youre) + @strip_words ~w(the and are for not but had has was all any too one you his her can that with have this will your from they want been much some very them into which then now get its youll youre isnt wasnt) @doc """ The public parse!/1 function begins the post parse process by reading @@ -36,7 +36,7 @@ defmodule Home73k.Blog.Post do # """ defp parse_frontmatter([fm, md]) do case parse_frontmatter_string(fm) do - {%{} = parsed_fm, _} -> {set_post_slug(parsed_fm), String.trim(md)} + {%{} = parsed_fm, _} -> {set_post_id(parsed_fm), String.trim(md)} {:error, _} -> nil end end @@ -46,6 +46,11 @@ defmodule Home73k.Blog.Post do # """ parse_lede/1 # Look for lede/excerpt/summary in content and extract it if present. # We return updated frontmatter, and content with stripped. + defp parse_lede({%{lede: lede} = fm, md}) do + lede = String.trim(lede) |> Earmark.as_html!() + {Map.put(fm, :lede, lede), md} + end + defp parse_lede({fm, md}) do {lede, body_md} = String.split(md, "", parts: 2) |> extract_lede() {Map.put(fm, :lede, lede), String.replace(body_md, "", " ")} @@ -58,6 +63,8 @@ defmodule Home73k.Blog.Post do # TODO: handle syntax highlighting defp parse_body({fm, md}) do Map.put(fm, :body, Earmark.as_html!(md)) + # TODO: Earmark.as_ast(md) |> parse_body(fm) + # def parse_body({:ok, ast, _}, fm) end defp parse_body(_), do: nil @@ -66,12 +73,15 @@ defmodule Home73k.Blog.Post do # Create a searchable word list for the post, for live searching defp build_corpus(%{title: title, lede: lede, body: body, tags: tags} = post_data) do # initialize corpus string from: title, lede, body, tags - corpus = (tags ++ [title, (lede && lede) || " ", body]) |> Enum.join(" ") |> String.downcase() - - # scrub out (but replace with spaces): - # code blocks, html tags, html entities, newlines, forward and back slashes - html_scrub_regex = ~r/(
<\/pre>)|(<(.|\n)+?>)|(&#(.)+?;)|(&(.)+?;)|\n|\/|\\/
-    corpus = Regex.replace(html_scrub_regex, corpus, " ")
+    # grab text only, rejecting HTML
+    # downcase & scrub line breaks, slashes
+    corpus =
+      (tags ++ [title, (lede && lede) || " ", body])
+      |> Enum.join(" ")
+      |> Floki.parse_fragment!()
+      |> Floki.text()
+      |> String.downcase()
+      |> String.replace(["\n", "/", "\\", "(", ")", ":", "=", "_", ".", ",", "[", "]"], " ")
 
     # restrict corpus to letters & numbers,
     # then split to words (space delim), trimming as we go
@@ -108,8 +118,7 @@ defmodule Home73k.Blog.Post do
   # """
   defp parse_frontmatter_string(fm) do
     try do
-      String.trim_leading(fm, "-")
-      |> Code.eval_string()
+      Code.eval_string(fm)
     rescue
       _ -> {:error, nil}
     end
@@ -124,22 +133,23 @@ defmodule Home73k.Blog.Post do
 
   defp extract_lede([body]), do: {nil, body}
 
-  # """ set_frontmatter_slug
-  # If no slug in frontmatter, convert title to slug and add to map
+  # """ set_post_id
+  # If no id in frontmatter, convert title to id and add to map
   # """
-  defp set_post_slug(%{slug: _} = fm), do: fm
+  defp set_post_id(%{id: _} = fm), do: fm
 
-  defp set_post_slug(%{title: title} = fm) do
-    Map.put(fm, :slug, parse_title_to_slug(title))
+  defp set_post_id(%{title: title} = fm) do
+    Map.put(fm, :id, parse_title_to_id(title))
   end
 
-  # """ parse_title_to_slug
-  # Takes a post title and returns a slug cleansed for URI request path
+  # """ parse_title_to_id
+  # Takes a post title and returns a id cleansed for URI request path
   # """
-  defp parse_title_to_slug(title) do
-    title = String.downcase(title)
+  def parse_title_to_id(title) do
+    title_text = Floki.parse_fragment!(title) |> Floki.text() |> String.downcase()
 
-    Regex.replace(~r/[^a-z0-9 ]/, title, "")
+    ~r/[^a-z0-9 ]/
+    |> Regex.replace(title_text, "")
     |> String.split(" ", trim: true)
     |> Stream.reject(&reject_word?/1)
     |> Enum.join("-")
@@ -147,7 +157,7 @@ defmodule Home73k.Blog.Post do
 
   # """ reject_word?
   # Returns true to reject short or common words
-  # Used by parse_title_to_slug and build_corpus
+  # Used by parse_title_to_id and build_corpus
   # """
   defp reject_word?(word), do: String.length(word) < 3 || word in @strip_words
 end
diff --git a/lib/home73k_web/controllers/home_controller.ex b/lib/home73k_web/controllers/home_controller.ex
index fe7e1f4..dcd9766 100644
--- a/lib/home73k_web/controllers/home_controller.ex
+++ b/lib/home73k_web/controllers/home_controller.ex
@@ -5,6 +5,10 @@ defmodule Home73kWeb.HomeController do
     render(conn, "index.html")
   end
 
+  def about(conn, _params) do
+    render(conn, "about.html", page_title: "About")
+  end
+
   def resume(conn, _params) do
     render(conn, "resume.html", page_title: "Résumé")
   end
diff --git a/lib/home73k_web/endpoint.ex b/lib/home73k_web/endpoint.ex
index 3e9a938..18ce5a4 100644
--- a/lib/home73k_web/endpoint.ex
+++ b/lib/home73k_web/endpoint.ex
@@ -23,7 +23,7 @@ defmodule Home73kWeb.Endpoint do
   plug Plug.Static,
     at: "/",
     from: :home73k,
-    gzip: true,
+    gzip: (Mix.env() not in [:dev, :test]),
     only: ~w(css fonts images js favicon.ico robots.txt DF185CEE29A3D443_public_key.asc)
 
   # Code reloading can be explicitly enabled under the
diff --git a/lib/home73k_web/live/blog_live.ex b/lib/home73k_web/live/blog_live.ex
new file mode 100644
index 0000000..fb8a99c
--- /dev/null
+++ b/lib/home73k_web/live/blog_live.ex
@@ -0,0 +1,54 @@
+defmodule Home73kWeb.BlogLive do
+  use Home73kWeb, :live_view
+
+  alias Home73k.Blog
+
+  @impl true
+  def mount(_params, _session, socket) do
+    socket
+    |> 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
+
+  defp format_date(date) do
+    Calendar.strftime(date, "%B %-d, %Y")
+  end
+end
diff --git a/lib/home73k_web/live/blog_live.html.leex b/lib/home73k_web/live/blog_live.html.leex
new file mode 100644
index 0000000..1e055c9
--- /dev/null
+++ b/lib/home73k_web/live/blog_live.html.leex
@@ -0,0 +1,41 @@
+
+ +
+ + <%= for post <- @posts do %> + +
+ +

+ <%= live_redirect "#{post.title}", to: Routes.post_path(@socket, :show, post) %> +

+ + + + <%= if length(post.tags) > 0 do %> + + <% end %> + +
+ <%= raw post.lede %> +
+ +

+ <%= live_redirect raw("Read more…"), to: Routes.post_path(@socket, :show, post), class: "fs-6" %> +

+ +
+ + <% end %> + +
+ +
diff --git a/lib/home73k_web/live/page_live.ex b/lib/home73k_web/live/page_live.ex deleted file mode 100644 index fe36b7a..0000000 --- a/lib/home73k_web/live/page_live.ex +++ /dev/null @@ -1,48 +0,0 @@ -defmodule Home73kWeb.PageLive do - use Home73kWeb, :live_view - - @impl true - def mount(_params, _session, socket) do - {:ok, - assign(socket, query: "", results: %{}, page_title: "~") - |> put_flash(:success, "Log in was a success. Good for you.") - |> put_flash(:error, "Lorem ipsum dolor sit amet consectetur adipisicing elit.") - |> put_flash( - :info, - "Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatibus dolore sunt quia aperiam sint id reprehenderit? Dolore incidunt alias inventore accusantium nulla optio, ducimus eius aliquam hic, pariatur voluptate distinctio." - ) - |> put_flash(:warning, "Oh no, there's nothing to worry about!") - |> put_flash(:primary, "Something in the brand color.")} - 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 diff --git a/lib/home73k_web/live/page_live.html.leex b/lib/home73k_web/live/page_live.html.leex deleted file mode 100644 index 52509c2..0000000 --- a/lib/home73k_web/live/page_live.html.leex +++ /dev/null @@ -1,48 +0,0 @@ -
-

<%= gettext "Welcome to %{name}!", name: "Phoenix" %>

-

Peace of mind from prototype to production

- -
- - - <%= for {app, _vsn} <- @results do %> - - <% end %> - - -
-
- -
- - -
diff --git a/lib/home73k_web/live/post_live.ex b/lib/home73k_web/live/post_live.ex new file mode 100644 index 0000000..dbdb003 --- /dev/null +++ b/lib/home73k_web/live/post_live.ex @@ -0,0 +1,61 @@ +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 + + defp format_date(date) do + Calendar.strftime(date, "%B %-d, %Y") + end +end diff --git a/lib/home73k_web/live/post_live.html.leex b/lib/home73k_web/live/post_live.html.leex new file mode 100644 index 0000000..6ed92d1 --- /dev/null +++ b/lib/home73k_web/live/post_live.html.leex @@ -0,0 +1,36 @@ +
+ +
+ +
+ +

<%= raw @post.title %>

+ + + + <%= if length(@post.tags) > 0 do %> + + <% end %> + +
+ <%= raw @post.lede %> +
+ +
+ <%= raw @post.body %> +
+ + +
+ +
+ +
diff --git a/lib/home73k_web/router.ex b/lib/home73k_web/router.ex index a2bd4cc..352684e 100644 --- a/lib/home73k_web/router.ex +++ b/lib/home73k_web/router.ex @@ -18,9 +18,12 @@ defmodule Home73kWeb.Router do pipe_through :browser get "/", HomeController, :index + get "/about", HomeController, :about get "/resume", HomeController, :resume get "/folio", HomeController, :folio - live "/live", PageLive, :index + live "/blog", BlogLive, :index + # live "/blog/page/:page", BlogLive, :older + live "/blog/:id", PostLive, :show end # Other scopes may use custom stacks. diff --git a/lib/home73k_web/templates/home/about.html.eex b/lib/home73k_web/templates/home/about.html.eex new file mode 100644 index 0000000..36637ee --- /dev/null +++ b/lib/home73k_web/templates/home/about.html.eex @@ -0,0 +1,48 @@ +
+ +
+ +

About

+ +
Adam Piontek. Human.
+ + +
+
+

“Probabilities collapse. I become increasingly unlikely.”

+
+ +
+ +
+

+ From Minnesota, via California, now New York. I'm always learning, and I often think most things should slow down a little, though I'm still working on that practice for myself. I've been a tech nerd since my teens in the 1990s, but didn't begin to take it seriously until around 2009. +

+ +

+ I'm now a Desktop Systems Engineer with a large global law firm, involved in desktop & mobile security, testing, deployment, and elevated user support. +

+ +

+ I enjoy spending time with my many pets, talking science fiction & politics, going hiking, and tinkering with programming projects. I've written a few things I'm reasonably proud of — for work, for family, for myself — including this website! +

+ +

+ This site is powered by a custom little <%= link "Phoenix", to: "https://phoenixframework.org/" %> project hosted on a <%= link "Linode", to: "https://www.linode.com/" %> VPS, where I also run instances of <%= link "Gitea", to: "https://gitea.io/" %>, <%= link "Miniflux", to: "https://miniflux.app/" %>, <%= link "ZNC", to: "https://znc.in/" %>, <%= link "The Lounge", to: "https://thelounge.chat/" %>, <%= link "Calibre-Web", to: "https://github.com/janeczku/calibre-web" %>, <%= link "BicBucStriim", to: "https://projekte.textmulch.de/bicbucstriim/" %>, a little <%= link "hubot", to: "https://hubot.github.com/" %> for my family's group chat (Discord for now), and some other odds & ends. +

+ +

+ At home, between a plucky Raspberry Pi 4 running Ubuntu Linux, and a beefier Windows machine, I also run: <%= link "Jellyfin", to: "https://jellyfin.org/" %>, <%= link "Plex", to: "https://www.plex.tv/" %>, and several apps via Docker, including <%= link "audioserve", to: "https://github.com/izderadicka/audioserve" %>, <%= link "Komga", to: "https://komga.org/" %>, <%= link "Unifi Controller", to: "https://github.com/jacobalberty/unifi-docker" %>, and a group of others (like <%= link "transmission-openvpn", to: "https://hub.docker.com/r/haugene/transmission-openvpn/" %> and <%= link "nzbget", to: "https://hub.docker.com/r/linuxserver/nzbget" %> (Arr!)) +

+ +

+ I'm very lucky to have some super amazing people in my life, always reminding me of how little I know, enabling beautiful experiences, and appreciating places and systems I otherwise wouldn't give a second thought to. +

+ +
+ +
+ +
diff --git a/lib/home73k_web/templates/home/folio.html.eex b/lib/home73k_web/templates/home/folio.html.eex index 257e5c2..a94deec 100644 --- a/lib/home73k_web/templates/home/folio.html.eex +++ b/lib/home73k_web/templates/home/folio.html.eex @@ -13,11 +13,11 @@
-

+

<%= icon_div @conn, "mdi-account-hard-hat", [class: "icon baseline me-2"] %>Working on it!

-
I've made some things over the years (like this site!), and at some point I'll highlight some here.
+
I've made some things over the years (like this site!), and at some point I'll highlight some here.
diff --git a/lib/home73k_web/templates/home/index.html.eex b/lib/home73k_web/templates/home/index.html.eex index e333cd4..83a7720 100644 --- a/lib/home73k_web/templates/home/index.html.eex +++ b/lib/home73k_web/templates/home/index.html.eex @@ -13,11 +13,11 @@
-

+

<%= icon_div @conn, "mdi-account", [class: "icon baseline me-2"] %>Adam Piontek

-
Desktop Systems Engineer. Human.
+
Desktop Systems Engineer. Human.
<%= for s <- socials(@conn) do %> diff --git a/lib/home73k_web/templates/home/resume.html.eex b/lib/home73k_web/templates/home/resume.html.eex index f512d24..1b8f2cc 100644 --- a/lib/home73k_web/templates/home/resume.html.eex +++ b/lib/home73k_web/templates/home/resume.html.eex @@ -1,14 +1,14 @@ -
+
-

+

<%= icon_div @conn, "mdi-account", [class: "icon baseline me-2"] %>Adam Piontek

-
Desktop Systems Engineer.
+
Desktop Systems Engineer.
<%= for s <- socials_prof(@conn) do %> @@ -24,21 +24,21 @@
qualifications
-
    +
      <%= for qualif <- resume_qualifs() do %> <%= content_tag :li, qualif %> <% end %>
    -
    experience
    +
    experience
    <%= for %{employer: employer, positions: positions} <- resume_experience() do %> -
    <%= employer%>
    +
    <%= employer%>
    <%= for position <- positions do %>
    - <%= position.title %> - · <%= position.start %> — <%= position.end %> + <%= position.title %> + · <%= position.start %> — <%= position.end %>
    <% end %>
    diff --git a/lib/home73k_web/templates/layout/_navbar.html.eex b/lib/home73k_web/templates/layout/_navbar.html.eex index 3ecf682..1da18f9 100644 --- a/lib/home73k_web/templates/layout/_navbar.html.eex +++ b/lib/home73k_web/templates/layout/_navbar.html.eex @@ -1,10 +1,10 @@ -