From ff8ede44ef523c4c7325490e008e05c161f6fbb8 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Thu, 7 Nov 2024 15:46:15 -0800 Subject: [PATCH 01/19] WIP --- EssentialCSharp.Web.Tests/SiteMappingTests.cs | 12 ++++++------ EssentialCSharp.Web/Controllers/HomeController.cs | 2 +- .../Extensions/SiteMappingListExtensions.cs | 2 +- EssentialCSharp.Web/Views/Shared/_Layout.cshtml | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/EssentialCSharp.Web.Tests/SiteMappingTests.cs b/EssentialCSharp.Web.Tests/SiteMappingTests.cs index 140e982c..7d20f845 100644 --- a/EssentialCSharp.Web.Tests/SiteMappingTests.cs +++ b/EssentialCSharp.Web.Tests/SiteMappingTests.cs @@ -5,7 +5,7 @@ namespace EssentialCSharp.Web.Tests; public class SiteMappingTests { static SiteMapping HelloWorldSiteMapping => new( - key: "hello-world", + keys: ["hello-world"], pagePath: [ "Chapters", @@ -22,7 +22,7 @@ public class SiteMappingTests ); static SiteMapping CSyntaxFundamentalsSiteMapping => new( - key: "c-syntax-fundamentals", + keys: ["c-syntax-fundamentals"], pagePath: [ "Chapters", @@ -52,7 +52,7 @@ public void FindHelloWorldWithAnchorSlugReturnsCorrectSiteMap() { SiteMapping? foundSiteMap = GetSiteMap().Find("hello-world#hello-world"); Assert.NotNull(foundSiteMap); - Assert.Equal(HelloWorldSiteMapping, foundSiteMap); + Assert.Equivalent(HelloWorldSiteMapping, foundSiteMap); } [Fact] @@ -60,7 +60,7 @@ public void FindCSyntaxFundamentalsWithSpacesReturnsCorrectSiteMap() { SiteMapping? foundSiteMap = GetSiteMap().Find("C# Syntax Fundamentals"); Assert.NotNull(foundSiteMap); - Assert.Equal(CSyntaxFundamentalsSiteMapping, foundSiteMap); + Assert.Equivalent(CSyntaxFundamentalsSiteMapping, foundSiteMap); } [Fact] @@ -68,7 +68,7 @@ public void FindCSyntaxFundamentalsWithSpacesAndAnchorReturnsCorrectSiteMap() { SiteMapping? foundSiteMap = GetSiteMap().Find("C# Syntax Fundamentals#hello-world"); Assert.NotNull(foundSiteMap); - Assert.Equal(CSyntaxFundamentalsSiteMapping, foundSiteMap); + Assert.Equivalent(CSyntaxFundamentalsSiteMapping, foundSiteMap); } [Fact] @@ -76,6 +76,6 @@ public void FindCSyntaxFundamentalsSanitizedWithAnchorReturnsCorrectSiteMap() { SiteMapping? foundSiteMap = GetSiteMap().Find("c-syntax-fundamentals#hello-world"); Assert.NotNull(foundSiteMap); - Assert.Equal(CSyntaxFundamentalsSiteMapping, foundSiteMap); + Assert.Equivalent(CSyntaxFundamentalsSiteMapping, foundSiteMap); } } diff --git a/EssentialCSharp.Web/Controllers/HomeController.cs b/EssentialCSharp.Web/Controllers/HomeController.cs index f20f0ee0..32237400 100644 --- a/EssentialCSharp.Web/Controllers/HomeController.cs +++ b/EssentialCSharp.Web/Controllers/HomeController.cs @@ -125,7 +125,7 @@ private string FlipPage(int currentChapter, int currentPage, bool next) return ""; } } - return $"{siteMap.Key}#{siteMap.AnchorId}"; + return $"{siteMap.Keys.First()}#{siteMap.AnchorId}"; } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] diff --git a/EssentialCSharp.Web/Extensions/SiteMappingListExtensions.cs b/EssentialCSharp.Web/Extensions/SiteMappingListExtensions.cs index 058e7ff7..28ae5643 100644 --- a/EssentialCSharp.Web/Extensions/SiteMappingListExtensions.cs +++ b/EssentialCSharp.Web/Extensions/SiteMappingListExtensions.cs @@ -16,7 +16,7 @@ public static class SiteMappingListExtensions } foreach (string? potentialMatch in key.GetPotentialMatches()) { - if (siteMappings.FirstOrDefault(x => x.Key == potentialMatch) is { } siteMap) + if (siteMappings.FirstOrDefault(x => x.Keys.Any(x => x == potentialMatch)) is { } siteMap) { return siteMap; } diff --git a/EssentialCSharp.Web/Views/Shared/_Layout.cshtml b/EssentialCSharp.Web/Views/Shared/_Layout.cshtml index 04a9367b..3815a994 100644 --- a/EssentialCSharp.Web/Views/Shared/_Layout.cshtml +++ b/EssentialCSharp.Web/Views/Shared/_Layout.cshtml @@ -277,18 +277,18 @@ .Select(i => new { Level = indentLevel, - Key = i.Key, - Href = $"{i.Key}#{i.AnchorId}", + Key = i.Keys.First(), + Href = $"{i.Keys.First()}#{i.AnchorId}", Title = i.RawHeading, // Any children of this node will be /after/ this node, // so skip any items that are /before/ the current node. - Items = GetItems(chapterItems.SkipWhile(q => i.Key != q.Key), indentLevel + 1) + Items = GetItems(chapterItems.SkipWhile(q => i.Keys.First() != q.Keys.First()), indentLevel + 1) }); var tocData = _SiteMappings.SiteMappings.GroupBy(x => x.ChapterNumber).OrderBy(x => x.Key).Select(x => new { Level = 0, - Key = x.First().Key, - Href = $"{x.First().Key}#{x.First().AnchorId}", + Key = x.First().Keys.First(), + Href = $"{x.First().Keys.First()}#{x.First().AnchorId}", Title = $"Chapter {x.Key}: {x.First().ChapterTitle}", Items = GetItems(x, 1) }); From 9a41866375935aeaf62124840a1ea471b4febab9 Mon Sep 17 00:00:00 2001 From: Joshua Lester Date: Sun, 17 Nov 2024 14:40:04 -0800 Subject: [PATCH 02/19] Working solution (only if EssentialCSharp.Common adds orderOnPage) --- .../Services/ISiteMappingService.cs | 1 + .../Services/SiteMappingDto.cs | 10 ++++ .../Services/SiteMappingService.cs | 38 +++++++++++++ .../Views/Shared/_Layout.cshtml | 53 ++++++++++--------- 4 files changed, 76 insertions(+), 26 deletions(-) create mode 100644 EssentialCSharp.Web/Services/SiteMappingDto.cs diff --git a/EssentialCSharp.Web/Services/ISiteMappingService.cs b/EssentialCSharp.Web/Services/ISiteMappingService.cs index fe8cfbc7..ac2a719e 100644 --- a/EssentialCSharp.Web/Services/ISiteMappingService.cs +++ b/EssentialCSharp.Web/Services/ISiteMappingService.cs @@ -3,4 +3,5 @@ public interface ISiteMappingService { IList SiteMappings { get; } + IEnumerable GetTocData(); } diff --git a/EssentialCSharp.Web/Services/SiteMappingDto.cs b/EssentialCSharp.Web/Services/SiteMappingDto.cs new file mode 100644 index 00000000..4e100403 --- /dev/null +++ b/EssentialCSharp.Web/Services/SiteMappingDto.cs @@ -0,0 +1,10 @@ +namespace EssentialCSharp.Web.Services; + +public class SiteMappingDto +{ + public required int Level { get; set; } + public required List Key { get; set; } + public required string Href { get; set; } + public required string Title { get; set; } + public required IEnumerable Items { get; set; } +} diff --git a/EssentialCSharp.Web/Services/SiteMappingService.cs b/EssentialCSharp.Web/Services/SiteMappingService.cs index c086596f..c5bdd49f 100644 --- a/EssentialCSharp.Web/Services/SiteMappingService.cs +++ b/EssentialCSharp.Web/Services/SiteMappingService.cs @@ -12,4 +12,42 @@ public SiteMappingService(IWebHostEnvironment webHostEnvironment) List? siteMappings = System.Text.Json.JsonSerializer.Deserialize>(File.OpenRead(path)) ?? throw new InvalidOperationException("No table of contents found"); SiteMappings = siteMappings; } + + public IEnumerable GetTocData() + { + return SiteMappings.GroupBy(x => x.ChapterNumber).OrderBy(x => x.Key).Select(x => + { + IEnumerable orderedX = x.OrderBy(i => i.PageNumber).ThenBy(i => i.OrderOnPage); + SiteMapping firstX = orderedX.First(); + return new SiteMappingDto() + { + Level = 0, + Key = [firstX.Keys.First()], + Href = $"{firstX.Keys.First()}#{firstX.AnchorId}", + Title = $"Chapter {x.Key}: {firstX.ChapterTitle}", + Items = GetItems(orderedX.Skip(1), 1) + }; + } + ); + } + + private static IEnumerable GetItems(IEnumerable chapterItems, int indentLevel) + { + return chapterItems + // Examine all items up until we move up to a level higher than where we're starting, + // which would indicate that we've reached the end of the entries nested under `indentationLevel` + .TakeWhile(i => i.IndentLevel >= indentLevel) + // Of all the multi-level descendants we found, take only those at the current level that we're wanting to render. + .Where(i => i.IndentLevel == indentLevel) + .Select(i => new SiteMappingDto() + { + Level = indentLevel, + Key = i.Keys, + Href = $"{i.Keys.First()}#{i.AnchorId}", + Title = i.RawHeading, + // Any children of this node will be /after/ this node, + // so skip any items that are /before/ the current node. + Items = GetItems(chapterItems.SkipWhile(q => i.Keys.First() != q.Keys.First()).Skip(1), indentLevel + 1) + }); + } } diff --git a/EssentialCSharp.Web/Views/Shared/_Layout.cshtml b/EssentialCSharp.Web/Views/Shared/_Layout.cshtml index 3815a994..284e02db 100644 --- a/EssentialCSharp.Web/Views/Shared/_Layout.cshtml +++ b/EssentialCSharp.Web/Views/Shared/_Layout.cshtml @@ -266,32 +266,33 @@ @await RenderSectionAsync("Scripts", required: false);