feat: use Handlebars to render template excerpt card

This commit is contained in:
Oliver Booth 2023-08-10 14:36:51 +01:00
parent 3868fcbaa8
commit 217c1d660e
Signed by: oliverbooth
GPG Key ID: 725DB725A0D9EE61
3 changed files with 47 additions and 119 deletions

View File

@ -1,9 +1,5 @@
@page @page
@using Humanizer
@using OliverBooth.Data.Blog
@using OliverBooth.Services
@model OliverBooth.Pages.Blog.Index @model OliverBooth.Pages.Blog.Index
@inject BlogService BlogService
<div id="all-blog-posts"> <div id="all-blog-posts">
<div id="blog-loading-spinner" class="d-flex justify-content-center"> <div id="blog-loading-spinner" class="d-flex justify-content-center">
@ -13,67 +9,33 @@
</div> </div>
</div> </div>
@foreach (BlogPost post in ArraySegment<BlogPost>.Empty /*BlogService.AllPosts*/) <script id="blog-post-template" type="text/x-handlebars-template">
{
BlogService.TryGetAuthor(post, out Author? author);
DateTimeOffset published = post.Published;
DateTimeOffset timestamp = post.Updated ?? published;
bool isUpdated = post.Updated.HasValue;
var year = published.ToString("yyyy");
var month = published.ToString("MM");
var day = published.ToString("dd");
<div class="card blog-card" style="margin-bottom: 50px;">
<div class="card-body"> <div class="card-body">
<h2> <h2>
<a asp-page="/blog/article" <a href="{{post.url}}"> {{post.title}}</a>
asp-route-year="@year"
asp-route-month="@month"
asp-route-day="@day"
asp-route-slug="@post.Slug">
@post.Title
</a>
</h2> </h2>
<p class="text-muted"> <p class="text-muted">
<img class="blog-author-icon" src="https://gravatar.com/avatar/@author?.AvatarHash?s=28" alt="@author?.Name"> <img class="blog-author-icon" src="{{author.avatar}}" alt="{{author.name}}">
@author?.Name &bull; <span>{{author.name}}<span>
<abbr data-bs-toggle="tooltip" data-bs-title="@timestamp.ToString("f")">
@(isUpdated ? "Updated" : "Published") @timestamp.Humanize()
</abbr>
@if (post.EnableComments)
{
<span> &bull; </span> <span> &bull; </span>
<a asp-page="/blog/article" <span title="{{ post.date }}">{{ post.date_humanized }}</span>
asp-fragment="disqus_thread" {{#if post.enable_comments}}
asp-route-year="@year" <span> &bull; </span>
asp-route-month="@month" <a href="{{post.url}}#disqus_thread" data-disqus-identifier="{{post.disqus_identifier}}">
asp-route-day="@day"
asp-route-slug="@post.Slug"
data-disqus-identifier="@post.GetDisqusIdentifier()">
0 Comments 0 Comments
</a> </a>
} {{/if}}
</p> </p>
<p>@Html.Raw(BlogService.GetExcerpt(post, out bool trimmed))</p> <p>{{{post.excerpt}}}</p>
@if (trimmed) {{#if post.trimmed}}
{
<p> <p>
<a asp-page="/blog/article" <a href="{{post.url}}">
asp-route-year="@year"
asp-route-month="@month"
asp-route-day="@day"
asp-route-slug="@post.Slug">
Read more... Read more...
</a> </a>
</p> </p>
} {{/if}}
</div> </div>
</div> </script>
<script id="dsq-count-scr" src="https://oliverbooth-dev.disqus.com/count.js" async></script>
}

View File

@ -2,6 +2,10 @@
public static get blogPostContainer(): HTMLDivElement { public static get blogPostContainer(): HTMLDivElement {
return document.querySelector("#all-blog-posts"); return document.querySelector("#all-blog-posts");
} }
public static get blogPostTemplate(): HTMLDivElement {
return document.querySelector("#blog-post-template");
}
} }
export default UI; export default UI;

View File

@ -4,10 +4,12 @@ import UI from "./UI";
declare const bootstrap: any; declare const bootstrap: any;
declare const katex: any; declare const katex: any;
declare const Handlebars: any;
(() => { (() => {
const blogPostContainer = UI.blogPostContainer; const blogPostContainer = UI.blogPostContainer;
if (blogPostContainer) { if (blogPostContainer) {
const template = Handlebars.compile(UI.blogPostTemplate.innerHTML);
API.getBlogPostCount().then(async (count) => { API.getBlogPostCount().then(async (count) => {
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const posts = await API.getBlogPosts(i, 5); const posts = await API.getBlogPosts(i, 5);
@ -21,63 +23,23 @@ declare const katex: any;
card.classList.add("animate__fadeIn"); card.classList.add("animate__fadeIn");
card.style.marginBottom = "50px"; card.style.marginBottom = "50px";
const cardBody = document.createElement("div"); const body = template({
cardBody.classList.add("card-body"); post: {
card.appendChild(cardBody); title: post.title,
excerpt: post.excerpt,
const postTitle = document.createElement("h2"); url: post.url,
postTitle.classList.add("card-title"); date: TimeUtility.formatRelativeTimestamp(post.published),
cardBody.appendChild(postTitle); date_humanized: `${post.updated ? "Updated" : "Published"} ${post.humanizedTimestamp}`,
enable_comments: post.commentsEnabled,
const titleLink = document.createElement("a"); disqus_identifier: post.identifier,
titleLink.href = post.url; trimmed: post.trimmed,
titleLink.innerText = post.title; },
postTitle.appendChild(titleLink); author: {
name: author.name,
const metadata = document.createElement("p"); avatar: `https://gravatar.com/avatar/${author.avatarHash}?s=28`,
metadata.classList.add("text-muted");
cardBody.appendChild(metadata);
const authorIcon = document.createElement("img");
authorIcon.classList.add("blog-author-icon");
authorIcon.src = `https://gravatar.com/avatar/${author.avatarHash}?s=28`;
authorIcon.alt = author.name;
metadata.appendChild(authorIcon);
const authorName = document.createElement("span");
authorName.innerHTML = ` ${author.name} &bull; `;
metadata.appendChild(authorName);
const postDate = document.createElement("span");
if (post.updated) {
postDate.innerHTML = `Updated ${TimeUtility.formatRelativeTimestamp(post.updated)}`;
} else {
postDate.innerHTML = `Published ${TimeUtility.formatRelativeTimestamp(post.published)}`;
}
metadata.appendChild(postDate);
if (post.commentsEnabled) {
const bullet = document.createElement("span");
bullet.innerHTML = " &bull; ";
metadata.appendChild(bullet);
const commentCount = document.createElement("a");
commentCount.href = post.url + "#disqus_thread";
commentCount.innerHTML = "0 Comments";
commentCount.setAttribute("data-disqus-identifier", post.identifier);
metadata.appendChild(commentCount);
}
const postExcerpt = document.createElement("p");
postExcerpt.innerHTML = post.excerpt;
cardBody.appendChild(postExcerpt);
if (post.trimmed) {
const readMoreLink = document.createElement("a");
readMoreLink.href = post.url;
readMoreLink.innerHTML = "Read more &hellip;";
cardBody.appendChild(readMoreLink);
} }
});
card.innerHTML = body.trim();
blogPostContainer.appendChild(card); blogPostContainer.appendChild(card);
} }