Current section
Files
Jump to
Current section
Files
src/scriptorium/rendering/views/single_post.gleam
import gleam/list
import gleam/option
import scriptorium/compiler.{type CompiledPost}
import scriptorium/config.{type Configuration}
import scriptorium/models/database.{type Database}
import scriptorium/models/post.{type Post}
import scriptorium/utils/date
import scriptorium/utils/luxon
import scriptorium/utils/time
import lustre/attribute.{attribute, class, href}
import lustre/element.{type Element, text}
import lustre/element/html.{
a, article, div, footer, h2, header, li, nav, p, time, ul,
}
/// Generate a view that renders a full post.
pub fn full_view(db: Database, config: Configuration) {
view(_, True, db, config)
}
/// Generate a view that renders a post that's inside of a list.
pub fn list_view(db: Database, config: Configuration) {
view(_, False, db, config)
}
fn view(post: CompiledPost, is_full: Bool, _db: Database, config: Configuration) {
let post_url =
config.paths.html(config.paths.root <> config.paths.single_post(post.orig))
let content = case post.content.short, is_full {
option.Some(content), False -> content
_, _ -> post.content.full
}
article([class("post")], [
header([], [
wrap_heading(h2([], [text(post.orig.title)]), post_url, is_full),
p([class("post__time")], [
text("Posted on "),
post_time(post.orig),
text("."),
]),
nav([attribute("aria-label", "Tags")], [
ul(
[],
list.map(post.orig.tags, fn(tag) {
li([], [
a(
[
href(config.paths.html(
config.paths.root <> config.paths.tag(tag),
)),
],
[text(tag)],
),
])
}),
),
]),
]),
div([attribute("dangerous-unescaped-html", content)], []),
case is_full, post.content.short {
True, _ | False, option.None -> element.none()
False, option.Some(_) ->
footer([], [a([href(post_url)], [text("Read more…")])])
},
])
}
fn wrap_heading(heading: Element(Nil), post_url: String, is_full: Bool) {
case is_full {
True -> heading
False -> a([href(post_url)], [heading])
}
}
fn post_time(post: Post) {
let post_date = post.get_date(post)
let date_str = date.format(post_date)
let time_str =
option.unwrap(
option.map(post.get_time(post), fn(t) { ", " <> time.format(t) }),
"",
)
let human_str = date_str <> time_str
let datetime_str = case post.get_luxon(post) {
option.Some(lx) -> luxon.to_iso(lx)
option.None -> date.format_iso(post_date)
}
time([attribute("datetime", datetime_str)], [text(human_str)])
}