Item views (#7)

* Add Earmark dependency

For markdown to HTML conversion

* Display items by slug: articles, pages

* Handle 401 Forbidden responses in Directus

* Add item controller as pokemon route

* Strip out generated layouts

* Plan future routes

* Look for item in order: article, link, event, album

* Add item templates
This commit is contained in:
Anders Englöf Ytterström 2024-09-06 22:41:12 +02:00 committed by GitHub
parent e907e347c9
commit 8bbe8a1b24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 264 additions and 70 deletions

View file

@ -7,12 +7,16 @@ defmodule Mse25.Directus do
params =
[
"sort=-pubDate",
"fields=" <> Enum.join([
"slug",
"title",
"date_updated",
"pubDate"
], ",")
"fields=" <>
Enum.join(
[
"slug",
"title",
"date_updated",
"pubDate"
],
","
)
]
|> query_params_string(options)
@ -41,18 +45,22 @@ defmodule Mse25.Directus do
params =
[
"sort=-started_at",
#"filter={\"upcoming\":{\"_eq\":true}}",
"fields=" <> Enum.join([
"started_at",
"ended_at",
"title",
"lead",
"poster.filename_download",
"poster.width",
"poster.height",
"bands.artists_id.name",
"mia.artists_id.name"
], ",")
# "filter={\"upcoming\":{\"_eq\":true}}",
"fields=" <>
Enum.join(
[
"started_at",
"ended_at",
"title",
"lead",
"poster.filename_download",
"poster.width",
"poster.height",
"bands.artists_id.name",
"mia.artists_id.name"
],
","
)
]
|> query_params_string(options)
@ -67,15 +75,19 @@ defmodule Mse25.Directus do
params =
[
"sort=-pubDate",
"fields=" <> Enum.join([
"slug",
"title",
"date_updated",
"pubDate",
"h1",
"source",
"contents"
], ",")
"fields=" <>
Enum.join(
[
"slug",
"title",
"date_updated",
"pubDate",
"h1",
"source",
"contents"
],
","
)
]
|> query_params_string(options)
@ -99,11 +111,16 @@ defmodule Mse25.Directus do
[base_url: base_url, token: token] = Application.fetch_env!(:mse25, :directus)
req = Req.new(base_url: base_url <> "/items")
Req.get!(req, url: resource, auth: {:bearer, token})
|> payload
case Req.get!(req, url: resource, auth: {:bearer, token})
|> payload do
{:ok, payload} -> payload
{:forbidden, message} -> message
end
end
defp payload(%Req.Response{body: %{"data" => payload}}), do: payload
defp payload(%Req.Response{status: 200, body: %{"data" => payload}}), do: {:ok, payload}
defp payload(%Req.Response{status: 401}), do: {:forbidden, "Invalid Directus credentials"}
defp query_params_string(params, options),
do:

View file

@ -1,32 +1,32 @@
<header class="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="/">
<img src={~p"/images/logo.svg"} width="36" />
</a>
<p class="bg-brand/5 text-brand rounded-full px-2 font-medium leading-6">
v<%= Application.spec(:phoenix, :vsn) %>
</p>
</div>
<div class="flex items-center gap-4 font-semibold leading-6 text-zinc-900">
<a href="https://twitter.com/elixirphoenix" class="hover:text-zinc-700">
@elixirphoenix
</a>
<a href="https://github.com/phoenixframework/phoenix" class="hover:text-zinc-700">
GitHub
</a>
<a
href="https://hexdocs.pm/phoenix/overview.html"
class="rounded-lg bg-zinc-100 px-2 py-1 hover:bg-zinc-200/80"
>
Get Started <span aria-hidden="true">&rarr;</span>
</a>
</div>
</div>
</header>
<main class="px-4 py-20 sm:px-6 lg:px-8">
<div class="mx-auto max-w-2xl">
<.flash_group flash={@flash} />
<%= @inner_content %>
</div>
</main>
<b>madr</b><br>
|&gt; Hårdrock<br>
|&gt; Programmering<br>
|&gt; Ljudteknik<br>
|&gt; Åsikter
<nav>
<ul>
<li><a href="/om">Om mig</a></li>
<li><a href="/webblogg">Webblogg</a></li>
<li><a href="/delningar">Delningar</a></li>
<li><a href="/evenemang">Evenemang</a></li>
<li><a href="/vad-jag-gor">Vad jag gör</a></li>
</ul>
</nav>
<form metod="get" action="/search">
<label>Search site <input type="search" name="q"></label>
</form>
<ul>
<li><a href="https://github.com/madr" rel="external">Github</a></li>
<li><a href="https://linkedin.com/anders-ytterstrom" rel="external">LinkedIn</a></li>
<li><a href="https://discogs.com/madr" rel="external">Discogs</a></li>
<li><a href="https://songkick.com/madr" rel="external">Songkick</a></li>
</ul>
<%= @inner_content %>
<footer>
<p><a href="https://madr.se" rel="home">madr.se</a> av Anders Englöf Ytterström, sedan 2006. <a href="/colophon">Kolofon</a>.</p>
</footer>

View file

@ -1,15 +1,13 @@
<!DOCTYPE html>
<html lang="en" class="[scrollbar-gutter:stable]">
<html lang="sv">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="csrf-token" content={get_csrf_token()} />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content={get_csrf_token()}>
<.live_title suffix=" · Phoenix Framework">
<%= assigns[:page_title] || "Mse25" %>
</.live_title>
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
</script>
<link rel="stylesheet" href={~p"/assets/app.css"}>
</head>
<body class="bg-white">
<%= @inner_content %>

View file

@ -0,0 +1,100 @@
defmodule Mse25Web.ItemController do
use Mse25Web, :controller
alias Mse25.Directus
def index(conn, _params) do
case conn.path_info |> fetch do
{:ok, item_type, item_data} ->
render(conn, item_type, assigns(item_type, item_data))
{:not_found, message} ->
render(conn, message)
end
end
defp fetch([year, slug], :article) do
case Directus.get_article(year <> "/" <> slug) do
{:ok, response} -> {:ok, :article, response}
_ -> fetch([year, slug], :link)
end
end
defp fetch([year, slug], :link) do
case Directus.get_link(year <> "/" <> slug) do
{:ok, response} -> {:ok, :link, response}
_ -> fetch([year, slug], :event)
end
end
defp fetch([year, slug], :event) do
case Directus.get_event(year <> "/" <> slug) do
{:ok, response} -> {:ok, :event, response}
not_found -> not_found
end
end
defp fetch([year, slug]) do
fetch([year, slug], :article)
end
defp fetch([slug]) do
case Directus.get_page(slug) do
{:ok, response} -> {:ok, :page, response}
not_found -> not_found
end
end
defp assigns(:article, %{
"title" => heading,
"contents" => contents,
"pubDate" => published_at,
"date_updated" => updated_at
}) do
[
heading: heading,
contents: Earmark.as_html!(contents),
published_at: published_at,
updated_at: updated_at
]
end
defp assigns(:event, %{
"title" => heading,
"contents" => contents,
"started_at" => published_at
}) do
[
heading: heading,
contents: Earmark.as_html!(contents),
published_at: published_at
]
end
defp assigns(:link, %{
"title" => heading,
"contents" => contents,
"pubDate" => published_at,
"source" => url,
"h1" => title
}) do
[
heading: heading,
contents: Earmark.as_html!(contents),
published_at: published_at,
url: url,
title: title
]
end
defp assigns(:page, %{
"title" => heading,
"contents" => contents,
"date_updated" => updated_at
}) do
[
heading: heading,
contents: Earmark.as_html!(contents),
updated_at: updated_at
]
end
end

View file

@ -0,0 +1,5 @@
defmodule Mse25Web.ItemHTML do
use Mse25Web, :html
embed_templates "item_html/*"
end

View file

@ -0,0 +1,12 @@
<article>
<header>
<date><%= @published_at %></date>
<h1><%= @heading %></h1>
</header>
<%= raw @contents %>
<footer>
<p>Skribent: Anders Englöf Ytterström. Publicerad <%= @published_at %> och senast uppdaterad <%= @updated_at %>.</p>
</footer>
</article>

View file

@ -0,0 +1,9 @@
<article>
<header>
<date><%= @published_at %></date>
<h1><%= @heading %></h1>
</header>
<%= raw @contents %>
</article>

View file

@ -0,0 +1,14 @@
<article>
<header>
<date><%= @published_at %></date>
<h1><%= @heading %></h1>
</header>
<%= raw @contents %>
<p>
Källa: <a href={@url} rel="external"><%= @title %></a>
</p>
<footer>
<p>Skribent: Anders Englöf Ytterström. Publicerad <%= @published_at %>.</p>
</footer>
</article>

View file

@ -0,0 +1,11 @@
<article>
<header>
<h1><%= @heading %></h1>
</header>
<%= raw @contents %>
<footer>
<p>Skribent: Anders Englöf Ytterström. Senast uppdaterad <%= @updated_at %>.</p>
</footer>
</article>

View file

@ -0,0 +1,12 @@
<article>
<header>
<date><%= @published_at %></date>
<h1><%= @heading %></h1>
</header>
<%= raw @contents %>
<footer>
<p>Skribent: Anders Englöf Ytterström. Publicerad <%= @published_at %> och senast uppdaterad <%= @updated_at %>.</p>
</footer>
</article>

View file

@ -18,6 +18,20 @@ defmodule Mse25Web.Router do
pipe_through :browser
get "/", PageController, :home
# get "/evenemang", EventController, :index
# get "/kommande-evenemang.ics", EventController, :calendar
# get "/event-map.js", EventController, :interactive_map
# get "/webblogg", ArticleController, :index
# get "/webblogg/prenumerera.xml", ArticleController, :feed
# get "/delningar", ShareController, :index
# get "/delningar/prenumerera.xml", ShareController, :feed
# get "/:year", TimelineController, :annual
# get "/prenumerera.xml", TimelineController, :feed
get "/*path", ItemController, :index
end
# Other scopes may use custom stacks.

View file

@ -45,7 +45,8 @@ defmodule Mse25.MixProject do
{:jason, "~> 1.2"},
{:dns_cluster, "~> 0.1.1"},
{:bandit, "~> 1.5"},
{:req, "~> 0.5.0"}
{:req, "~> 0.5.0"},
{:earmark, "~> 1.4"}
]
end

View file

@ -2,6 +2,7 @@
"bandit": {:hex, :bandit, "1.5.7", "6856b1e1df4f2b0cb3df1377eab7891bec2da6a7fd69dc78594ad3e152363a50", [:mix], [{:hpax, "~> 1.0.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f2dd92ae87d2cbea2fa9aa1652db157b6cba6c405cb44d4f6dd87abba41371cd"},
"castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"},
"dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"},
"earmark": {:hex, :earmark, "1.4.47", "7e7596b84fe4ebeb8751e14cbaeaf4d7a0237708f2ce43630cfd9065551f94ca", [:mix], [], "hexpm", "3e96bebea2c2d95f3b346a7ff22285bc68a99fbabdad9b655aa9c6be06c698f8"},
"esbuild": {:hex, :esbuild, "0.8.1", "0cbf919f0eccb136d2eeef0df49c4acf55336de864e63594adcea3814f3edf41", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "25fc876a67c13cb0a776e7b5d7974851556baeda2085296c14ab48555ea7560f"},
"expo": {:hex, :expo, "1.0.1", "f9e2f984f5b8d195815d52d0ba264798c12c8d2f2606f76fa4c60e8ebe39474d", [:mix], [], "hexpm", "f250b33274e3e56513644858c116f255d35c767c2b8e96a512fe7839ef9306a1"},
"file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},