diff --git a/Directory.Build.props b/Directory.Build.props index e3276bc..24fd94d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.4.1 + 1.4.2 net10.0 enable enable diff --git a/src/NoteBookmark.BlazorApp.Tests/Tests/PostReaderTests.cs b/src/NoteBookmark.BlazorApp.Tests/Tests/PostReaderTests.cs index b480ab3..382a6eb 100644 --- a/src/NoteBookmark.BlazorApp.Tests/Tests/PostReaderTests.cs +++ b/src/NoteBookmark.BlazorApp.Tests/Tests/PostReaderTests.cs @@ -34,7 +34,7 @@ public PostReaderTests() } [Fact] - public void PostReader_RendersTitleAndContentAndSlider() + public void PostReader_RendersTitleAndContentAndSlidersAndBackButtonsAtTopAndBottom() { var cut = Render(ps => ps.Add(p => p.PostId, "p1")); @@ -44,23 +44,43 @@ public void PostReader_RendersTitleAndContentAndSlider() cut.Markup.Should().Contain("reader-content"); cut.Markup.Should().Contain("Text size:"); - var slider = cut.FindComponent>(); - slider.Instance.Min.Should().Be(8); - slider.Instance.Max.Should().Be(56); + var sliders = cut.FindComponents>(); + sliders.Should().HaveCount(2); + sliders[0].Instance.Min.Should().Be(8); + sliders[0].Instance.Max.Should().Be(56); + sliders[1].Instance.Min.Should().Be(8); + sliders[1].Instance.Max.Should().Be(56); + + var backButtons = cut.FindComponents() + .Where(b => b.Instance.Title == "Back to posts") + .ToList(); + backButtons.Should().HaveCount(2); } [Fact] - public void PostReader_SliderValueChange_UpdatesContentFontSize() + public void PostReader_TopSliderValueChange_UpdatesContentFontSize() { var cut = Render(ps => ps.Add(p => p.PostId, "p1")); var contentDivBefore = cut.Find("div.reader-content"); contentDivBefore.GetAttribute("style").Should().Contain("font-size: 16px;"); - var slider = cut.FindComponent>(); - cut.InvokeAsync(() => slider.Instance.ValueChanged.InvokeAsync(24)); + var sliders = cut.FindComponents>(); + cut.InvokeAsync(() => sliders[0].Instance.ValueChanged.InvokeAsync(24)); var contentDivAfter = cut.Find("div.reader-content"); contentDivAfter.GetAttribute("style").Should().Contain("font-size: 24px;"); } + + [Fact] + public void PostReader_BottomSliderValueChange_UpdatesContentFontSize() + { + var cut = Render(ps => ps.Add(p => p.PostId, "p1")); + + var sliders = cut.FindComponents>(); + cut.InvokeAsync(() => sliders[1].Instance.ValueChanged.InvokeAsync(20)); + + var contentDivAfter = cut.Find("div.reader-content"); + contentDivAfter.GetAttribute("style").Should().Contain("font-size: 20px;"); + } } diff --git a/src/NoteBookmark.BlazorApp.Tests/Tests/PostsTests.cs b/src/NoteBookmark.BlazorApp.Tests/Tests/PostsTests.cs index 70ee8ea..6c3c6db 100644 --- a/src/NoteBookmark.BlazorApp.Tests/Tests/PostsTests.cs +++ b/src/NoteBookmark.BlazorApp.Tests/Tests/PostsTests.cs @@ -133,5 +133,34 @@ public void Posts_ReadPostButton_IsAlwaysRendered() cut.Markup.Should().Contain("Read post"); } + + [Fact] + public void Posts_DisplaysSyncProgress_WhenSyncProgressChangedFired() + { + var cut = Render(); + + cut.InvokeAsync(() => + { + _dataServiceMock.Raise(s => s.SyncProgressChanged += null, new SyncProgressEventArgs(1, 6, "Downloading 1 of 6 posts...")); + }); + + cut.Markup.Should().Contain("Downloading 1 of 6 posts..."); + var progress = cut.FindComponent(); + progress.Instance.Value.Should().Be(1); + progress.Instance.Max.Should().Be(6); + } + + [Fact] + public void Posts_DisplaysCleaningStatus_WhenSyncProgressChangedFired() + { + var cut = Render(); + + cut.InvokeAsync(() => + { + _dataServiceMock.Raise(s => s.SyncProgressChanged += null, new SyncProgressEventArgs(0, 0, "Cleaning...")); + }); + + cut.Markup.Should().Contain("Cleaning..."); + } } diff --git a/src/NoteBookmark.MauiApp.Tests/SyncServiceTests.cs b/src/NoteBookmark.MauiApp.Tests/SyncServiceTests.cs index 6cc0536..1ad1706 100644 --- a/src/NoteBookmark.MauiApp.Tests/SyncServiceTests.cs +++ b/src/NoteBookmark.MauiApp.Tests/SyncServiceTests.cs @@ -466,7 +466,9 @@ public async Task SyncAsync_ShouldRaiseSyncProgressChanged_WhenDownloadingPostHt await _sut.SyncAsync(); progressEvents.Should().NotBeEmpty(); - progressEvents.Should().Contain(e => e.Status.Contains("Downloading offline text")); + progressEvents.Should().Contain(e => e.Status == "Cleaning..."); + progressEvents.Should().Contain(e => e.Status == "Downloading 1 of 2 posts..." && e.Current == 1 && e.Total == 2); + progressEvents.Should().Contain(e => e.Status == "Downloading 2 of 2 posts..." && e.Current == 2 && e.Total == 2); progressEvents.Last().Status.Should().Be("Synchronization complete!"); } } diff --git a/src/NoteBookmark.MauiApp/Data/SyncService.cs b/src/NoteBookmark.MauiApp/Data/SyncService.cs index 4f52f3d..495b749 100644 --- a/src/NoteBookmark.MauiApp/Data/SyncService.cs +++ b/src/NoteBookmark.MauiApp/Data/SyncService.cs @@ -243,6 +243,8 @@ private async Task SyncHtmlAsync() var postMap = posts.ToDictionary(p => p.Id ?? p.RowKey); var cachedIds = localHtmlStorageService.GetCachedPostIds().ToHashSet(); + SyncProgressChanged?.Invoke(this, new SyncProgressEventArgs(0, 0, "Cleaning...")); + // Prune cached HTML for posts that are read or no longer exist foreach (var cachedId in cachedIds) { @@ -258,7 +260,7 @@ private async Task SyncHtmlAsync() if (total > 0) { - SyncProgressChanged?.Invoke(this, new SyncProgressEventArgs(0, total, $"Downloading offline text (0/{total})...")); + SyncProgressChanged?.Invoke(this, new SyncProgressEventArgs(0, total, $"Downloading 0 of {total} posts...")); for (int i = 0; i < unreadToDownload.Count; i++) { @@ -279,7 +281,7 @@ private async Task SyncHtmlAsync() } int current = i + 1; - SyncProgressChanged?.Invoke(this, new SyncProgressEventArgs(current, total, $"Downloading offline text ({current}/{total})...")); + SyncProgressChanged?.Invoke(this, new SyncProgressEventArgs(current, total, $"Downloading {current} of {total} posts...")); } } } diff --git a/src/NoteBookmark.MauiApp/NoteBookmark.MauiApp.csproj b/src/NoteBookmark.MauiApp/NoteBookmark.MauiApp.csproj index fce28f8..680d90d 100644 --- a/src/NoteBookmark.MauiApp/NoteBookmark.MauiApp.csproj +++ b/src/NoteBookmark.MauiApp/NoteBookmark.MauiApp.csproj @@ -44,9 +44,9 @@ c5m.notebookmark.mauiapp - 1.3.3 - 6 - 1.3.3 + 1.4.2 + 7 + 1.4.2 None diff --git a/src/NoteBookmark.SharedUI/Components/Pages/PostReader.razor b/src/NoteBookmark.SharedUI/Components/Pages/PostReader.razor index 57af22a..ff1005f 100644 --- a/src/NoteBookmark.SharedUI/Components/Pages/PostReader.razor +++ b/src/NoteBookmark.SharedUI/Components/Pages/PostReader.razor @@ -9,10 +9,16 @@ @(post?.Title ?? "Reading...") - - Back + + + Back + + Text size: + @(textSize)px + + @if (isLoading) @@ -51,6 +57,10 @@ + Back + Text size: @(textSize)px diff --git a/src/NoteBookmark.SharedUI/Components/Pages/Posts.razor b/src/NoteBookmark.SharedUI/Components/Pages/Posts.razor index 3308e1d..0dbebea 100644 --- a/src/NoteBookmark.SharedUI/Components/Pages/Posts.razor +++ b/src/NoteBookmark.SharedUI/Components/Pages/Posts.razor @@ -115,14 +115,22 @@ try { + isSyncing = true; await client.SyncAsync(); await LoadPosts(); - StateHasChanged(); } catch (Exception) { // Ignore background sync errors } + finally + { + isSyncing = false; + syncProgressStatus = string.Empty; + syncProgressCurrent = 0; + syncProgressTotal = 0; + StateHasChanged(); + } } private async Task OpenUrlInNewWindow(string? url) @@ -287,6 +295,10 @@ finally { isSyncing = false; + syncProgressStatus = string.Empty; + syncProgressCurrent = 0; + syncProgressTotal = 0; + StateHasChanged(); } }