Repository navigation
Conversation
There was a problem hiding this comment.
Thanks for the PR, the flow works well.
Before we merge, there are a few changes to make once #43 is merged. First, please update your branch with develop so it includes the new /health endpoint. Then, in ApiClient.cs, call health instead of WeatherForecast, since #43 removes that endpoint (see my inline comment).
Please also update the testing section of the PR. With #43 , the API needs the database to start, so you have to run docker compose up -d db and add the connection string to your user-secrets first. It would also be good to add a test where the database is stopped, to check that the app shows "Server unavailable".
| try | ||
| { | ||
| // Any endpoint that answers successfully proves the API is reachable. | ||
| using var response = await _httpClient.GetAsync("WeatherForecast"); |
| public HomeViewModel(IApiClient apiClient) | ||
| { | ||
| _apiClient = apiClient; | ||
| CheckApiCommand.Execute(null); |
There was a problem hiding this comment.
Crash risk: the async command is started from the constructor without being awaited, and IsApiReachableAsync only catches HttpRequestException and TaskCanceledException. If any other exception escapes (e.g. InvalidOperationException or ObjectDisposedException), AsyncRelayCommand rethrows it on the UI thread and the app crashes on sign-in or when clicking Home.
Suggestion: add a fallback catch (Exception) (in CheckApiAsync or in ApiClient), and ideally trigger the check from the view (Loaded) rather than from the constructor.
| client.BaseAddress = new Uri(apiBaseUrl); | ||
| client.Timeout = TimeSpan.FromSeconds(15); | ||
| }); | ||
| services.AddSingleton<MainViewModel>(); |
There was a problem hiding this comment.
The typed client registered with AddHttpClient<IApiClient, ApiClient> is transient, but MainViewModel is a singleton that holds onto it for the app's whole lifetime (captive dependency). The handler is never rotated, so if the API's DNS/IP changes during a long session, the app will be stuck on "Server unavailable".
Suggestion: inject IHttpClientFactory or a Func<IApiClient> into MainViewModel, or register HomeViewModel in the container and resolve it on each navigation.
| { | ||
| var configuration = new ConfigurationBuilder() | ||
| .SetBasePath(AppContext.BaseDirectory) | ||
| .AddJsonFile("appsettings.json") |
There was a problem hiding this comment.
appsettings.json is required (optional: false by default) and new Uri(apiBaseUrl) isn't validated. If the file is missing (e.g. in a publish folder) or the URL has no scheme (localhost:5297), we get a FileNotFoundException or UriFormatException before any window exists, and the app closes with no message.
Suggestion: use Uri.TryCreate(apiBaseUrl, UriKind.Absolute, out var uri) with a clear error message, and make sure appsettings.json is copied to the output directory (CopyToOutputDirectory).
| try | ||
| { | ||
| // Any endpoint that answers successfully proves the API is reachable. | ||
| using var response = await _httpClient.GetAsync("WeatherForecast"); |
There was a problem hiding this comment.
The check calls WeatherForecast, which comes from the template and will be removed. After that, the app will show "Server unavailable" even though the API is up. Also, with a BaseUrl like http://host/api (no trailing /), the request goes to http://host/WeatherForecast.
Suggestion: add builder.Services.AddHealthChecks() + app.MapHealthChecks("/health") on the API side and call health here.
| [RelayCommand] | ||
| private void ShowHome() | ||
| { | ||
| CurrentPage = new HomeViewModel(_apiClient); |
There was a problem hiding this comment.
Every click on Home creates a new HomeViewModel, which fires a new request, and previous ones are never cancelled. If the API is down, several 15-second requests can run in parallel.
Suggestion: keep a single HomeViewModel instance, or pass a CancellationToken that gets cancelled when navigating away.
| [NotifyPropertyChangedFor(nameof(IsSignedIn))] | ||
| public partial ViewModelBase CurrentPage {get; set;} | ||
|
|
||
| public bool IsSignedIn => CurrentPage is not LoginViewModel; |
There was a problem hiding this comment.
IsSignedIn is derived from the current page's type. As soon as we add another page reachable before sign-in (forgot password, splash screen…), the nav bar and Sign out button will show up incorrectly.
Suggestion: keep an actual session state (e.g. a boolean or an auth service) and derive IsSignedIn from it. This can wait until auth is wired up.
| x:DataType="vm:MainViewModel" | ||
| Icon="/Assets/avalonia-logo.ico" | ||
| Title="MoissonMontreal.Desktop"> | ||
| Title="Moisson Montréal"> |
There was a problem hiding this comment.
The <Design.DataContext> block was removed. Since MainViewModel no longer has a parameterless constructor, the IDE previewer has no data, here or in the new views.
Suggestion (optional): add a Design.DataContext backed by a fake IApiClient.
| public partial string Title { get; set; } = "Welcome"; | ||
|
|
||
| [ObservableProperty] | ||
| public partial string ApiStatus { get; set; } = "Checking the API..."; |
There was a problem hiding this comment.
The "Checking the API..." string is set here and again at the start of CheckApiAsync. Since the check runs right away, we could drop the initializer, or move the status messages into constants.
| public partial class DonationsViewModel : ViewModelBase | ||
| { | ||
| [ObservableProperty] | ||
| public partial string Title { get; set; } = "Donations"; |
There was a problem hiding this comment.
Title never changes, so it doesn't need to be an [ObservableProperty]. public string Title => "Donations"; is enough. Same for DonorsViewModel and LoginViewModel.
Closes #1
Overview
Provisional black-on-white desktop app, so the team can test the flow end to end while the final design is in progress. It will be replaced by the design system's components later.
ApiClientservice (IApiClient) registered with dependency injection; the API base URL is read fromappsettings.jsonTesting
dotnet builddotnet testScreenshots / Screencasts
Login

Home, API online

Navigation (Donations)

Home, API stopped

Checklist
Notes
GET /WeatherForecast, since it is the only endpoint ondevelop. This PR does not change the Api project, so it is independent of Feature/11 api postgres #43. Once Feature/11 api postgres #43 is merged (it removesWeatherForecastand adds/health), the endpoint inApiClient.csneeds a one-line change.