Files
blog.stevanfreeborn.com/src/Blog/Components/Pages/Post.razor
T
Stevan Freeborn dd7e7e1783 feat: add individual post page (#4)
* chore: add markdown lint to ignore no h1 heading in markdown files

* chore: update example settings

* chore: update example development env file

* fix: remove fixed 100% height on container

* feat: add individual post page

* chore: update README.md

* feat: add links to headings in post and add copy button to code blocks

* tests: add tests for get post async method

* tests: remove heading for test blogs

* fix: only add links to headings in markdown body

* tests: add tests for post page

* tests: add test to check for blog content

* docs: add jsdoc comments

* chore: fix incorrect env variable format in example

* fix: don't render anything if blog post is null

* fix: don't render anything until posts are fetched

* chore: add docker compose file for prod
2024-04-11 00:58:22 -05:00

82 lines
1.9 KiB
Plaintext

@page "/{slug:nonfile}"
@implements IAsyncDisposable
@inject IPostService PostService
@inject NavigationManager NavigationManager
@inject IJSRuntime JSRuntime
@inject ILogger<Post> Logger
@if (BlogPost is null)
{
return;
}
else
{
<PageTitle>@BlogPost.Title - journal</PageTitle>
<HeadContent>
<meta name="description" content="@BlogPost.Lead">
<link rel="stylesheet" href="markdown.css">
<link rel="stylesheet" href="prism.css">
<script src="prism.js"></script>
<script src="https://cdn.jsdelivr.net/npm/anchor-js/anchor.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.11/dist/clipboard.min.js"></script>
</HeadContent>
<article>
<div class="heading-container">
<h2>@BlogPost.Title</h2>
<p>@BlogPost.PublishedAt.ToString("MMMM d, yyyy")</p>
</div>
<div class="markdown-body">
@((MarkupString)BlogPost.Content)
</div>
</article>
}
@code {
[Parameter]
public string Slug { get; set; } = string.Empty;
private PostWithContent? BlogPost;
private IJSObjectReference? module;
protected override async Task OnInitializedAsync()
{
var post = await PostService.GetPostAsync(Slug);
if (post is null)
{
NavigationManager.NavigateTo("/Error/404");
return;
}
BlogPost = post;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./Components/Pages/Post.razor.js");
await module.InvokeVoidAsync("addAnchors");
await module.InvokeVoidAsync("addClipboard");
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
try
{
if (module is not null)
{
await module.DisposeAsync();
}
}
catch (Exception ex) when (ex is JSDisconnectedException)
{
}
catch (Exception ex)
{
Logger.LogError(ex, "Error disposing of the module");
}
}
}