Skip to content

Commit ff91889

Browse files
EmanuelGFlinkdotnet
authored andcommitted
fix: address review feedback
1 parent d091719 commit ff91889

8 files changed

Lines changed: 50 additions & 62 deletions

File tree

docs/Setup/Configuration.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ The appsettings.json file has a lot of options to customize the content of the b
6767
"ContainerName": "",
6868
"CdnEndpoint": ""
6969
},
70-
"UseMultiAuthorMode": false
70+
"UseMultiAuthorMode": false,
71+
"EnableTagDiscoveryPanel": true
7172
}
7273
```
7374

@@ -113,3 +114,4 @@ The appsettings.json file has a lot of options to customize the content of the b
113114
| ContainerName | string | The container name for the image storage provider |
114115
| CdnEndpoint | string | Optional CDN endpoint to use for uploaded images. If set, the blog will return this URL instead of the storage account URL for uploaded assets. |
115116
| UseMultiAuthorMode | boolean | The default value is `false`. If set to `true` then author name will be associated with blog posts at the time of creation. This author name will be fetched from the identity provider's `name` or `nickname` or `preferred_username` claim property. |
117+
| EnableTagDiscoveryPanel | boolean | The default value is `true`. Enables the Tag Discovery Panel, which helps users discover topics by browsing popular tags. |

src/LinkDotNet.Blog.Web/ApplicationConfiguration.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,4 @@ public sealed record ApplicationConfiguration
2929
public bool UseMultiAuthorMode { get; init; }
3030

3131
public bool EnableTagDiscoveryPanel { get; set; }
32-
33-
public bool ShowTagsWithCountInTagDiscovery { get; set; }
3432
}

src/LinkDotNet.Blog.Web/Features/Home/Components/NavMenu.razor

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@
6161

6262
@if (Configuration.Value.EnableTagDiscoveryPanel)
6363
{
64-
<li class="nav-item d-flex align-items-center">
65-
<a class="tag-discovery-btn" @onclick="ToggleTagDiscoveryPanel"
66-
title="Discover new topics"> &#xE936; </a>
64+
<li class="nav-item d-flex align-items-center me-lg-2 mb-2 mb-lg-0">
65+
<a class="nav-link d-flex align-items-center justify-content-center" @onclick="ToggleTagDiscoveryPanel"
66+
style="font-family: 'icons'; font-weight: 900; cursor: pointer;"
67+
title="Discover new topics"> &#xE936; </a>
6768
<TagDiscoveryPanel IsOpen="@_isOpen" OnClose="CloseTagDiscoveryPanel" />
6869
</li>
6970
}
Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,49 @@
1-
using Azure.Storage.Blobs.Models;
21
using LinkDotNet.Blog.Domain;
32
using LinkDotNet.Blog.Infrastructure.Persistence;
3+
using Microsoft.Extensions.Options;
4+
using Raven.Client.Documents.Operations.AI;
45
using System;
56
using System.Collections.Generic;
67
using System.Linq;
78
using System.Threading.Tasks;
9+
using ZiggyCreatures.Caching.Fusion;
810

911
namespace LinkDotNet.Blog.Web.Features.Services.Tags;
1012

11-
public sealed class TagQueryService(IRepository<BlogPost> blogPostRepository) : ITagQueryService
13+
public sealed class TagQueryService(
14+
IRepository<BlogPost> blogPostRepository,
15+
IFusionCache fusionCache,
16+
IOptions<ApplicationConfiguration> appConfiguration) : ITagQueryService
1217
{
18+
private const string TagCacheKey = "TagUsageList";
19+
1320
public async Task<IReadOnlyList<TagCount>> GetAllOrderedByUsageAsync()
21+
{
22+
return await fusionCache.GetOrSetAsync(
23+
TagCacheKey,
24+
async _ => await LoadTagsAsync(),
25+
options =>
26+
{
27+
options.SetDuration(TimeSpan.FromMinutes(
28+
appConfiguration.Value.FirstPageCacheDurationInMinutes));
29+
});
30+
}
31+
32+
private async Task<IReadOnlyList<TagCount>> LoadTagsAsync()
1433
{
1534
var posts = await blogPostRepository.GetAllAsync();
1635

1736
var tagCounts = posts
18-
// Flatten the collection of tag lists into a single sequence.
1937
.SelectMany(p => p.Tags ?? Enumerable.Empty<string>())
20-
21-
// Defensive guard against invalid tag values.
2238
.Where(tag => !string.IsNullOrEmpty(tag))
23-
2439
.GroupBy(tag => tag.Trim())
25-
26-
// Transform each group into a TagCount DTO.
27-
// group.Key = tag name
28-
// group.Count() = number of occurrences
2940
.Select(group => new TagCount(
3041
group.Key,
3142
group.Count()))
32-
33-
// Sort descending by usage count (most popular first).
3443
.OrderByDescending(tc => tc.Count)
3544
.ThenBy(tc => tc.Name)
3645
.ToList();
37-
3846
return tagCounts;
3947
}
48+
4049
}

src/LinkDotNet.Blog.Web/Features/TagDiscovery/TagDiscoveryPanel.razor

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,21 @@
44

55
@if (!AppConfiguration.Value.EnableTagDiscoveryPanel || !IsOpen) { return; }
66

7-
<div class="tag-overlay" @onclick="Close"></div>
7+
<div class="position-fixed top-0 start-0 w-100 h-100 bg-dark bg-opacity-25"
8+
style="z-index:1040; backdrop-filter: blur(2px);"
9+
@onclick="Close">
10+
</div>
811

9-
<div class="tag-panel">
10-
<div class="tag-discovery-container">
12+
<div class="position-fixed top-50 start-50 translate-middle bg-body border rounded shadow p-3"
13+
style="max-width: 400px; max-height: 70vh; overflow-y:auto; z-index:1050;">
14+
<div class="d-flex flex-wrap gap-2">
1115
@foreach (var tag in _tags)
1216
{
13-
<span class="tag-badge" @onclick="() => Navigate(tag.Name)">
17+
<span class="badge bg-secondary d-flex align-items-center gap-1"
18+
style="cursor: pointer;"
19+
@onclick="() => Navigate(tag.Name)">
1420
@tag.Name
15-
16-
@if (AppConfiguration.Value.ShowTagsWithCountInTagDiscovery)
17-
{
18-
<span class="tag-count">@tag.Count</span>
19-
}
21+
<span class="badge bg-light text-dark">@tag.Count</span>
2022
</span>
2123
}
2224
</div>
@@ -30,7 +32,7 @@
3032

3133
protected override async Task OnParametersSetAsync()
3234
{
33-
if (IsOpen && _tags.Count == 0)
35+
if (IsOpen)
3436
{
3537
_tags = await TagQueryService.GetAllOrderedByUsageAsync();
3638
}

src/LinkDotNet.Blog.Web/appsettings.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,5 @@
5050
"ShowSimilarPosts": true,
5151
"ShowBuildInformation": true,
5252
"UseMultiAuthorMode": false,
53-
"EnableTagDiscoveryPanel": true,
54-
"ShowTagsWithCountInTagDiscovery": true
53+
"EnableTagDiscoveryPanel": true
5554
}

src/LinkDotNet.Blog.Web/wwwroot/css/basic.css

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -645,17 +645,6 @@ code {
645645
object-fit: cover;
646646
}
647647

648-
.tag-discovery-btn {
649-
font-family: 'icons';
650-
font-weight: 900;
651-
content: "\e936";
652-
cursor: pointer;
653-
padding: 6px;
654-
margin: 6px;
655-
text-decoration: none;
656-
color: var(--bs-navbar-color);
657-
}
658-
659648
@media only screen and (max-width: 700px) {
660649
.blog-outer-box .blog-container {
661650
width: 90%;

tests/LinkDotNet.Blog.UnitTests/Web/Features/Services/Tags/TagQueryServiceTests.cs

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using LinkDotNet.Blog.Domain;
22
using LinkDotNet.Blog.Infrastructure;
33
using LinkDotNet.Blog.Infrastructure.Persistence;
4+
using LinkDotNet.Blog.TestUtilities;
45
using LinkDotNet.Blog.Web.Features.Services.Tags;
56
using MongoDB.Driver;
67
using NSubstitute;
@@ -42,9 +43,9 @@ public async Task AggregatesAndSortsTagsByUsage()
4243
// Arrange
4344
var posts = new List<BlogPost>
4445
{
45-
CreatePost(["CSharp", "Blazor", "DotNet"]),
46-
CreatePost(["CSharp", "Blazor"]),
47-
CreatePost(["CSharp"])
46+
new BlogPostBuilder().WithTags("CSharp", "Blazor", "DotNet").Build(),
47+
new BlogPostBuilder().WithTags("CSharp", "Blazor").Build(),
48+
new BlogPostBuilder().WithTags("CSharp").Build(),
4849
};
4950

5051
repository.GetAllAsync()
@@ -72,8 +73,8 @@ public async Task ShouldIgnoreNullOrWhitespaceTags()
7273
// Arrange
7374
var posts = new List<BlogPost>
7475
{
75-
CreatePost(["CSharp", " "]),
76-
CreatePost(null!)
76+
new BlogPostBuilder().WithTags("CSharp", " ").Build(),
77+
new BlogPostBuilder().Build(),
7778
};
7879

7980
repository.GetAllAsync()
@@ -94,8 +95,8 @@ public async Task ShouldSortAlphabeticallyWhenCountsAreEqual()
9495
// Arrange
9596
var posts = new List<BlogPost>
9697
{
97-
CreatePost(["CSharp"]),
98-
CreatePost(["Blazor"])
98+
new BlogPostBuilder().WithTags("CSharp").Build(),
99+
new BlogPostBuilder().WithTags("Blazor").Build(),
99100
};
100101

101102
repository.GetAllAsync()
@@ -109,19 +110,6 @@ public async Task ShouldSortAlphabeticallyWhenCountsAreEqual()
109110
result[1].Name.ShouldBe("CSharp");
110111
}
111112

112-
private static BlogPost CreatePost(List<string> tags)
113-
{
114-
var unique = Guid.NewGuid().ToString("N");
115-
116-
return BlogPost.Create(
117-
$"Post-{unique}",
118-
$"Slug-{unique}",
119-
$"Excerpt-{unique}",
120-
"#",
121-
false,
122-
tags: tags);
123-
}
124-
125113
private static PagedList<BlogPost> CreatePagedList(List<BlogPost> posts)
126114
{
127115
return new PagedList<BlogPost>(

0 commit comments

Comments
 (0)