diff --git a/CHANGELOG.md b/CHANGELOG.md index af5479790..e1a3be3a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,31 +1,6 @@ # Changelog -## [1.0.8-rc25] - unstable -### Added - -* [Yavsc.Api] Notification du performer à la création d'un rendez-vous - depuis PostIt, selon ses préférences de contact, sans supprimer l'envoi Org. -* [Yavsc.Api][Yavsc.Server] Notification du performer à la signature - client d'un devis (JSON ou image), enregistrée pour l'accueil PostIt - et transmise par e-mail et SignalR. -* [Yavsc.Api] Nouveau point d'entrée `GET /api/v1/notifications/me` - pour exposer les notifications publiques et ciblées à PostIt sur la - Home page ; les notifications d'administration restent filtrées. -* [PostIt] Affichage des notifications sur la page d'accueil, avec - actualisation, indicateur de chargement et message d'erreur. Les - notifications publiques sont accessibles sans connexion et la liste - est rechargée lors d'un changement de session. - -### Changed - -### Fixed - -* [Yavsc.Api][Yavsc.Blogs] Les appels API et la bande passante sont - désormais enregistrés côté serveur pour les requêtes authentifiées : - le middleware de télémétrie est maintenant branché après - l'authentification, ce qui permet de comptabiliser `ApiCalls` et - `BandwidthMb` au lieu d'enregistrer des zéros. ## [1.0.8-rc24] - unstable diff --git a/contrib/template.appsettings-blogs.json b/contrib/template.appsettings-blogs.json index 6f8fa02bc..f522feaca 100644 --- a/contrib/template.appsettings-blogs.json +++ b/contrib/template.appsettings-blogs.json @@ -8,7 +8,7 @@ "CorsAllowedOrigins": [ "https://preprod-api.$DOMAIN" ], - "UserFilesRoot": "files" + "Blog": "blog" }, "ConnectionStrings": { "YavscConnection": "Server=localhost;Port=5432;Database=lame-db-name;Username=lame-user-name;Password=lame-password;" diff --git a/contrib/template.appsettings-org.json b/contrib/template.appsettings-org.json index c330f5f32..76203d9e9 100644 --- a/contrib/template.appsettings-org.json +++ b/contrib/template.appsettings-org.json @@ -30,7 +30,7 @@ "EMail": "paul@pschneider.fr" }, "Quota": 200000000, - "UserFilesRoot": "files", + "Blog": "blog", "Avatars": "avatars", "Bills": "bills", "TempDir": "temp", diff --git a/src/PostIt/PostIt.Tests/HomePageNotificationsTests.cs b/src/PostIt/PostIt.Tests/HomePageNotificationsTests.cs deleted file mode 100644 index 667ea599a..000000000 --- a/src/PostIt/PostIt.Tests/HomePageNotificationsTests.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System.Net.Http; -using Avalonia.Controls; -using Avalonia.Headless.XUnit; -using Avalonia.VisualTree; -using PostIt.Services; -using PostIt.ViewModels; -using PostIt.Views; -using Yavsc.Abstract.Models.Messaging; -using Yavsc.Api.Client; - -namespace PostIt.Tests; - -public class HomePageNotificationsTests -{ - [Fact] - public async Task Refresh_loads_public_notifications_from_the_business_host() - { - var transport = new NotificationTransport(); - var vm = CreateViewModel(transport); - transport.Response = Task.FromResult(new List { Note(1, "Information") }); - - await vm.RefreshNotificationsAsync(); - - Assert.Equal("https://business.example/api/v1/notifications", transport.LastPath); - Assert.True(transport.WasAnonymous); - Assert.Equal("Information", Assert.Single(vm.Notifications).title); - Assert.False(vm.IsLoadingNotifications); - Assert.False(vm.HasNoNotifications); - Assert.Null(vm.NotificationsError); - } - - [Fact] - public async Task Refresh_replaces_notifications_and_distinguishes_empty_from_failed() - { - var transport = new NotificationTransport(); - var vm = CreateViewModel(transport); - vm.Notifications.Add(Note(1, "Old")); - await vm.RefreshNotificationsAsync(); - - Assert.Empty(vm.Notifications); - Assert.True(vm.HasNoNotifications); - - transport.Response = Task.FromException>(new HttpRequestException("Server unavailable")); - await vm.RefreshNotificationsAsync(); - - Assert.Empty(vm.Notifications); - Assert.False(vm.HasNoNotifications); - Assert.Contains("Server unavailable", vm.NotificationsError); - Assert.False(vm.IsLoadingNotifications); - - transport.Response = Task.FromResult(new List { Note(2, "Recovered") }); - await vm.RefreshNotificationsAsync(); - Assert.Null(vm.NotificationsError); - Assert.Equal(2, Assert.Single(vm.Notifications).Id); - } - - [Fact] - public async Task Refresh_ignores_an_older_response_after_a_newer_refresh() - { - var transport = new NotificationTransport(); - var vm = CreateViewModel(transport); - var oldResponse = new TaskCompletionSource>(); - transport.Response = oldResponse.Task; - var oldLoad = vm.RefreshNotificationsAsync(); - Assert.True(vm.IsLoadingNotifications); - - transport.Response = Task.FromResult(new List { Note(2, "Current") }); - await vm.RefreshNotificationsAsync(); - oldResponse.SetResult(new List { Note(1, "Stale") }); - await oldLoad; - - Assert.Equal(2, Assert.Single(vm.Notifications).Id); - Assert.False(vm.IsLoadingNotifications); - } - - [AvaloniaFact] - public async Task Login_and_logout_reload_notifications_and_discard_in_flight_private_data() - { - var tokenPath = Path.Combine(Path.GetTempPath(), $"postit-notifications-{Guid.NewGuid():N}.json"); - var settings = new Settings(); - var store = new TokenStore(tokenPath); - store.Save(new RefreshTokenRecord("test-access-token", "test-refresh-token", - DateTimeOffset.UtcNow.AddHours(1), null)); - await using var api = new YavscApiClient(settings, store); - try - { - var session = new SessionStatusViewModel { Api = api }; - var transport = new NotificationTransport(); - var vm = CreateViewModel(transport, session); - session.Refresh(); - Assert.False(transport.WasAnonymous); - Assert.EndsWith("/notifications/me", transport.LastPath); - var privateResponse = new TaskCompletionSource>(); - transport.Response = privateResponse.Task; - var privateLoad = vm.RefreshNotificationsAsync(); - Assert.False(transport.WasAnonymous); - Assert.EndsWith("/notifications/me", transport.LastPath); - - vm.Notifications.Add(Note(9, "Private")); - transport.Response = Task.FromResult(new List { Note(2, "Public") }); - session.SetError("Disconnected"); - Assert.Equal(2, Assert.Single(vm.Notifications).Id); - Assert.True(transport.WasAnonymous); - - privateResponse.SetResult(new List { Note(1, "Late private data") }); - await privateLoad; - Assert.Equal(2, Assert.Single(vm.Notifications).Id); - } - finally - { - File.Delete(tokenPath); - } - } - - [AvaloniaFact] - public async Task Home_page_loads_on_navigation_and_renders_wrapped_titles_and_bodies() - { - var transport = new NotificationTransport - { - Response = Task.FromResult(new List { Note(1, "Notification title") }) - }; - var vm = CreateViewModel(transport); - var page = new HomePage { DataContext = vm }; - var nav = new NavigationPage(); - var window = new Window { Content = nav, Width = 320, Height = 450 }; - try - { - window.Show(); - await nav.PushAsync(page); - window.UpdateLayout(); - - Assert.Single(vm.Notifications); - var texts = page.GetVisualDescendants().OfType().ToArray(); - Assert.Contains(texts, t => t.Text == "Notification title" - && t.TextWrapping == Avalonia.Media.TextWrapping.Wrap); - Assert.Contains(texts, t => t.Text == "Notification body" - && t.TextWrapping == Avalonia.Media.TextWrapping.Wrap); - var refresh = Assert.Single(page.GetVisualDescendants().OfType