Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/LinkDotNet.Blog.Infrastructure/PagedList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public PagedList(IReadOnlyList<T> items, int totalCount, int pageNumber, int pag

public int PageNumber { get; }

internal static int? TotalCountFromPage(int itemCount, int page, int pageSize)
=> itemCount < pageSize && (itemCount > 0 || page == 1) ? ((page - 1) * pageSize) + itemCount : null;

internal static IPagedList<T> Create(IReadOnlyList<T> items, int totalCount, int page, int pageSize)
=> totalCount == 0 ? Empty : new PagedList<T>(items, totalCount, page, pageSize);

public bool IsFirstPage => PageNumber == 1;

public bool IsLastPage => PageNumber == totalPages;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,11 @@ public static class PaginatedListQueryExtensions
{
public static async Task<IPagedList<T>> ToPagedListAsync<T>(this IQueryable<T> source, int pageIndex, int pageSize, CancellationToken token = default)
{
var count = await source.CountAsync(token);
if (count > 0)
{
var items = await source
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToListAsync(token);
return new PagedList<T>(items, count, pageIndex, pageSize);
}

return PagedList<T>.Empty;
var items = await source
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToListAsync(token);
var count = PagedList<T>.TotalCountFromPage(items.Count, pageIndex, pageSize) ?? await source.CountAsync(token);
return PagedList<T>.Create(items, count, pageIndex, pageSize);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,11 @@ public static class PaginatedListQueryExtensions
{
public static async Task<IPagedList<T>> ToPagedListAsync<T>(this IRavenQueryable<T> source, int pageIndex, int pageSize, CancellationToken token = default)
{
var count = await source.CountAsync(token);
if (count > 0)
{
var items = await source
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToArrayAsync(token);
return new PagedList<T>(items, count, pageIndex, pageSize);
}

return PagedList<T>.Empty;
var items = await source
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToArrayAsync(token);
var count = PagedList<T>.TotalCountFromPage(items.Length, pageIndex, pageSize) ?? await source.CountAsync(token);
return PagedList<T>.Create(items, count, pageIndex, pageSize);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,16 @@ public static class PaginatedListQueryExtensions
{
public static async Task<IPagedList<T>> ToPagedListAsync<T>(this IQueryable<T> source, int page, int pageSize, CancellationToken token = default)
{
var count = await source.CountAsync(token);
if (count > 0)
{
// I tried ToListAsync and it performed just poorly!
// Mainly because we have a VARCHAR(max) column
// See here: https://stackoverflow.com/questions/28543293/entity-framework-async-operation-takes-ten-times-as-long-to-complete/28619983
// I tried ToListAsync and it performed just poorly!
// Mainly because we have a VARCHAR(max) column
// See here: https://stackoverflow.com/questions/28543293/entity-framework-async-operation-takes-ten-times-as-long-to-complete/28619983
#pragma warning disable S6966
var items = source
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToArray();
var items = source
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToArray();
#pragma warning restore S6966
return new PagedList<T>(items, count, page, pageSize);
}

return PagedList<T>.Empty;
var count = PagedList<T>.TotalCountFromPage(items.Length, page, pageSize) ?? await source.CountAsync(token);
return PagedList<T>.Create(items, count, page, pageSize);
}
}
4 changes: 2 additions & 2 deletions src/LinkDotNet.Blog.Web/App.razor
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@
<link rel="dns-prefetch" href="//cdnjs.cloudflare.com" />
<link rel="preconnect" href="//cdnjs.cloudflare.com" />
<link rel="preconnect" href="//cdn.jsdelivr.net" />
<link rel="preconnect" href="//cdn.jsdelivr.net" crossorigin />
<link rel="preload" href="css/fonts/icons.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="css/fonts/icons.woff" as="font" type="font/woff" crossorigin />
<link href="css/basic.css" rel="stylesheet" />
<link href="css/icons.css" rel="stylesheet" />
<link href="css/markdown-editor.css" rel="stylesheet" />
<link rel="preload" href="LinkDotNet.Blog.Web.styles.css" as="style">
<link href="LinkDotNet.Blog.Web.styles.css" rel="stylesheet"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.12.0/styles/github-dark-dimmed.min.css" integrity="sha512-zcatBMvxa7rT7dDklfjauWsfiSFParF+hRfCdf4Zr40/MmA1gkFcBRbop0zMpvYF3FmznYFgcL8wlcuO/GwHoA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.12.0/styles/github-dark-dimmed.min.css" media="print" onload="this.media='all'" integrity="sha512-zcatBMvxa7rT7dDklfjauWsfiSFParF+hRfCdf4Zr40/MmA1gkFcBRbop0zMpvYF3FmznYFgcL8wlcuO/GwHoA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@6.0.0-alpha.1/dist/css/bootstrap.min.css" integrity="sha384-B/GM4XqrwHnWXNOWMbloTmrYXZg10cakYGmpfsR/bbzQ6JAJI4ihuyADKLnBgrCe" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.2/font/bootstrap-icons.min.css">
</head>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,16 @@
public RenderFragment? Stats { get; set; }

private List<ProfileInformationEntry> profileInformationEntries = [];

[PersistentState]
public List<ProfileInformationEntry>? PersistedEntries { get; set; }
private ConfirmDialog Dialog { get; set; } = default!;
private string? currentDeleteKey;
private ProfileInformationEntry? currentDragItem;

protected override async Task OnInitializedAsync()
{
profileInformationEntries = (await Repository.GetAllAsync(orderBy: d => d.SortOrder, descending: false)).ToList();
profileInformationEntries = PersistedEntries ??= (await Repository.GetAllAsync(orderBy: d => d.SortOrder, descending: false)).ToList();
}

private void ShowDeleteDialog(string key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@
private bool isUpcoming;
private DateTime today;

[PersistentState]
public List<Domain.Talk>? PersistedTalks { get; set; }

[PersistentState]
public List<Domain.Skill>? PersistedSkills { get; set; }

private string SpotlightDetail => (spotlightTalk!.PublishedDate.Date - today).Days switch
{
_ when !isUpcoming => spotlightTalk.PublishedDate.Year.ToString(CultureInfo.InvariantCulture),
Expand All @@ -60,15 +66,17 @@

public async Task RefreshAsync()
{
PersistedTalks = null;
PersistedSkills = null;
await LoadAsync();
StateHasChanged();
}

private async Task LoadAsync()
{
today = TimeProvider.GetLocalNow().Date;
var talks = await TalkRepository.GetAllAsync(orderBy: t => t.PublishedDate, descending: false);
var skills = await SkillRepository.GetAllAsync();
var talks = PersistedTalks ??= (await TalkRepository.GetAllAsync(orderBy: t => t.PublishedDate, descending: false)).ToList();
var skills = PersistedSkills ??= (await SkillRepository.GetAllAsync()).ToList();

talkCount = talks.Count;
firstTalkYear = talks.FirstOrDefault()?.PublishedDate.Year ?? 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,14 @@

private List<Skill> skills = [];

[PersistentState]
public List<Skill>? PersistedSkills { get; set; }

private Skill? currentDragItem;

protected override async Task OnInitializedAsync()
{
skills = (await Repository.GetAllAsync()).ToList();
skills = PersistedSkills ??= (await Repository.GetAllAsync()).ToList();
}

private async Task AddSkill(Skill skillToAdd)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@using LinkDotNet.Blog.Web.Features.AboutMe.Components.Talk

<div class="d-flex flex-column gap-6">
<div class="nav nav-pills segmented-control" id="aboutMe" role="tablist" aria-label="About me sections">
<div class="nav segmented" id="aboutMe" role="tablist" aria-label="About me sections">
<button class="nav-link flex-fill justify-content-center active" id="pills-skills-tab"
data-bs-toggle="tab" data-bs-target="#pills-home"
type="button" role="tab" aria-controls="pills-home" aria-selected="true">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
.segmented-control {
--bs-nav-pills-bg: var(--tag-background);
--bs-nav-pills-padding: 0.125rem;
--bs-nav-pills-link-active-bg: var(--surface);
--bs-nav-pills-link-active-color: var(--bs-fg-body);
.segmented {
width: min(100%, 20rem);
}

.segmented-control .active {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ else

private List<Talk> talks = [];

[PersistentState]
public List<Talk>? PersistedTalks { get; set; }

private AddTalkEntryDialog AddTalkEntryDialog { get; set; } = default!;

private RenderFragment<(string Title, List<Talk> Talks, bool IsUpcoming)> TalkGroup => group =>
Expand All @@ -57,7 +60,7 @@ else

protected override async Task OnInitializedAsync()
{
talks = (await Repository.GetAllAsync(orderBy: t => t.PublishedDate, descending: true)).ToList();
talks = PersistedTalks ??= (await Repository.GetAllAsync(orderBy: t => t.PublishedDate, descending: true)).ToList();
}

private async Task DeleteTalk(Talk talk)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,7 @@
<input type="search" placeholder="Search shortcodes" aria-label="Search shortcodes" autocomplete="off"
@bind="SearchTerm" @bind:event="oninput" />
</label>
<div class="segmented-control" role="group" aria-label="Sort shortcodes">
@foreach (var option in Enum.GetValues<SortOrder>())
{
<button type="button" class="segment" aria-pressed="@(sortOrder == option ? "true" : "false")" @onclick="() => sortOrder = option">@SortLabel(option)</button>
}
</div>
<SegmentedControl Options="@(Enum.GetValues<SortOrder>())" @bind-Value="sortOrder" Label="SortLabel" AriaLabel="Sort shortcodes" />
</div>

var visibleShortCodes = GetVisibleShortCodes();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,34 +110,6 @@
font-size: 1rem;
}

.segmented-control {
display: flex;
padding: 0.125rem;
border-radius: 0.75rem;
background: var(--tag-background);
}

.segment {
flex: 1 1 auto;
min-height: 2.5rem;
padding-inline: 0.875rem;
border: 0;
border-radius: 0.625rem;
background: transparent;
color: inherit;
font-size: 0.9375rem;
font-weight: 500;
white-space: nowrap;
cursor: pointer;
transition: background-color 0.15s ease, box-shadow 0.15s ease;
}

.segment[aria-pressed="true"] {
background: var(--surface);
font-weight: 600;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
}

.no-results {
padding: 3rem 1rem;
text-align: center;
Expand Down Expand Up @@ -547,7 +519,6 @@
}

.sheet-action:focus-visible,
.segment:focus-visible,
.token-chip:focus-visible,
.card-action:focus-visible,
.template-chip:focus-visible,
Expand All @@ -566,7 +537,7 @@
grid-template-columns: 1fr;
}

.segmented-control {
.toolbar ::deep .segmented {
flex: 1 1 100%;
}

Expand Down
13 changes: 8 additions & 5 deletions src/LinkDotNet.Blog.Web/Features/Archive/ArchivePage.razor
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,23 @@
private IReadOnlyCollection<IGrouping<int, BlogPostPerYear>>? blogPostsPerYear;
private int blogPostCount;

[PersistentState]
public List<BlogPostPerYear>? BlogPosts { get; set; }

protected override async Task OnInitializedAsync()
{
var blogPosts = await Repository.GetAllByProjectionAsync(
BlogPosts ??= (await Repository.GetAllByProjectionAsync(
p => new BlogPostPerYear(p.Id, p.Slug, p.Title, p.UpdatedDate),
p => p.IsPublished);
p => p.IsPublished)).ToList();

blogPostCount = blogPosts.Count;
blogPostsPerYear = blogPosts
blogPostCount = BlogPosts.Count;
blogPostsPerYear = BlogPosts
.GroupBy(r => r.UpdatedDate.Year)
.OrderByDescending(r => r.Key)
.ToImmutableArray();
}

private static string HeadingKey(int year) => $"heading{year}";

private sealed record BlogPostPerYear(string Id, string Slug, string Title, DateTime UpdatedDate);
public sealed record BlogPostPerYear(string Id, string Slug, string Title, DateTime UpdatedDate);
}
7 changes: 1 addition & 6 deletions src/LinkDotNet.Blog.Web/Features/Bookmarks/Bookmarks.razor
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,7 @@
<input type="search" placeholder="Search bookmarks" aria-label="Search bookmarks" autocomplete="off"
@bind="SearchTerm" @bind:event="oninput" />
</label>
<div class="segmented-control" role="group" aria-label="Sort bookmarks">
@foreach (var option in Enum.GetValues<SortOrder>())
{
<button type="button" class="segment" aria-pressed="@(sortOrder == option ? "true" : "false")" @onclick="() => sortOrder = option">@SortLabel(option)</button>
}
</div>
<SegmentedControl Options="@(Enum.GetValues<SortOrder>())" @bind-Value="sortOrder" Label="SortLabel" AriaLabel="Sort bookmarks" />
</div>

var visiblePosts = GetVisiblePosts();
Expand Down
41 changes: 2 additions & 39 deletions src/LinkDotNet.Blog.Web/Features/Bookmarks/Bookmarks.razor.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,39 +55,6 @@
font-size: 1rem;
}

.segmented-control {
display: flex;
padding: 0.125rem;
border-radius: 0.75rem;
background: var(--tag-background);
}

.segment {
flex: 1 1 auto;
min-height: 2.5rem;
padding-inline: 0.875rem;
border: 0;
border-radius: 0.625rem;
background: transparent;
color: inherit;
font-size: 0.9375rem;
font-weight: 500;
white-space: nowrap;
cursor: pointer;
transition: background-color 0.15s ease, box-shadow 0.15s ease;
}

.segment[aria-pressed="true"] {
background: var(--surface);
font-weight: 600;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
}

.segment:focus-visible {
outline: 2px solid var(--bs-primary-base, #0a84ff);
outline-offset: 1px;
}

/* Inset-grouped list */
.list-group-inset {
margin: 0;
Expand Down Expand Up @@ -226,11 +193,11 @@
font-size: 2rem;
}

.segmented-control {
.toolbar ::deep .segmented {
flex: 1 1 100%;
}

.segment {
.toolbar ::deep .segmented > button {
padding-inline: 0.5rem;
}

Expand All @@ -246,8 +213,4 @@
.skeleton-text {
animation: none;
}

.segment {
transition: none;
}
}
Loading
Loading