use v6.e.PREVIEW; use HTML::Functional; use DB::BlogMeta; use DB::Post; unit class Config; method generate-head($title, BlogMeta:D $meta, $description?) { head [ meta :charset; meta :name, :content; meta :author :content; do if $title ~~ Str:D { title "$title — {$meta.title}"; } else { title $meta.title; } # Add description, if one exists do if $description ~~ Str:D { meta :description :content($description) } else { [] } # Preconnect to all our resource sources link :rel :href; link :rel :href; link :rel :href :crossorigin; link :rel :href; # Load fonts, Iosevka for code, Open Sans for content, and boxicons for # icons link :rel, :href; link :rel, :href; link :rel, :href; # Inline our style sheets style %?RESOURCES.slurp; style %?RESOURCES.slurp; ]; } method site-header(BlogMeta:D $meta) { header :class, [ div :class, [ # TODO: Use a real image here $meta.title ]; div :class, [ $meta.tagline ]; div :class, [ a :href, [ icon 'home'; ' '; span [ 'Home'; ]; ]; a :href, [ icon 'archive'; ' '; span [ 'Archive'; ]; ]; a :href, [ icon 'info-circle'; ' '; span [ 'About'; ]; ]; a :href, [ icon 'rss'; ' '; span [ 'Feed'; ]; ]; ]; ] } method post-date(Post:D $post) { my $datetime = $post.posted-at; 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("Posted At $timestamp"), [ icon 'time'; ' '; $timestamp ] } method post-edit(Post:D $post) { return [] unless $post.edited-at.elems; my $datetime = $post.edited-at.max; 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("Laste Edited At $timestamp"), [ icon 'edit'; ' '; $timestamp ] } sub mins-to-string($mins) { if $mins < 60 { $mins.Str ~ "m" } else { my $h = $mins div 60; my $m = $mins mod 60; $h.Str ~ "h" ~ $m.Str ~ "m" } } method post-read-time(Post:D $post) { my ($slow, $average, $fast) = $post.readtimes; div :class, :title, [ icon 'timer'; ' '; mins-to-string $slow; ' '; '/'; ' '; mins-to-string $average; ' '; '/'; ' '; mins-to-string $fast; ] } method post-info(Post:D $post) { div :class, [ self.post-date: $post; self.post-edit: $post; self.post-read-time: $post; # TODO: Add tags once we have support for that ]; } method post-header(Post:D $post) { header :class, [ div :class, [ h1 $post.title; ]; self.post-info: $post; ] } # TODO: Support GFM admonitions method generate-post(Post:D $post, BlogMeta:D $meta) { my $content = $post.render-html; my $head = self.generate-head($post.title, $meta, $post.description); my $body = body [ self.site-header: $meta; article :class, [ self.post-header: $post; div :class, [ $content; ] ] ]; # TODO: Setup footer # my $footer; my $html = html :lang, [ $head, $body ]; "$html" } method generate-blurb(Int:D $id, $db) { my $post = $db.posts{$id}; my $desc = $post.description; my @slugs = $post.all-slugs; # Use the primary slug if there is one, the id if there isn't my $link = do if @slugs.elems > 0 { "/posts/by-slug/{@slugs[0]}.html" } else { "/posts/by-id/$id.html" } div :class, [ div :class, [ a :href($link), span [ h2 $post.title; ]; ]; self.post-info: $post; 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 }); my $head = self.generate-head(Nil, $db.meta); my $body = body [ self.site-header: $db.meta; div :class, [ h1 "Recent Posts" ], @most-recent; ]; my $html = html :lang, [ $head, $body ]; "$html" } method generate-archive($db) { my @most-recent = $db.sorted-posts .grep(!*.value.hidden) .map(-> $pair { self.generate-blurb: $pair.key, $db }); my $head = self.generate-head(Nil, $db.meta); my $body = body [ self.site-header: $db.meta; div :class, [ h1 "All Posts" ], @most-recent; ]; my $html = html :lang, [ $head, $body ]; "$html" } sub icon($icon) { i(:class("bx bx-$icon")) }