Repository navigation
Shared segmented control; load data once per visit and stop hydration flashes - #573
Merged
Merged
Conversation
One global .segmented style and a SegmentedControl component replace the four per-page copies (topic discovery, search, bookmarks, shortcodes). The About Me tab list reuses the same style, so all toggles look alike.
- Paging skips the COUNT query when the page comes back short, halving queries for every get-all call (SQL, RavenDB, MongoDB) - Prerendered data is handed to the interactive render via [PersistentState] for blog post short codes, About Me and the archive, so the circuit no longer re-queries and renders empty in between - Preload only the woff2 icon font, load the highlight.js theme non-blocking and preconnect CORS for the jsdelivr fonts
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues are reported for this change.
No open issues 🎉
What changed in this PR
This PR consolidates segmented controls across the Blazor UI and reduces repeated data loading during hydration. It also avoids unnecessary paging count queries and adjusts asset loading to reduce rendering delays.
Changes:
- Introduces a shared segmented-control component and global styling, while preserving About Me tab semantics.
- Persists selected prerendered data with entity-aware serialization to avoid hydration reloads and flashes.
- Optimizes paging across SQL, RavenDB, and MongoDB, updates asset loading, and adds serialization and paging tests.
| File | Description |
|---|---|
| tests/LinkDotNet.Blog.UnitTests/Web/Features/PersistedStateSerializerTests.cs | Adds entity serialization round-trip tests. |
| tests/LinkDotNet.Blog.IntegrationTests/Web/Features/Bookmarks/BookmarksTests.cs | Updates the sort-control selector. |
| tests/LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/Sql/BlogPostRepositoryTests.cs | Tests paging boundaries and empty results. |
| src/LinkDotNet.Blog.Web/wwwroot/css/icons.css | Prefers the WOFF2 icon font. |
| src/LinkDotNet.Blog.Web/wwwroot/css/basic.css | Defines shared segmented-control styling. |
| src/LinkDotNet.Blog.Web/ServiceExtensions.cs | Registers the persistent-state serializer. |
| src/LinkDotNet.Blog.Web/Features/TagDiscovery/TagDiscoveryPanel.razor.css | Removes duplicated control styling. |
| src/LinkDotNet.Blog.Web/Features/TagDiscovery/TagDiscoveryPanel.razor | Uses the shared topic-sort control. |
| src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor | Persists shortcodes and guards repeated post loads. |
| src/LinkDotNet.Blog.Web/Features/Search/SearchResultsView.razor.css | Retains layout rules for the shared control. |
| src/LinkDotNet.Blog.Web/Features/Search/SearchResultsView.razor | Uses the shared article-sort control. |
| src/LinkDotNet.Blog.Web/Features/PersistedStateSerializer.cs | Supports restoring entities with private members. |
| src/LinkDotNet.Blog.Web/Features/Components/SegmentedControl.razor | Adds a reusable, bindable segmented control. |
| src/LinkDotNet.Blog.Web/Features/Bookmarks/Bookmarks.razor.css | Replaces duplicated styles with shared-control layout rules. |
| src/LinkDotNet.Blog.Web/Features/Bookmarks/Bookmarks.razor | Uses the shared bookmark-sort control. |
| src/LinkDotNet.Blog.Web/Features/Archive/ArchivePage.razor | Persists projected archive data across hydration. |
| src/LinkDotNet.Blog.Web/Features/Admin/ShortCodes/ShortCodesPage.razor.css | Removes duplicated styles and adapts responsive layout. |
| src/LinkDotNet.Blog.Web/Features/Admin/ShortCodes/ShortCodesPage.razor | Uses the shared shortcode-sort control. |
| src/LinkDotNet.Blog.Web/Features/AboutMe/Components/Talk/Talks.razor | Persists the loaded talk list. |
| src/LinkDotNet.Blog.Web/Features/AboutMe/Components/TabbedNavigation.razor.css | Keeps tab sizing while adopting global styles. |
| src/LinkDotNet.Blog.Web/Features/AboutMe/Components/TabbedNavigation.razor | Applies shared styling while retaining Bootstrap tabs. |
| src/LinkDotNet.Blog.Web/Features/AboutMe/Components/Skill/SkillTable.razor | Persists the loaded skill list. |
| src/LinkDotNet.Blog.Web/Features/AboutMe/Components/ProfileStats.razor | Persists stats inputs and clears them on refresh. |
| src/LinkDotNet.Blog.Web/Features/AboutMe/Components/Profile.razor | Persists profile entries across hydration. |
| src/LinkDotNet.Blog.Web/App.razor | Adjusts font hints and defers highlight-theme application. |
| src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/PaginatedListQueryExtensions.cs | Derives totals from short pages when possible. |
| src/LinkDotNet.Blog.Infrastructure/Persistence/RavenDb/PaginatedListQueryExtensions.cs | Applies the paging count optimization to RavenDB. |
| src/LinkDotNet.Blog.Infrastructure/Persistence/MongoDB/PaginatedListQueryExtensions.cs | Applies the paging count optimization to MongoDB. |
| src/LinkDotNet.Blog.Infrastructure/PagedList.cs | Centralizes total inference and empty-result creation. |
Review effort: Balanced
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two commits.
1. Shared segmented control. The four per-page copies are replaced by one global
.segmentedstyle (the topic-discovery look) and one component. The About Me tab list reuses the same style but keeps its tab semantics.2. Load data once per visit and stop hydration flashes.
Pages are prerendered and then re-initialised when the circuit connects. Before this change they queried again and rendered empty in between.
sequenceDiagram participant Server participant Browser Server->>Browser: prerendered HTML + [PersistentState] payload Browser->>Server: circuit connects Note over Server: before: OnInitializedAsync re-queries, renders empty Note over Server: after: state restored, no query, no empty render[PersistentState]is applied to:PersistedStateSerializer<>lets System.Text.Json restore entities with private setters and constructors.Assets: preload only the woff2 icon font, load the highlight.js theme non-blocking, and add a CORS preconnect for the jsdelivr fonts.
Evidence
Benchmark setup:
master; after = this branch.Other measured changes:
bootstrap.min.cssfrom the CDN.Tests:
New
BlogPostRepositoryTests.ShouldPageWithCorrectLastPage:New
PersistedStateSerializerTests: round-tripsBlogPost,Skill(incl.ProficiencyLevel),Talk,ProfileInformationEntryandShortCode.Unit 445/445, integration 228/228.
The segmented controls were checked visually in light and dark mode on About Me, topic discovery, search and bookmarks. They now render identically.
Merge Danger
Door: two-way
No schema or data changes. Each commit reverts cleanly.
Blast Radius: moderate
GetAllAsyncon all three providers. Totals are now derived from short pages. The Raven and Mongo paths are only covered by the container tests.Contentfirst.