feat: delegate blog listing to BlogService

This commit is contained in:
Oliver Booth 2023-08-08 01:30:32 +01:00
parent 79a45643cb
commit 83e5757429
Signed by: oliverbooth
GPG Key ID: 725DB725A0D9EE61
4 changed files with 119 additions and 15 deletions

View File

@ -1,16 +1,73 @@
@page @page
@using Humanizer
@using OliverBooth.Data.Blog @using OliverBooth.Data.Blog
@using OliverBooth.Services
@model OliverBooth.Pages.Blog.Index @model OliverBooth.Pages.Blog.Index
@inject BlogService BlogService
@foreach (BlogPost post in Model.BlogPosts) @foreach (BlogPost post in BlogService.AllPosts)
{ {
<h2> Author? author = Model.GetAuthor(post);
<a asp-page="/blog/article" asp-route-year="@post.Published.Year" asp-route-month="@post.Published.Month.ToString().PadLeft(2, '0')" asp-route-slug="@post.Slug">@post.Title</a> DateTimeOffset published = post.Published;
</h2> var year = published.ToString("yyyy");
<p class="text-muted">@post.Published.ToString("MMMM dd, yyyy") &bull; @Model.GetAuthor(post)?.Name</p> var month = published.ToString("MM");
<p>@Html.Raw(Model.SanitizeContent(Model.TrimContent(post.Body, out bool trimmed)))</p> var day = published.ToString("dd");
if (trimmed)
{ bool isLegacyPost = post.WordPressId is not null;
<p><a asp-page="/blog/article" asp-route-year="@post.Published.Year" asp-route-month="@post.Published.Month.ToString().PadLeft(2, '0')" asp-route-slug="@post.Slug">Read more...</a></p> string disqusDomain = isLegacyPost ? "https://blog.oliverbooth.dev" : "https://oliverbooth.dev/blog";
} string disqusId = isLegacyPost ? $"{post.WordPressId} {disqusDomain}/?p={post.WordPressId}" : post.Id.ToString();
<div class="card blog-card" style="margin-bottom: 50px;">
<div class="card-body">
<h2>
<a asp-page="/blog/article"
asp-route-year="@year"
asp-route-month="@month"
asp-route-day="@day"
asp-route-slug="@post.Slug">
@post.Title
</a>
</h2>
<p class="text-muted">
<img class="blog-author-icon" src="https://gravatar.com/avatar/@author?.AvatarHash?s=28">
@author?.Name
&bull;
<abbr data-bs-toggle="tooltip" data-bs-title="@post.Published.ToString("f")">
@post.Published.Humanize()
</abbr>
@if (post.EnableComments)
{
<span>&bull;</span>
<a asp-page="/blog/article"
asp-route-year="@year"
asp-route-month="@month"
asp-route-day="@day"
asp-route-slug="@post.Slug"
asp-fragment="disqus_thread" data-disqus-identifier="@disqusId">
0 Comments
</a>
}
</p>
<p>@Html.Raw(Model.SanitizeContent(Model.TrimContent(post.Body, out bool trimmed)))</p>
<article>
@if (trimmed)
{
<p>
<a asp-page="/blog/article"
asp-route-year="@year"
asp-route-month="@month"
asp-route-day="@day"
asp-route-slug="@post.Slug">
Read more...
</a>
</p>
}
</article>
</div>
</div>
<script id="dsq-count-scr" src="https://oliverbooth-dev.disqus.com/count.js" async></script>
} }

View File

@ -1,4 +1,5 @@
using Humanizer; using Humanizer;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using OliverBooth.Data; using OliverBooth.Data;
@ -19,7 +20,7 @@ public class Index : PageModel
public string SanitizeContent(string content) public string SanitizeContent(string content)
{ {
content = content.Replace("<more>", string.Empty); content = content.Replace("<!--more-->", string.Empty);
while (content.Contains("\n\n")) while (content.Contains("\n\n"))
{ {
@ -32,20 +33,33 @@ public class Index : PageModel
public string TrimContent(string content, out bool trimmed) public string TrimContent(string content, out bool trimmed)
{ {
ReadOnlySpan<char> span = content.AsSpan(); ReadOnlySpan<char> span = content.AsSpan();
int moreIndex = span.IndexOf("<more>", StringComparison.Ordinal); int moreIndex = span.IndexOf("<!--more-->", StringComparison.Ordinal);
trimmed = moreIndex != -1 || span.Length > 256; trimmed = moreIndex != -1 || span.Length > 256;
return moreIndex != -1 ? span[..moreIndex].Trim().ToString() : content.Truncate(256); return moreIndex != -1 ? span[..moreIndex].Trim().ToString() : content.Truncate(256);
} }
public Author? GetAuthor(BlogPost post) public Author? GetAuthor(BlogPost post)
{ {
using BlogContext context = _dbContextFactory.CreateDbContext(); using BlogContext context = _dbContextFactory.CreateDbContext();
return context.Authors.FirstOrDefault(a => a.Id == post.AuthorId); return context.Authors.FirstOrDefault(a => a.Id == post.AuthorId);
} }
public void OnGet() public IActionResult OnGet([FromQuery(Name = "p")] int? postId = null)
{ {
using BlogContext context = _dbContextFactory.CreateDbContext(); using BlogContext context = _dbContextFactory.CreateDbContext();
BlogPosts = context.BlogPosts.ToArray(); if (postId is null)
{
BlogPosts = context.BlogPosts.ToArray();
return Page();
}
BlogPost? post = context.BlogPosts.FirstOrDefault(p => p.WordPressId == postId);
if (post is not null)
{
return Redirect($"/blog/{post.Published:yyyy/MM}/{post.Slug}");
}
return NotFound();
} }
} }

View File

@ -27,6 +27,7 @@ builder.Services.AddSingleton(new MarkdownPipelineBuilder()
builder.Services.AddDbContextFactory<BlogContext>(); builder.Services.AddDbContextFactory<BlogContext>();
builder.Services.AddDbContextFactory<WebContext>(); builder.Services.AddDbContextFactory<WebContext>();
builder.Services.AddSingleton<BlogService>();
builder.Services.AddRazorPages().AddRazorRuntimeCompilation(); builder.Services.AddRazorPages().AddRazorRuntimeCompilation();
builder.Services.AddControllersWithViews(); builder.Services.AddControllersWithViews();
builder.Services.AddRouting(options => options.LowercaseUrls = true); builder.Services.AddRouting(options => options.LowercaseUrls = true);

View File

@ -0,0 +1,32 @@
using Microsoft.EntityFrameworkCore;
using OliverBooth.Data;
using OliverBooth.Data.Blog;
namespace OliverBooth.Services;
public sealed class BlogService
{
private IDbContextFactory<BlogContext> _dbContextFactory;
/// <summary>
/// Initializes a new instance of the <see cref="BlogService" /> class.
/// </summary>
/// <param name="dbContextFactory">The <see cref="IDbContextFactory{TContext}" />.</param>
public BlogService(IDbContextFactory<BlogContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
/// <summary>
/// Gets a read-only view of all blog posts.
/// </summary>
/// <returns>A read-only view of all blog posts.</returns>
public IReadOnlyCollection<BlogPost> AllPosts
{
get
{
using BlogContext context = _dbContextFactory.CreateDbContext();
return context.BlogPosts.OrderByDescending(p => p.Published).ToArray();
}
}
}