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 {
"/posts/by-slug/{@slugs[*-1]}.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"))
}