diff --git a/src/PostIt.Tests/LoginPageViewModelTests.cs b/src/PostIt.Tests/LoginPageViewModelTests.cs
index 38f4c76e..abd0916e 100644
--- a/src/PostIt.Tests/LoginPageViewModelTests.cs
+++ b/src/PostIt.Tests/LoginPageViewModelTests.cs
@@ -60,7 +60,7 @@ public class LoginPageViewModelTests
// Trailing slash on Authority is normalised away.
Assert.Equal(
- "https://yavsc.example.com/signin?ReturnUrl=~%2F&AllowRememberLogin=true",
+ "https://yavsc.example.com/Account/Register",
vm.RegisterUrl);
Assert.Equal(
"https://yavsc.example.com/Account/ForgotPassword",
diff --git a/src/PostIt.Tests/SettingsLoadTests.cs b/src/PostIt.Tests/SettingsLoadTests.cs
new file mode 100644
index 00000000..7e9f396c
--- /dev/null
+++ b/src/PostIt.Tests/SettingsLoadTests.cs
@@ -0,0 +1,36 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace PostIt.Tests;
+
+public class SettingsLoadTests
+{
+ ///
+ /// On the dev machine, the user-level settings file
+ /// (~/.config/PostIt/postit-settings.json) does not exist, so Load()
+ /// must fall back to the embedded default resource shipped inside
+ /// PostIt.dll.
+ ///
+ [Fact]
+ public async Task Load_falls_back_to_embedded_resource_when_user_file_missing()
+ {
+ // Skip if a user-level file exists (CI / different dev machines).
+ var userConfigPath = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ "PostIt",
+ "postit-settings.json");
+ if (File.Exists(userConfigPath))
+ {
+ return; // nothing to assert: user file wins.
+ }
+
+ var settings = new PostIt.Settings();
+ await settings.Load();
+
+ // The bundled postit-settings.json points at yavsc.pschneider.fr.
+ Assert.False(string.IsNullOrWhiteSpace(settings.Authentication?.Authority));
+ Assert.Equal("postit", settings.Authentication.ClientId);
+ }
+}
\ No newline at end of file
diff --git a/src/PostIt/PostIt/PostIt.csproj b/src/PostIt/PostIt/PostIt.csproj
index 8cf9465c..13645f88 100644
--- a/src/PostIt/PostIt/PostIt.csproj
+++ b/src/PostIt/PostIt/PostIt.csproj
@@ -26,4 +26,9 @@
PreserveNewest
+
+
+ PostIt.postit-settings.json
+
+
diff --git a/src/PostIt/PostIt/Settings/Settings.cs b/src/PostIt/PostIt/Settings/Settings.cs
index b17c341e..1d8e59c2 100644
--- a/src/PostIt/PostIt/Settings/Settings.cs
+++ b/src/PostIt/PostIt/Settings/Settings.cs
@@ -1,3 +1,5 @@
+using System.Runtime.CompilerServices;
+using System.Runtime.CompilerServices;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
@@ -9,6 +11,8 @@ using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
+[assembly: InternalsVisibleTo("PostIt.Tests")]
+
namespace PostIt;
public partial class Settings : ObservableObject
@@ -87,7 +91,16 @@ public partial class Settings : ObservableObject
if (!configFileInfo.Exists)
{
Console.Error.WriteLine($"🩎 Settings file not found at {configFileInfo.FullName}");
- return; // no settings file
+ // Only fall back to the embedded default when the in-memory
+ // settings haven't been populated yet. This protects callers
+ // (notably tests) that pre-load Settings with explicit values
+ // from being silently overwritten by the bundled default.
+ if (string.IsNullOrWhiteSpace(this.Authentication?.Authority)
+ && !TryLoadEmbeddedFallback())
+ {
+ Console.Error.WriteLine("🩎 No embedded default settings; running with empty configuration.");
+ }
+ return; // no user settings file
}
Console.WriteLine($"🔎 Loading settings from {configFileInfo.FullName}");
@@ -97,17 +110,49 @@ public partial class Settings : ObservableObject
using var stream = configFileInfo.OpenRead();
using var reader = new StreamReader(stream);
var json = await reader.ReadToEndAsync();
- if (string.IsNullOrWhiteSpace(json))
- {
- Console.Error.WriteLine("🩎 Settings file is empty.");
- return;
- }
+ ApplyJson(json, $"user file {configFileInfo.FullName}");
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine($"🩎 Error loading settings: {ex.Message}");
+ }
+ }
+ private bool TryLoadEmbeddedFallback()
+ {
+ const string ResourceName = "PostIt.postit-settings.json";
+ var assembly = typeof(Settings).Assembly;
+ using var stream = assembly.GetManifestResourceStream(ResourceName);
+ if (stream is null)
+ {
+ Console.Error.WriteLine($"🩎 Embedded resource {ResourceName} not found.");
+ return false;
+ }
+ using var reader = new StreamReader(stream);
+ var json = reader.ReadToEnd();
+ if (string.IsNullOrWhiteSpace(json))
+ {
+ Console.Error.WriteLine("🩎 Embedded settings resource is empty.");
+ return false;
+ }
+ Console.WriteLine($"🔎 Loading embedded default settings ({ResourceName}).");
+ ApplyJson(json, $"embedded resource {ResourceName}");
+ return true;
+ }
+
+ private void ApplyJson(string json, string source)
+ {
+ if (string.IsNullOrWhiteSpace(json))
+ {
+ Console.Error.WriteLine($"🩎 Settings payload is empty (source: {source}).");
+ return;
+ }
+ try
+ {
var settings = JsonSerializer.Deserialize(json);
-
if (settings is null)
{
- Console.Error.WriteLine("🩎 Settings file is invalid.");
+ Console.Error.WriteLine($"🩎 Settings payload is invalid (source: {source}).");
return;
}
this.Authentication = settings.Authentication;
@@ -115,11 +160,10 @@ public partial class Settings : ObservableObject
this.ApiUrl = settings.ApiUrl;
this.RedirectUri = string.IsNullOrWhiteSpace(settings.RedirectUri) ? DefaultLoopbackRedirectUri : settings.RedirectUri;
this.Scopes = settings.Scopes;
-
}
catch (Exception ex)
{
- Console.Error.WriteLine($"🩎 Error loading settings: {ex.Message}");
+ Console.Error.WriteLine($"🩎 Error applying settings from {source}: {ex.Message}");
}
}
}
\ No newline at end of file
diff --git a/src/PostIt/PostIt/ViewModels/LoginPageViewModel.cs b/src/PostIt/PostIt/ViewModels/LoginPageViewModel.cs
index 6a22cbe7..70877733 100644
--- a/src/PostIt/PostIt/ViewModels/LoginPageViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/LoginPageViewModel.cs
@@ -13,12 +13,12 @@ public partial class LoginPageViewModel : ViewModelBase
public string Password { get; set; }
///
- /// URL of the Yavsc.Org register/sign-in page for new users.
+ /// URL of the Yavsc.Org account-registration page.
/// Derived from 's Authority.
/// Empty when the authority is not configured.
///
public string RegisterUrl =>
- BuildExternalUrl("/signin?ReturnUrl=~%2F&AllowRememberLogin=true");
+ BuildExternalUrl("/Account/Register");
///
/// URL of the Yavsc.Org password-reset page (open to anonymous users).