+
+
-
+
@@ -59,14 +59,9 @@
private bool isBookmarked = false;
- [Parameter]
- public bool UseAlternativeStyle { get; set; }
-
[Parameter]
public bool LazyLoadPreviewImage { get; set; }
- private string AltCssClass => UseAlternativeStyle ? "alt" : string.Empty;
-
private async Task ToggleBookmark()
{
isBookmarked = !isBookmarked;
@@ -83,9 +78,6 @@
case nameof(BlogPost):
BlogPost = (BlogPost)parameter.Value;
break;
- case nameof(UseAlternativeStyle):
- UseAlternativeStyle = (bool)parameter.Value;
- break;
case nameof(LazyLoadPreviewImage):
LazyLoadPreviewImage = (bool)parameter.Value;
break;
diff --git a/src/LinkDotNet.Blog.Web/Features/Components/UploadFileModalDialog.razor b/src/LinkDotNet.Blog.Web/Features/Components/UploadFileModalDialog.razor
index b6b57add..745cb9bd 100644
--- a/src/LinkDotNet.Blog.Web/Features/Components/UploadFileModalDialog.razor
+++ b/src/LinkDotNet.Blog.Web/Features/Components/UploadFileModalDialog.razor
@@ -1,54 +1,40 @@
-
-
+
+
-
+
+
+ @BlogPost.Title
+-
@if (BlogPost.IsScheduled)
{
-
- Scheduled +
- Scheduled } else if (!BlogPost.IsPublished) { -
- Draft +
- Draft } -
- @BlogPost.UpdatedDate.ToShortDateString() +
- @BlogPost.UpdatedDate.ToShortDateString() +
- @BlogPost.ReadingTimeInMinutes minute read + @if (AppConfiguration.Value.UseMultiAuthorMode && BlogPost.AuthorName is not null) + { +
- @BlogPost.AuthorName + } @if (BlogPost.Tags.Any()) { -
- - -
-
-
-
- @BlogPost.Title
-@MarkdownConverter.ToMarkupString(BlogPost.ShortDescription)
-@MarkdownConverter.ToMarkupString(BlogPost.ShortDescription)
+
-
-
- result = null!;
- private string modalDisplay = "none;";
- private string modalClass = "";
- private bool showBackdrop;
+ private bool isOpen;
private readonly UploadFileModalDialogObject model = new();
public async Task ShowAsync(string fileName)
{
- modalDisplay = "block;";
- modalClass = "show";
- showBackdrop = true;
+ isOpen = true;
model.Name = fileName;
result = new TaskCompletionSource();
StateHasChanged();
@@ -57,18 +43,14 @@
private void Abort()
{
- modalDisplay = "none";
- modalClass = string.Empty;
- showBackdrop = false;
+ isOpen = false;
result.SetResult(null);
StateHasChanged();
}
private void Ok()
{
- modalDisplay = "none";
- modalClass = string.Empty;
- showBackdrop = false;
+ isOpen = false;
result.SetResult(model);
StateHasChanged();
}
diff --git a/src/LinkDotNet.Blog.Web/Features/CopyCodeBlockToClipboardExtension.cs b/src/LinkDotNet.Blog.Web/Features/CopyCodeBlockToClipboardExtension.cs
index bd06f3a7..6cc9a2a6 100644
--- a/src/LinkDotNet.Blog.Web/Features/CopyCodeBlockToClipboardExtension.cs
+++ b/src/LinkDotNet.Blog.Web/Features/CopyCodeBlockToClipboardExtension.cs
@@ -72,8 +72,9 @@ protected override void Write(HtmlRenderer renderer, CodeBlock obj)
renderer.Write("""""").WriteEscape(fenced.Info).Write("");
}
renderer.Write("""
-
diff --git a/src/LinkDotNet.Blog.Web/Features/DummyData/DummyDataSeeder.cs b/src/LinkDotNet.Blog.Web/Features/DummyData/DummyDataSeeder.cs
index 21cd6ebb..e6ab04dd 100644
--- a/src/LinkDotNet.Blog.Web/Features/DummyData/DummyDataSeeder.cs
+++ b/src/LinkDotNet.Blog.Web/Features/DummyData/DummyDataSeeder.cs
@@ -40,6 +40,23 @@ private static List GenerateDummyBlogPosts(int count)
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ > [!NOTE]
+ > Callouts render as Bootstrap alerts. This one checks spacing and contrast in both themes.
+
+ ## Getting Set Up
+
+ Before diving in, make sure you have the following installed:
+
+ - The latest .NET SDK
+ - An editor with C# support
+ - A terminal you are comfortable with
+
+ ### Project Structure
+
+ Run `dotnet new console` and you get a minimal project. The interesting parts live in `Program.cs`.
+
+ 
+
## Code Example
```csharp
@@ -52,15 +69,39 @@ public void HelloWorld()
}
```
+ ### A Longer Snippet
+
+ ```csharp
+ app.MapGet("/weatherforecast", async (IWeatherService service, CancellationToken token) =>
+ {
+ var forecasts = await service.GetForecastsAsync(days: 5, cancellationToken: token);
+ return forecasts.Count == 0 ? Results.NoContent() : Results.Ok(forecasts);
+ });
+ ```
+
+ ## Comparing Options
+
+ | Approach | Startup | Memory | Notes |
+ |---|---|---|---|
+ | JIT | Slower | Higher | Most flexible |
+ | ReadyToRun | Faster | Medium | Larger binaries |
+ | Native AOT | Fastest | Lowest | No runtime reflection |
+
+ > Simplicity is prerequisite for reliability.
+
## More Content
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
+
+ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
""";
var titles = new[]
{
"Getting Started with C# 12",
+ "Array, List, Collection, Set, ReadOnlyList - what? A comprehensive and exhaustive list of collection-like types",
"Understanding Blazor Components",
+ "How to write your own cron Job scheduler in ASP.NET Core (like Quartz, Hangfire, ...)",
"ASP.NET Core Best Practices",
"Mastering Entity Framework Core",
"Introduction to LINQ",
@@ -93,6 +134,14 @@ public void HelloWorld()
"Docker",
"Azure",
"Architecture",
+ "Performance",
+ "Security",
+ "OpenTelemetry",
+ "Minimal APIs",
+ ".NET Aspire",
+ "Source Generators",
+ "Kubernetes",
+ "GitHub Actions",
};
var blogPosts = new List();
@@ -100,20 +149,25 @@ public void HelloWorld()
for (var i = 0; i < count; i++)
{
var title = i < titles.Length ? titles[i] : $"Blog Post {i + 1}";
- var shortDescription = $"This is a dummy blog post about {title}. It contains valuable information for developers.";
+ var shortDescription = (i % 3) switch
+ {
+ 0 => $"A short look at {title}.",
+ 1 => $"This is a dummy blog post about {title}. It contains valuable information for developers.",
+ _ => $"This is a dummy blog post about {title}. It walks through the motivation, shows a minimal example, compares the trade-offs against the alternatives and ends with a few practical tips you can apply in your own code base today.",
+ };
var selectedTags = tags
.OrderBy(_ => Random.Shared.Next())
- .Take(Random.Shared.Next(1, 4))
+ .Take(Random.Shared.Next(1, 9))
.ToArray();
var blogPost = BlogPost.Create(
title,
shortDescription,
loremIpsum,
- "https://via.placeholder.com/800x400",
+ $"https://picsum.photos/seed/blog-{i}/1200/630",
true,
- DateTime.UtcNow.AddDays(-i),
+ DateTime.UtcNow.AddDays(-i * 23),
null,
selectedTags,
null,
diff --git a/src/LinkDotNet.Blog.Web/Features/Home/Components/AccessControl.razor b/src/LinkDotNet.Blog.Web/Features/Home/Components/AccessControl.razor
index ed01cc59..877606ea 100644
--- a/src/LinkDotNet.Blog.Web/Features/Home/Components/AccessControl.razor
+++ b/src/LinkDotNet.Blog.Web/Features/Home/Components/AccessControl.razor
@@ -4,49 +4,47 @@
- -
-
+
- Admin
+ Admin
-
@if (authorName is not null)
{
- -
-
- @authorName
+
-
+
+ @authorName
-
}
- else
- {
- - Log out
- }
-
- - Log in
-
@code {
diff --git a/src/LinkDotNet.Blog.Web/Features/Home/Components/BlogPostNavigation.razor b/src/LinkDotNet.Blog.Web/Features/Home/Components/BlogPostNavigation.razor
index e50cd297..c2e7d014 100644
--- a/src/LinkDotNet.Blog.Web/Features/Home/Components/BlogPostNavigation.razor
+++ b/src/LinkDotNet.Blog.Web/Features/Home/Components/BlogPostNavigation.razor
@@ -1,12 +1,12 @@
@typeparam T
@using LinkDotNet.Blog.Infrastructure
diff --git a/src/LinkDotNet.Blog.Web/Features/Home/Components/Footer.razor b/src/LinkDotNet.Blog.Web/Features/Home/Components/Footer.razor
index dae91af5..4569fac4 100644
--- a/src/LinkDotNet.Blog.Web/Features/Home/Components/Footer.razor
+++ b/src/LinkDotNet.Blog.Web/Features/Home/Components/Footer.razor
@@ -3,20 +3,28 @@
@inject IOptions AppConfiguration
@inject IOptions ProfileInformation
@inject IOptions SupportConfiguration
+@inject NavigationManager NavigationManager
+@implements IDisposable
-
-
-
- Configuration
-
-
-
-
-
-@if (showBackdrop)
-{
-
-}
+
+
+
-
+
+
+ Configuration
+
+
+
+
-
+ The filename of the media file. On services like Azure, this can include the directory path.
- The filename of the media file. On services like Azure, this can include the directory path.
-
-
-
- Enable Media Caching
- - If enabled, the "Cache-Control" header will be set automatically.
- The value is set to one week. If disabled, no header will be set. - -
- - - If enabled, the "Cache-Control" header will be set automatically.
- The value is set to one week. If disabled, no header will be set. - -
- Abort
- OK
+
+
+ Enable Media Caching
+ If enabled, the "Cache-Control" header will be set automatically.
The value is set to one week. If disabled, no header will be set.
-
+
+
+ The value is set to one week. If disabled, no header will be set.
+ Abort
+ OK
+
+
@code {
private TaskCompletionSource-
-
Blog posts
- - Create new -
- Show drafts -
- Show settings -
Analytics
- - Dashboard +
- Broken links + Broken links } -
Others
- - Shortcodes -
- Releases - +
+
+
+
+ Releases + @if (authorName is null) + { +
+ Log out + } +
Blog posts
+ Create new + Show drafts + Show settings ++
Analytics
+ Dashboard @if (AppConfiguration.Value.EnableBrokenLinkChecker) { -+
Others
+ Shortcodes ++ Releases + @if (authorName is null) + { +
+ Log out + } +
-
-
- Log out -
+ Log out
+