From 4d45b2097e4ee720e9df46216b36cfea1dcc8d1f Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Sat, 29 Apr 2023 11:46:15 +0200 Subject: [PATCH 1/8] feat: added reading indicator --- .../Components/CommentSection.razor | 1 - .../Components/ReadingIndicator.razor | 63 ++++++++++++++++++ .../Components/ReadingIndicator.razor.js | 41 ++++++++++++ .../ShowBlogPost/ShowBlogPostPage.razor | 66 ++++++++++--------- 4 files changed, 138 insertions(+), 33 deletions(-) create mode 100644 src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor create mode 100644 src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.js diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/CommentSection.razor b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/CommentSection.razor index f59efe07..d3705db9 100644 --- a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/CommentSection.razor +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/CommentSection.razor @@ -18,5 +18,4 @@ @code { private bool MultipleCommentPlugins => AppConfiguration.IsDisqusEnabled && AppConfiguration.IsGiscusEnabled; - } \ No newline at end of file diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor new file mode 100644 index 00000000..90d2c740 --- /dev/null +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor @@ -0,0 +1,63 @@ +@inject IJSRuntime JSRuntime + +
+ + + + +
+ + + +@code { + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await using var _ = await JSRuntime.InvokeAsync("import", "./Features/ShowBlogPost/Components/ReadingIndicator.razor.js"); + await JSRuntime.InvokeVoidAsync("initCircularReadingProgress"); + } + } +} diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.js b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.js new file mode 100644 index 00000000..e21e2223 --- /dev/null +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.js @@ -0,0 +1,41 @@ +let progressTimeout; + +function getContentHeight() { + const content = document.querySelector(".blog-inner-content"); + if (!content) { + return 0; + } + const contentRect = content.getBoundingClientRect(); + return contentRect.height; +} + +function showProgressIndicator(progressContainer) { + progressContainer.classList.add("visible"); + progressContainer.style.animation = "none"; +} + +function hideProgressIndicator(progressContainer) { + progressContainer.style.animation = "fadeOut 0.5s forwards"; +} + +window.initCircularReadingProgress = () => { + const progressBar = document.getElementById("progressBar"); + const progressContainer = progressBar.closest(".progress-container"); + + window.addEventListener("scroll", () => { + clearTimeout(progressTimeout); + + const contentHeight = getContentHeight(); + const windowHeight = document.documentElement.clientHeight; + const scrollAmount = document.documentElement.scrollTop; + const maxScrollAmount = contentHeight - windowHeight; + const progress = Math.max(0, Math.min(100, (scrollAmount / maxScrollAmount) * 100)); + progressBar.style.strokeDashoffset = 100 - progress; + + showProgressIndicator(progressContainer); + + progressTimeout = setTimeout(() => { + hideProgressIndicator(progressContainer); + }, 2000); + }); +}; \ No newline at end of file diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor index 168b799c..046e7640 100644 --- a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor @@ -19,40 +19,42 @@ else AbsolutePreviewImageUrl="@OgDataImage" Description="@(Markdown.ToPlainText(BlogPost.ShortDescription))" Keywords="@Tags"> -
-
-
-
-

@BlogPost.Title

-
-
@BlogPost.UpdatedDate.ToString("dd/MM/yyyy")
- @if (BlogPost.Tags != null && BlogPost.Tags.Any()) - { - - @foreach (var tag in BlogPost.Tags.Select(t => t.Content)) - { - @tag - } - - } -
+
+
+
+
+

@BlogPost.Title

+
+
@BlogPost.UpdatedDate.ToString("dd/MM/yyyy")
+ @if (BlogPost.Tags != null && BlogPost.Tags.Any()) + { + + @foreach (var tag in BlogPost.Tags.Select(t => t.Content)) + { + @tag + } + + } +
-
- -
+
+ +
-
- @(MarkdownConverter.ToMarkupString(BlogPost.Content)) -
-
-
- - -
- - -
-
+
+ @(MarkdownConverter.ToMarkupString(BlogPost.Content)) +
+
+
+ + +
+ + +
+
+ + } @code { From 4f0f4bf9da0de8794203daf584dd17d7d85013bf Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Sat, 29 Apr 2023 12:19:56 +0200 Subject: [PATCH 2/8] Use requestAnimationFrame --- .../Components/ReadingIndicator.razor.js | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.js b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.js index e21e2223..f608bb13 100644 --- a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.js +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.js @@ -1,4 +1,5 @@ let progressTimeout; +let rafId; function getContentHeight() { const content = document.querySelector(".blog-inner-content"); @@ -11,18 +12,21 @@ function getContentHeight() { function showProgressIndicator(progressContainer) { progressContainer.classList.add("visible"); - progressContainer.style.animation = "none"; + progressContainer.style.animation = 'none'; } function hideProgressIndicator(progressContainer) { - progressContainer.style.animation = "fadeOut 0.5s forwards"; + progressContainer.style.animation = 'fadeOut 0.5s forwards'; + setTimeout(() => { + progressContainer.classList.remove('visible'); + }, 500); } window.initCircularReadingProgress = () => { - const progressBar = document.getElementById("progressBar"); + const progressBar = document.getElementById('progressBar'); const progressContainer = progressBar.closest(".progress-container"); - window.addEventListener("scroll", () => { + const onScroll = () => { clearTimeout(progressTimeout); const contentHeight = getContentHeight(); @@ -37,5 +41,13 @@ window.initCircularReadingProgress = () => { progressTimeout = setTimeout(() => { hideProgressIndicator(progressContainer); }, 2000); + + rafId = null; + }; + + window.addEventListener('scroll', () => { + if (!rafId) { + rafId = requestAnimationFrame(onScroll); + } }); -}; \ No newline at end of file +}; From 74474851cb253384b3063b207d083b1eb42646b8 Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Sat, 29 Apr 2023 13:31:59 +0200 Subject: [PATCH 3/8] Make the component more flexible and move css in its own file --- .../Components/ReadingIndicator.razor | 51 +++---------------- .../Components/ReadingIndicator.razor.css | 40 +++++++++++++++ .../Components/ReadingIndicator.razor.js | 9 ++-- .../ShowBlogPost/ShowBlogPostPage.razor | 2 +- 4 files changed, 51 insertions(+), 51 deletions(-) create mode 100644 src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.css diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor index 90d2c740..22448b63 100644 --- a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor @@ -1,63 +1,24 @@ @inject IJSRuntime JSRuntime -
+
- + private ElementReference progressContainer; -@code { protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { await using var _ = await JSRuntime.InvokeAsync("import", "./Features/ShowBlogPost/Components/ReadingIndicator.razor.js"); - await JSRuntime.InvokeVoidAsync("initCircularReadingProgress"); + await JSRuntime.InvokeVoidAsync("initCircularReadingProgress", ContainerCssSelector, progressContainer); } } } diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.css b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.css new file mode 100644 index 00000000..5d4177ec --- /dev/null +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.css @@ -0,0 +1,40 @@ +.progress-container { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 1000; + opacity: 0; + transition: opacity 0.5s; +} + +.progress-container.visible { + opacity: 1; +} + +@keyframes fadeOut { + to { + opacity: 0; + } + } + +.progress-circle { + width: 50px; + height: 50px; +} + +.progress-bg { + fill: none; + stroke: #f3f3f3; + stroke-width: 4; +} + +.progress-bar { + fill: none; + stroke: #4caf50; + stroke-width: 4; + stroke-linecap: round; + transform-origin: center; + transform: rotate(-90deg); + stroke-dasharray: 100, 100; + stroke-dashoffset: 100; +} \ No newline at end of file diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.js b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.js index f608bb13..32877e31 100644 --- a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.js +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.js @@ -1,8 +1,8 @@ let progressTimeout; let rafId; -function getContentHeight() { - const content = document.querySelector(".blog-inner-content"); +function getContentHeight(className) { + const content = document.querySelector(className); if (!content) { return 0; } @@ -22,14 +22,13 @@ function hideProgressIndicator(progressContainer) { }, 500); } -window.initCircularReadingProgress = () => { +window.initCircularReadingProgress = (parentContainer, progressContainer) => { const progressBar = document.getElementById('progressBar'); - const progressContainer = progressBar.closest(".progress-container"); const onScroll = () => { clearTimeout(progressTimeout); - const contentHeight = getContentHeight(); + const contentHeight = getContentHeight(parentContainer); const windowHeight = document.documentElement.clientHeight; const scrollAmount = document.documentElement.scrollTop; const maxScrollAmount = contentHeight - windowHeight; diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor index 046e7640..70ec0d4e 100644 --- a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor @@ -54,7 +54,7 @@ else
- + } @code { From cf1a15489e16cb37ff19c39ad520d750b9a137c4 Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Sat, 29 Apr 2023 14:51:26 +0200 Subject: [PATCH 4/8] Added AppConfig to toggle --- Readme.md | 54 ++++++++++--------- src/LinkDotNet.Blog.Web/AppConfiguration.cs | 2 + .../AppConfigurationFactory.cs | 1 + .../ShowBlogPost/ShowBlogPostPage.razor | 6 ++- src/LinkDotNet.Blog.Web/appsettings.json | 3 +- .../Web/AppConfigurationFactoryTests.cs | 2 + .../ShowBlogPost/ShowBlogPostPageTests.cs | 29 ++++++++-- 7 files changed, 65 insertions(+), 32 deletions(-) diff --git a/Readme.md b/Readme.md index d3b1be38..55027dfe 100644 --- a/Readme.md +++ b/Readme.md @@ -58,39 +58,41 @@ The appsettings.json file has a lot of options to customize the content of the b "Shortname": "blog" }, "KofiToken": "ABC123", - "GithubSponsorName": "your-tag-here" + "GithubSponsorName": "your-tag-here", + "ShowReadingIndicator": true } ``` -| Property | Type | Description | -| ------------------------- | -------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| BlogName | string | Name of your blog. Is used in the navbar and is used as the title of the page. Will not be shown when `BlogBrandUrl` is set | -| BlogBrandUrl | string | The url to an image which is used as a brand image in the navigation bar. If not set or `null` the `BlogName` will be shown | -| Social | node | Represents all possible linked social accounts | -| GithubAccountUrl | string | Url to your github account. If not set it is not shown in the introduction card | -| LinkedInAccountUrl | string | Url to your LinkedIn account. If not set it is not shown in the introduction card | -| TwitterAccountUrl | string | Url to your Twitter account. If not set it is not shown in the introduction card | -| Introduction | | Is used for the introduction part of the blog | -| Description | MarkdownString | Small introduction text for yourself. This is also used for `` tag. For this the markup will be converted to plain text | -| BackgroundUrl | string | Url or path to the background image. (Optional) | -| ProfilePictureUrl | string | Url or path to your profile picture | +| Property | Type | Description | +| ------------------------- | -------------- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| BlogName | string | Name of your blog. Is used in the navbar and is used as the title of the page. Will not be shown when `BlogBrandUrl` is set | +| BlogBrandUrl | string | The url to an image which is used as a brand image in the navigation bar. If not set or `null` the `BlogName` will be shown | +| Social | node | Represents all possible linked social accounts | +| GithubAccountUrl | string | Url to your github account. If not set it is not shown in the introduction card | +| LinkedInAccountUrl | string | Url to your LinkedIn account. If not set it is not shown in the introduction card | +| TwitterAccountUrl | string | Url to your Twitter account. If not set it is not shown in the introduction card | +| Introduction | | Is used for the introduction part of the blog | +| Description | MarkdownString | Small introduction text for yourself. This is also used for `` tag. For this the markup will be converted to plain text | +| BackgroundUrl | string | Url or path to the background image. (Optional) | +| ProfilePictureUrl | string | Url or path to your profile picture | | PersistenceProvider | string | Declares the type of the storage provider (one of the following: `SqlServer`, `Sqlite`, `RavenDb`, `InMemory`, `MySql`). More in-depth explanation down below | -| ConnectionString | string | Is used for connection to a database. Not used when `InMemoryStorageProvider` is used | -| DatabaseName | string | Name of the database. Only used with `RavenDbStorageProvider` | -| Auth0 | | Configuration for setting up Auth0 | -| Domain | string | See more details here: https://manage.auth0.com/dashboard/ | -| ClientId | string | See more details here: https://manage.auth0.com/dashboard/ | -| ClientSecret | string | See more details here: https://manage.auth0.com/dashboard/ | -| BlogPostsPerPage | int | Gives the amount of blog posts loaded and display per page. For more the user has to use the navigation | -| AboutMeProfileInformation | node | Sets information for the About Me Page. If omitted the page is disabled completely | -| Name | string | Name, which is displayed on top of the profile card | -| Heading | string | Displayed under the name. For example job title | -| ProfilePictureUrl | string | Displayed profile picture | +| ConnectionString | string | Is used for connection to a database. Not used when `InMemoryStorageProvider` is used | +| DatabaseName | string | Name of the database. Only used with `RavenDbStorageProvider` | +| Auth0 | | Configuration for setting up Auth0 | +| Domain | string | See more details here: https://manage.auth0.com/dashboard/ | +| ClientId | string | See more details here: https://manage.auth0.com/dashboard/ | +| ClientSecret | string | See more details here: https://manage.auth0.com/dashboard/ | +| BlogPostsPerPage | int | Gives the amount of blog posts loaded and display per page. For more the user has to use the navigation | +| AboutMeProfileInformation | node | Sets information for the About Me Page. If omitted the page is disabled completely | +| Name | string | Name, which is displayed on top of the profile card | +| Heading | string | Displayed under the name. For example job title | +| ProfilePictureUrl | string | Displayed profile picture | | Giscus | node | Enables the comment section via giscus. If left empty the comment secion will not be shown. For more information checkout the section about comments down below | | Disqus | node | Enables the comment section via disqus. If left empty the comment secion will not be shown. For more information checkout the section about comments down below | -| KofiToken | string | Enables the "Buy me a Coffee" button of Kofi. To aquire the token head down to the "Kofi" section | -| GithubSponsorName | string | Enables the "Github Sponsor" button which redirects to GitHub. Only pass in the user name instead of the url. | +| KofiToken | string | Enables the "Buy me a Coffee" button of Kofi. To aquire the token head down to the "Kofi" section | +| GithubSponsorName | string | Enables the "Github Sponsor" button which redirects to GitHub. Only pass in the user name instead of the url. | +| ShowReadingIndicator | boolean | If set to `true` (default) a circle indicates the progress when a user reads a blog post (without comments). | ## Storage Provider Currently, there are 5 Storage-Provider: diff --git a/src/LinkDotNet.Blog.Web/AppConfiguration.cs b/src/LinkDotNet.Blog.Web/AppConfiguration.cs index 6c9200b5..b069b1df 100644 --- a/src/LinkDotNet.Blog.Web/AppConfiguration.cs +++ b/src/LinkDotNet.Blog.Web/AppConfiguration.cs @@ -38,4 +38,6 @@ public record AppConfiguration public string GithubSponsorName { get; init; } public bool IsGithubSponsorAvailable => !string.IsNullOrEmpty(GithubSponsorName); + + public bool ShowReadingIndicator { get; init; } } diff --git a/src/LinkDotNet.Blog.Web/AppConfigurationFactory.cs b/src/LinkDotNet.Blog.Web/AppConfigurationFactory.cs index 7c7a863d..12b01022 100644 --- a/src/LinkDotNet.Blog.Web/AppConfigurationFactory.cs +++ b/src/LinkDotNet.Blog.Web/AppConfigurationFactory.cs @@ -28,6 +28,7 @@ public static AppConfiguration Create(IConfiguration config) DisqusConfiguration = disqus, KofiToken = config[nameof(AppConfiguration.KofiToken)], GithubSponsorName = config[nameof(AppConfiguration.GithubSponsorName)], + ShowReadingIndicator = config.GetValue(nameof(AppConfiguration.ShowReadingIndicator)), }; return configuration; diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor index 70ec0d4e..7e5b7f9c 100644 --- a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor @@ -7,6 +7,7 @@ @inject IRepository BlogPostRepository @inject IJSRuntime JsRuntime @inject IUserRecordService UserRecordService +@inject AppConfiguration AppConfiguration @if (BlogPost == null) { @@ -54,7 +55,10 @@ else - + @if (AppConfiguration.ShowReadingIndicator) + { + + } } @code { diff --git a/src/LinkDotNet.Blog.Web/appsettings.json b/src/LinkDotNet.Blog.Web/appsettings.json index fd7bd4c3..02755ccc 100644 --- a/src/LinkDotNet.Blog.Web/appsettings.json +++ b/src/LinkDotNet.Blog.Web/appsettings.json @@ -32,5 +32,6 @@ "Name": "Steven Giesel", "Heading": "Software Engineer", "ProfilePictureUrl": "assets/profile-picture.webp", - } + }, + "ShowReadingIndicator": true, } \ No newline at end of file diff --git a/tests/LinkDotNet.Blog.UnitTests/Web/AppConfigurationFactoryTests.cs b/tests/LinkDotNet.Blog.UnitTests/Web/AppConfigurationFactoryTests.cs index 3ac7a460..5a461fbd 100644 --- a/tests/LinkDotNet.Blog.UnitTests/Web/AppConfigurationFactoryTests.cs +++ b/tests/LinkDotNet.Blog.UnitTests/Web/AppConfigurationFactoryTests.cs @@ -32,6 +32,7 @@ public void ShouldMapFromAppConfiguration() { "Disqus:Shortname", "blog" }, { "KofiToken", "ABC" }, { "GithubSponsorName", "linkdotnet" }, + { "ShowReadingIndicator", "true" }, }; var configuration = new ConfigurationBuilder() .AddInMemoryCollection(inMemorySettings) @@ -64,6 +65,7 @@ public void ShouldMapFromAppConfiguration() appConfiguration.DisqusConfiguration.Shortname.Should().Be("blog"); appConfiguration.KofiToken.Should().Be("ABC"); appConfiguration.GithubSponsorName.Should().Be("linkdotnet"); + appConfiguration.ShowReadingIndicator.Should().BeTrue(); } [Theory] diff --git a/tests/LinkDotNet.Blog.UnitTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs b/tests/LinkDotNet.Blog.UnitTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs index c1201ef8..1874ef26 100644 --- a/tests/LinkDotNet.Blog.UnitTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs +++ b/tests/LinkDotNet.Blog.UnitTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs @@ -22,9 +22,8 @@ public void ShouldShowLoadingAnimation() { const string blogPostId = "2"; var repositoryMock = new Mock>(); - JSInterop.Mode = JSRuntimeMode.Loose; + SetupMocks(); Services.AddScoped(_ => repositoryMock.Object); - Services.AddScoped(_ => Mock.Of()); repositoryMock.Setup(r => r.GetByIdAsync(blogPostId)) .Returns(async () => { @@ -80,7 +79,6 @@ public void ShouldUseFallbackAsOgDataIfAvailable(string preview, string fallback [Fact] public void ShowTagWithLinksWhenAvailable() { - JSInterop.Mode = JSRuntimeMode.Loose; var repositoryMock = new Mock>(); var blogPost = new BlogPostBuilder() .WithTags("tag1") @@ -100,7 +98,6 @@ public void ShowTagWithLinksWhenAvailable() [Fact] public void ShowNotShowTagsWhenNotSet() { - JSInterop.Mode = JSRuntimeMode.Loose; var repositoryMock = new Mock>(); var blogPost = new BlogPostBuilder() .Build(); @@ -114,8 +111,32 @@ public void ShowNotShowTagsWhenNotSet() cut.FindAll(".goto-tag").Should().BeEmpty(); } + [Theory] + [InlineData(true, 1)] + [InlineData(false, 0)] + public void ShowReadingIndicatorWhenEnabled(bool isEnabled, int count) + { + var appConfiguration = new AppConfiguration + { + ShowReadingIndicator = isEnabled, + }; + var repositoryMock = new Mock>(); + var blogPost = new BlogPostBuilder() + .Build(); + repositoryMock.Setup(r => r.GetByIdAsync("1")).ReturnsAsync(blogPost); + Services.AddScoped(_ => repositoryMock.Object); + SetupMocks(); + Services.AddScoped(_ => appConfiguration); + + var cut = RenderComponent( + p => p.Add(s => s.BlogPostId, "1")); + + cut.FindComponents().Count.Should().Be(count); + } + private void SetupMocks() { + JSInterop.Mode = JSRuntimeMode.Loose; Services.AddScoped(_ => Mock.Of()); Services.AddScoped(_ => Mock.Of()); Services.AddScoped(_ => Mock.Of()); From ffa64daf582e5a74977099b025cc65935b12f401 Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Sat, 29 Apr 2023 14:59:16 +0200 Subject: [PATCH 5/8] Make json valid --- src/LinkDotNet.Blog.Web/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LinkDotNet.Blog.Web/appsettings.json b/src/LinkDotNet.Blog.Web/appsettings.json index 02755ccc..3b772c95 100644 --- a/src/LinkDotNet.Blog.Web/appsettings.json +++ b/src/LinkDotNet.Blog.Web/appsettings.json @@ -33,5 +33,5 @@ "Heading": "Software Engineer", "ProfilePictureUrl": "assets/profile-picture.webp", }, - "ShowReadingIndicator": true, + "ShowReadingIndicator": true } \ No newline at end of file From 36494c09efd5f2e1ed958b3b48f96e049b325c38 Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Sat, 29 Apr 2023 15:01:17 +0200 Subject: [PATCH 6/8] Simplified Test --- .../Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/LinkDotNet.Blog.UnitTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs b/tests/LinkDotNet.Blog.UnitTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs index 1874ef26..0a60231a 100644 --- a/tests/LinkDotNet.Blog.UnitTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs +++ b/tests/LinkDotNet.Blog.UnitTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs @@ -112,9 +112,9 @@ public void ShowNotShowTagsWhenNotSet() } [Theory] - [InlineData(true, 1)] - [InlineData(false, 0)] - public void ShowReadingIndicatorWhenEnabled(bool isEnabled, int count) + [InlineData(true)] + [InlineData(false)] + public void ShowReadingIndicatorWhenEnabled(bool isEnabled) { var appConfiguration = new AppConfiguration { @@ -131,7 +131,7 @@ public void ShowReadingIndicatorWhenEnabled(bool isEnabled, int count) var cut = RenderComponent( p => p.Add(s => s.BlogPostId, "1")); - cut.FindComponents().Count.Should().Be(count); + cut.HasComponent().Should().Be(isEnabled); } private void SetupMocks() From 9156bc282c2342a15ea06010013794ddbb774350 Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Sat, 29 Apr 2023 15:04:56 +0200 Subject: [PATCH 7/8] Fade out slower --- .../Features/ShowBlogPost/Components/ReadingIndicator.razor.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.css b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.css index 5d4177ec..9a16b014 100644 --- a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.css +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/ReadingIndicator.razor.css @@ -4,7 +4,7 @@ right: 20px; z-index: 1000; opacity: 0; - transition: opacity 0.5s; + transition: opacity 1.5s; } .progress-container.visible { From a1c3b0fd3ac34519a7696e39e2b22458b7375e46 Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Sat, 29 Apr 2023 15:06:02 +0200 Subject: [PATCH 8/8] Fix editorconfig --- .editorconfig | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.editorconfig b/.editorconfig index c3ed7849..91034b63 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,34 +3,29 @@ root = true [*] indent_style = tab indent_size = tab -tab_size = 4 +tab_width = 4 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.cs] -tab_size = 4 indent_style = space [*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}] -tab_size = 2 +tab_width = 2 [*.{htm,html,js,jsm,ts,tsx,css,sass,scss,less,svg,vue}] -tab_size = 2 +tab_width = 2 [*.json] -tab_size = 2 - -[*.{ps1,psm1}] -tab_size = 4 +tab_width = 2 [*.sh] -tab_size = 4 end_of_line = lf [*.{yml,yaml}] indent_style = space -tab_size = 2 +tab_width = 2 [*.md] trim_trailing_whitespace = false