Added human readable article links
This commit is contained in:
parent
e33ffd463e
commit
3909fbdfe0
23
Wave/Components/ArticleLink.razor
Normal file
23
Wave/Components/ArticleLink.razor
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
@using Wave.Data
|
||||||
|
@using System.Net
|
||||||
|
@using System.Web
|
||||||
|
|
||||||
|
<a href="@Link" @attributes="AdditionalAttributes">
|
||||||
|
@ChildContent
|
||||||
|
</a>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public required Article Article { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? ChildContent { get; set; }
|
||||||
|
|
||||||
|
private string TitleEncoded => Uri.EscapeDataString(Article.Title.ToLowerInvariant()).Replace("-", "+").Replace("%20", "-");
|
||||||
|
private string Link =>
|
||||||
|
Article.PublishDate.Year >= 9999
|
||||||
|
? $"/article/{Article.Id}"
|
||||||
|
: $"/{Article.PublishDate.Year}/{Article.PublishDate.Month:D2}/{Article.PublishDate.Day:D2}/{TitleEncoded}";
|
||||||
|
|
||||||
|
[Parameter(CaptureUnmatchedValues = true)]
|
||||||
|
public Dictionary<string, object>? AdditionalAttributes { get; set; }
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
@using Wave.Data
|
@using Wave.Data
|
||||||
|
|
||||||
<a href="/article/@Article.Id">
|
<ArticleLink Article="Article">
|
||||||
<article class="card card-compact bg-base-200 rounded-none h-full">
|
<article class="card card-compact bg-base-200 rounded-none h-full">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h3 class="card-title line-clamp-2">@Article.Title</h3>
|
<h3 class="card-title line-clamp-2">@Article.Title</h3>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</a>
|
</ArticleLink>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter]
|
[Parameter]
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
@page "/article/{id:guid}"
|
@page "/article/{id:guid}"
|
||||||
|
@page "/{year:int:min(1)}/{month:int:range(1,12)}/{day:int:range(1,31)}/{titleEncoded}"
|
||||||
@using Microsoft.EntityFrameworkCore
|
@using Microsoft.EntityFrameworkCore
|
||||||
@using Wave.Data
|
@using Wave.Data
|
||||||
@using System.Security.Claims
|
@using System.Security.Claims
|
||||||
@using System.Diagnostics.CodeAnalysis
|
@using System.Diagnostics.CodeAnalysis
|
||||||
|
@using System.Net
|
||||||
|
@using System.Web
|
||||||
@using Microsoft.Extensions.Options
|
@using Microsoft.Extensions.Options
|
||||||
|
|
||||||
@inject IDbContextFactory<ApplicationDbContext> ContextFactory;
|
@inject IDbContextFactory<ApplicationDbContext> ContextFactory
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@inject IOptions<Customization> Customizations
|
@inject IOptions<Customization> Customizations
|
||||||
@inject IStringLocalizer<ArticleView> Localizer
|
@inject IStringLocalizer<ArticleView> Localizer
|
||||||
|
@ -71,9 +74,27 @@
|
||||||
[CascadingParameter(Name = "TitlePrefix")]
|
[CascadingParameter(Name = "TitlePrefix")]
|
||||||
private string TitlePrefix { get; set; } = default!;
|
private string TitlePrefix { get; set; } = default!;
|
||||||
|
|
||||||
|
#region Route Parameters
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Guid Id { get; set; }
|
public Guid? Id { get; set; }
|
||||||
private Article? Article { get; set; } = default!;
|
[Parameter]
|
||||||
|
public int? Year { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public int? Month { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public int? Day { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public string? TitleEncoded { get; set; }
|
||||||
|
|
||||||
|
private DateTimeOffset? Date =>
|
||||||
|
Year is {} y && Month is {} m && Day is {} d
|
||||||
|
? new DateTimeOffset(new DateTime(y, m, d)) :
|
||||||
|
null;
|
||||||
|
private string? Title => TitleEncoded is null ? null : Uri.UnescapeDataString(TitleEncoded.Replace("-", "%20").Replace("+", "-"));
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
private Article? Article { get; set; }
|
||||||
|
|
||||||
private Article GetArticlePublic() {
|
private Article GetArticlePublic() {
|
||||||
if (Article is null) throw new ApplicationException("Article not found.");
|
if (Article is null) throw new ApplicationException("Article not found.");
|
||||||
|
@ -113,11 +134,19 @@
|
||||||
protected override void OnInitialized() {
|
protected override void OnInitialized() {
|
||||||
// We need blocking calls here, bc otherwise Blazor will execute Render in parallel,
|
// We need blocking calls here, bc otherwise Blazor will execute Render in parallel,
|
||||||
// running into a null pointer on the Article property and panicking
|
// running into a null pointer on the Article property and panicking
|
||||||
|
if (Id is not null) {
|
||||||
using var context = ContextFactory.CreateDbContext();
|
using var context = ContextFactory.CreateDbContext();
|
||||||
Article = context.Set<Article>()
|
Article = context.Set<Article>()
|
||||||
.Include(a => a.Author)
|
.Include(a => a.Author)
|
||||||
.Include(a => a.Reviewer)
|
.Include(a => a.Reviewer)
|
||||||
.FirstOrDefault(a => a.Id == Id);
|
.FirstOrDefault(a => a.Id == Id);
|
||||||
|
} else if (Date is { } date && Title is { } title) {
|
||||||
|
using var context = ContextFactory.CreateDbContext();
|
||||||
|
Article = context.Set<Article>()
|
||||||
|
.Include(a => a.Author)
|
||||||
|
.Include(a => a.Reviewer)
|
||||||
|
.FirstOrDefault(a => a.PublishDate.Date == date.Date && a.Title.ToLower() == title);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SubmitForReview() {
|
private async Task SubmitForReview() {
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
} else {
|
} else {
|
||||||
@if (Articles.FirstOrDefault() is {} featured) {
|
@if (Articles.FirstOrDefault() is {} featured) {
|
||||||
<article class="mb-6">
|
<article class="mb-6">
|
||||||
<a href="/article/@featured.Id">
|
<ArticleLink Article="featured">
|
||||||
<div class="hero bg-secondary text-secondary-content">
|
<div class="hero bg-secondary text-secondary-content">
|
||||||
<div class="hero-content">
|
<div class="hero-content">
|
||||||
<div class="flex flex-col space-y-6 my-3">
|
<div class="flex flex-col space-y-6 my-3">
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</ArticleLink>
|
||||||
</article>
|
</article>
|
||||||
} else {
|
} else {
|
||||||
<h2 class="text-2xl lg:text-4xl mb-6">@Localizer["NoArticles_Title"]</h2>
|
<h2 class="text-2xl lg:text-4xl mb-6">@Localizer["NoArticles_Title"]</h2>
|
||||||
|
|
Loading…
Reference in a new issue