diff --git a/lib/Config.rakumod b/lib/Config.rakumod index 1c63d24..767bd1e 100644 --- a/lib/Config.rakumod +++ b/lib/Config.rakumod @@ -10,14 +10,6 @@ use DB::Post; unit class Config; -sub show-html($html) { - my $out = "$html"; - # Work around HTML::Functional automatically putting newlines between tags - $out ~~ s:g/'' \v+ ''/<\/i>/; - $out ~~ s:g/\v+ ''/<\/a>/; - $out -} - # TODO: Support GFM admonitions method generate-post(Int:D $id, Post:D $post, $db) { my $meta = $db.meta; @@ -45,34 +37,13 @@ method generate-post(Int:D $id, Post:D $post, $db) { show-html $html } -method generate-blurb(Int:D $id, $db) { - my $post = $db.posts{$id}; - my $desc = $post.description; - my $link = post-link $id, $post; - div :class, [ - div :class, [ - a :href($link), span [ - h2 $post.title; - ]; - ]; - post-info $id, $post, $db; - if $desc ~~ Str:D { - div :class, [ - p $post.description; - ]; - } else { - [] - } - ] -} - method generate-index($db) { my @most-recent = $db.sorted-posts .head(10) .grep(!*.value.hidden) .map(-> $pair { - self.generate-blurb: $pair.key, $db + generate-blurb $pair.key, $db }); my $head = generate-head($db.meta); @@ -97,7 +68,7 @@ method generate-archive($db) { $db.sorted-posts .grep(!*.value.hidden) .map(-> $pair { - self.generate-blurb: $pair.key, $db + generate-blurb $pair.key, $db }); my $head = generate-head($db.meta); diff --git a/lib/DB.rakumod b/lib/DB.rakumod index f092775..4fc9288 100644 --- a/lib/DB.rakumod +++ b/lib/DB.rakumod @@ -14,6 +14,7 @@ use DB::BlogMeta; use DB::MarkdownPost; use DB::IdrisPost; use DB::PlaceholderPost; +use Render::Series; use Atom; use Config; @@ -140,6 +141,13 @@ class PostDB { $config.generate-tag-page(self, $tag); } # TODO: Generate the series pages + my $series-dir = $out-dir.add('series/'); + mkdir $series-dir unless $series-dir.e; + for %!series.kv -> $key, $value { + $series-dir.add("$key.html").spurt: + series-page($key, self); + } + # TODO: Generate the main series page # Render the rss/atom feed my $atom-path = $out-dir.add('atom.xml'); my $atom = posts-to-atom self; diff --git a/lib/DB/Series.rakumod b/lib/DB/Series.rakumod index 6510a7b..59fd964 100644 --- a/lib/DB/Series.rakumod +++ b/lib/DB/Series.rakumod @@ -22,3 +22,14 @@ method contains-post(Int:D $post-id --> Bool:D) { False } } + +#| Returns the date of the lastest post +method latest-post($db) { + my @posts = @!post-ids.map(-> $i {$db.posts{$i}}); + if @posts { + my $most-recent-post = @posts.max(*.posted-at); + $most-recent-post.posted-at + } else { + Nil + } +} diff --git a/lib/Render/Post.rakumod b/lib/Render/Post.rakumod index a196edf..8bb6f97 100644 --- a/lib/Render/Post.rakumod +++ b/lib/Render/Post.rakumod @@ -131,3 +131,24 @@ sub post-header(Int:D $id, Post:D $post, $db) is export { post-info $id, $post, $db; ] } + +sub generate-blurb(Int:D $id, $db) is export { + my $post = $db.posts{$id}; + my $desc = $post.description; + my $link = post-link $id, $post; + div :class, [ + div :class, [ + a :href($link), span [ + h2 $post.title; + ]; + ]; + post-info $id, $post, $db; + if $desc ~~ Str:D { + div :class, [ + p $post.description; + ]; + } else { + [] + } + ] +} diff --git a/lib/Render/Series.rakumod b/lib/Render/Series.rakumod new file mode 100644 index 0000000..0db9a55 --- /dev/null +++ b/lib/Render/Series.rakumod @@ -0,0 +1,105 @@ +use v6.e.PREVIEW; +unit module Render::Series; + +use Render::Util; +use Render::Head; +use Render::Post; +use DB::Post; +use DB::Series; + +use HTML::Functional; + +sub series-date(Series:D $series, $db) is export { + my $datetime = $series.latest-post: $db; + if $datetime { + my $timestamp = sprintf( + "%s %02d:%02d%s", + $datetime.yyyy-mm-dd, + ($datetime.hour % 12) || 12, + $datetime.minute, + $datetime.hour < 12 ?? 'am' !! 'pm' + ); + + div :class, :title("Latest post made at $timestamp"), [ + icon 'calendar'; + ' '; + $datetime.Date.Str + ] + } else { + [] + } +} + +sub series-read-time(Series:D $series, $db) is export { + my @readtimes = $series.post-ids.map(-> $i {$db.posts{$i}.readtimes}); + my ($slow, $average, $fast) = 0, 0, 0; + for @readtimes -> ($s, $a, $f) { + $slow += $s; + $average += $a; + $fast += $f; + } + div :class, :title, [ + icon 'timer'; + ' '; + mins-to-string $slow; + ' '; + '/'; + ' '; + mins-to-string $average; + ' '; + '/'; + ' '; + mins-to-string $fast; + ] +} + +sub series-count(Series:D $series, $db) is export { + my $count = $series.post-ids.elems; + div :class, :title("Series has $count articles"), [ + icon 'add-to-queue'; + ' '; + "$count articles"; + ] +} + +sub series-info(Series:D $series, $db) is export { + div :class, [ + series-date $series, $db; + series-read-time $series, $db; + series-count $series, $db; + ] +} + +sub series-header(Series:D $series, $db) is export { + header :class, [ + div :class, [ + h1 $series.title; + ]; + div :class, [ + p $series.desc; + ]; + series-info $series, $db; + ] +} + +sub series-page(Int:D $series-id, $db) is export { + my $meta = $db.meta; + my $series = $db.series{$series-id}; + my $head = generate-head($meta, $series.title, $series.desc); + my $body = + body [ + site-header $meta; + article :class, [ + series-header $series, $db; + div :class, + $series.post-ids.map(*.&generate-blurb($db)); + ] + ]; + + my $html = html :lang, [ + $head; + $body + ]; + + show-html $html; +} diff --git a/lib/Render/Util.rakumod b/lib/Render/Util.rakumod index 05630a7..79ece58 100644 --- a/lib/Render/Util.rakumod +++ b/lib/Render/Util.rakumod @@ -5,6 +5,15 @@ use DB::Post; use HTML::Functional; +sub show-html($html) is export { + my $out = "$html"; + # Work around HTML::Functional automatically putting newlines between tags + $out ~~ s:g/'' \v+ ''/<\/i>/; + $out ~~ s:g/\v+ ''/<\/a>/; + $out ~~ s:g/',' \v+ ' * { +.post-info > *, .series-info > * { background-color: var(--bg-2); } blockquote { diff --git a/resources/main.css b/resources/main.css index 5e890fc..7a78e3f 100644 --- a/resources/main.css +++ b/resources/main.css @@ -27,13 +27,13 @@ } /* Main Body and Post Flexboxs */ -body, .post { +body, .post, .series { display: flex; flex-direction: column; align-items: center; gap: var(--box-gap); } -.post { +.post, .series { width: 100%; } @@ -75,7 +75,7 @@ body, .post { /* Style the post header, body, and blurbs */ /* TODO: Style footnotes and get footnote hover working */ -.post-header, .post-body { +.post-header, .post-body, .series-header { display: flex; flex-direction: column; align-items: center; @@ -90,11 +90,11 @@ body, .post { margin: auto var(--box-margin-horz); align-self: stretch; } -.post-title h1 { +.post-title h1, .series-title h1 { margin-top: 0px; margin-bottom: 0px; } -.post-info { +.post-info, .series-info { display: flex; flex-direction: row; align-items: center; @@ -108,7 +108,7 @@ body, .post { .post-body h2, .post-body h3, .post-body h4 { text-align: center; } -.post-blurbs { +.post-blurbs, .series-blurbs { display: flex; flex-direction: column; align-items: center; @@ -116,6 +116,7 @@ body, .post { max-width: var(--content-width); padding: var(--box-padding-vert) var(--box-padding-horz); border-radius: var(--box-radius); + box-sizing: border-box; } .post-blurb { width: 100%; @@ -128,7 +129,7 @@ body, .post { flex-direction: column; box-sizing: border-box; } -.post-info > * { +.post-info > *, .series-info > * { padding: 0.25em; border-radius: 0.25em; }