diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bef40e30..1c7c92595 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,12 +13,14 @@
* [PostIt] Les avatars ne sont plus relies en string sur `Image.Source`: ils sont telecharges et lies en `Bitmap`.
* [Yavsc.Api.Client] `ActivityApiClient` accepte une base d'avatar dediee et construit les URLs avatar depuis l'autorite d'identification.
+* [PostIt] Les clients Activites/Billing utilisent maintenant `ApiUrl` en lecture dynamique: un changement via Parametres prend effet sans redemarrer l'application (apres sauvegarde et rafraichissement de la page).
* [PostIt] Le header de `MainPage` n'utilise plus `ScrollViewer`; remplacement par une barre de commandes basee sur `WrapPanel`.
* [PostIt] Alignement de la navigation blogs: renommage `PushMainPageAsync` -> `PushBlogsPageAsync` et ajustement de `HomePageViewModel`.
### Fixed
-néant
+* [PostIt.Android] Correction d'un 404 sur la page Activites au premier lancement: la configuration embarquee pointait `ApiUrl` vers le host Blogs au lieu de l'API metier.
+* [PostIt] Correction du bouton Sauver de la page Parametres: binding vers `SaveCommand` pour persister correctement `ApiUrl`/`BlogsApiUrl`.
## [1.0.8-rc11] - unstable
diff --git a/src/PostIt/PostIt.Tests/ActivitiesPageViewModelTests.cs b/src/PostIt/PostIt.Tests/ActivitiesPageViewModelTests.cs
index 2e8d3e544..04cc0f314 100644
--- a/src/PostIt/PostIt.Tests/ActivitiesPageViewModelTests.cs
+++ b/src/PostIt/PostIt.Tests/ActivitiesPageViewModelTests.cs
@@ -39,6 +39,22 @@ public class ActivitiesPageViewModelTests
Assert.Equal("https://business.example/api/v1/billing/Rdv", api.Paths[3]);
}
+ [Fact]
+ public async Task ActivityApiClient_uses_updated_business_base_without_restart()
+ {
+ var api = new StubActivityApi();
+ var baseUrl = "https://business-a.example/api/v1/";
+ var client = new ActivityApiClient(api, () => baseUrl);
+
+ await client.GetCatalogAsync(ct: TestContext.Current.CancellationToken);
+
+ baseUrl = "https://business-b.example/api/v1/";
+ await client.GetUsersAsync("brush", TestContext.Current.CancellationToken);
+
+ Assert.Equal("https://business-a.example/api/v1/activity/catalog", api.Paths[0]);
+ Assert.Equal("https://business-b.example/api/v1/activity/brush/users", api.Paths[1]);
+ }
+
[Fact]
public async Task RefreshAsync_loads_first_activity_then_specialization_performers()
{
diff --git a/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs b/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs
index f95f31e30..69f0a2dcd 100644
--- a/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs
+++ b/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs
@@ -26,9 +26,9 @@ public static class ServiceCollectionHelpers
var userSearchClient = new UserSearchClient(api, settings.BlogsApiUrl);
var activityClient = new ActivityApiClient(
api,
- settings.ApiUrl,
- settings.Authentication?.Authority);
- var billingClient = new BillingApiClient(api, settings.ApiUrl);
+ () => settings.ApiUrl,
+ () => settings.Authentication?.Authority);
+ var billingClient = new BillingApiClient(api, () => settings.ApiUrl);
var userDirectory = new UserDirectory(userSearchClient);
// Vues
diff --git a/src/PostIt/PostIt/Views/SettingsPage.axaml b/src/PostIt/PostIt/Views/SettingsPage.axaml
index 611b89954..bb19dfa3c 100644
--- a/src/PostIt/PostIt/Views/SettingsPage.axaml
+++ b/src/PostIt/PostIt/Views/SettingsPage.axaml
@@ -61,16 +61,11 @@
Click="ChooseAvatar_Click"
Margin="0,0,8,0"/>
-
+
diff --git a/src/PostIt/PostIt/postit-settings sample.json b/src/PostIt/PostIt/postit-settings sample.json
index 6d8da8faf..e23839b1f 100644
--- a/src/PostIt/PostIt/postit-settings sample.json
+++ b/src/PostIt/PostIt/postit-settings sample.json
@@ -5,7 +5,7 @@
},
"RedirectUri": "postit://callback",
"DarkMode": false,
- "ApiUrl": "https://blogs.pschneider.fr/api/v1/",
+ "ApiUrl": "https://api.pschneider.fr/api/v1/",
"Scopes": [
"openid",
"profile",
diff --git a/src/PostIt/PostIt/postit-settings.json b/src/PostIt/PostIt/postit-settings.json
index 079479515..080fed178 100644
--- a/src/PostIt/PostIt/postit-settings.json
+++ b/src/PostIt/PostIt/postit-settings.json
@@ -5,7 +5,7 @@
},
"RedirectUri": "postit://callback",
"DarkMode": true,
- "ApiUrl": "https://blogs.pschneider.fr/api/v1/",
+ "ApiUrl": "https://api.pschneider.fr/api/v1/",
"Scopes": [
"openid",
"profile",
diff --git a/src/Yavsc.Api.Client/ActivityApiClient.cs b/src/Yavsc.Api.Client/ActivityApiClient.cs
index 52fad1a49..8199de9d4 100644
--- a/src/Yavsc.Api.Client/ActivityApiClient.cs
+++ b/src/Yavsc.Api.Client/ActivityApiClient.cs
@@ -19,19 +19,29 @@ public sealed class ActivityApiClient
private const string PathPrefix = "activity";
private readonly IYavscApiClient _api;
- private readonly Uri _baseAddress;
- private readonly Uri _avatarBaseAddress;
+ private readonly Func _businessBaseAddress;
+ private readonly Func _avatarBaseAddress;
public ActivityApiClient(IYavscApiClient api, string businessBaseAddress, string? avatarBaseAddress = null)
+ : this(
+ api,
+ () => businessBaseAddress,
+ () => avatarBaseAddress)
+ {
+ }
+
+ public ActivityApiClient(
+ IYavscApiClient api,
+ Func businessBaseAddress,
+ Func? avatarBaseAddress = null)
{
_api = api ?? throw new ArgumentNullException(nameof(api));
- if (string.IsNullOrEmpty(businessBaseAddress))
- throw new ArgumentException("Base address is required.", nameof(businessBaseAddress));
+ _businessBaseAddress = businessBaseAddress ?? throw new ArgumentNullException(nameof(businessBaseAddress));
+ _avatarBaseAddress = avatarBaseAddress ?? (() => null);
- _baseAddress = new Uri(businessBaseAddress, UriKind.Absolute);
- _avatarBaseAddress = string.IsNullOrWhiteSpace(avatarBaseAddress)
- ? _baseAddress
- : new Uri(avatarBaseAddress, UriKind.Absolute);
+ // Validate initial values early to fail fast on invalid setup.
+ _ = ResolveBusinessBaseAddress();
+ _ = ResolveAvatarBaseAddress();
}
public Task> GetCatalogAsync(
@@ -65,7 +75,7 @@ public sealed class ActivityApiClient
public string BuildAvatarXsUrl(string? userName)
{
- var siteRoot = new Uri(_avatarBaseAddress, "/");
+ var siteRoot = new Uri(ResolveAvatarBaseAddress(), "/");
if (string.IsNullOrWhiteSpace(userName))
{
@@ -75,5 +85,22 @@ public sealed class ActivityApiClient
return new Uri(siteRoot, $"avatars/{Uri.EscapeDataString(userName)}.xs.png").ToString();
}
- private string Absolute(string relativePath) => new Uri(_baseAddress, relativePath).ToString();
+ private string Absolute(string relativePath) => new Uri(ResolveBusinessBaseAddress(), relativePath).ToString();
+
+ private Uri ResolveBusinessBaseAddress()
+ {
+ var raw = _businessBaseAddress();
+ if (string.IsNullOrWhiteSpace(raw))
+ throw new InvalidOperationException("Business base address is required.");
+
+ return new Uri(raw, UriKind.Absolute);
+ }
+
+ private Uri ResolveAvatarBaseAddress()
+ {
+ var raw = _avatarBaseAddress();
+ return string.IsNullOrWhiteSpace(raw)
+ ? ResolveBusinessBaseAddress()
+ : new Uri(raw, UriKind.Absolute);
+ }
}
diff --git a/src/Yavsc.Api.Client/BillingApiClient.cs b/src/Yavsc.Api.Client/BillingApiClient.cs
index 35a6722d5..c3a0be103 100644
--- a/src/Yavsc.Api.Client/BillingApiClient.cs
+++ b/src/Yavsc.Api.Client/BillingApiClient.cs
@@ -20,15 +20,20 @@ public sealed class BillingApiClient
private const string PathPrefix = "billing";
private readonly IYavscApiClient _api;
- private readonly Uri _baseAddress;
+ private readonly Func _businessBaseAddress;
public BillingApiClient(IYavscApiClient api, string businessBaseAddress)
+ : this(api, () => businessBaseAddress)
+ {
+ }
+
+ public BillingApiClient(IYavscApiClient api, Func businessBaseAddress)
{
_api = api ?? throw new ArgumentNullException(nameof(api));
- if (string.IsNullOrWhiteSpace(businessBaseAddress))
- throw new ArgumentException("Base address is required.", nameof(businessBaseAddress));
+ _businessBaseAddress = businessBaseAddress ?? throw new ArgumentNullException(nameof(businessBaseAddress));
- _baseAddress = new Uri(businessBaseAddress, UriKind.Absolute);
+ // Validate initial value early to fail fast on invalid setup.
+ _ = ResolveBusinessBaseAddress();
}
public Task CreateAsync(string billingCode, object payload, CancellationToken ct = default)
@@ -121,7 +126,16 @@ public sealed class BillingApiClient
ct: ct);
}
- private string Absolute(string relativePath) => new Uri(_baseAddress, relativePath).ToString();
+ private string Absolute(string relativePath) => new Uri(ResolveBusinessBaseAddress(), relativePath).ToString();
+
+ private Uri ResolveBusinessBaseAddress()
+ {
+ var raw = _businessBaseAddress();
+ if (string.IsNullOrWhiteSpace(raw))
+ throw new InvalidOperationException("Business base address is required.");
+
+ return new Uri(raw, UriKind.Absolute);
+ }
private static BillingQueryDetailsDto MapRdv(RdvQueryResponse dto, string billingCode)
{