Repository navigation
Feature/01 black on white #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
maayjayy
wants to merge
17
commits into
develop
Choose a base branch
from
feature/01-black-on-white
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+578
−17
Open
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
1df4671
feat(desktop): always use light theme
maayjayy 72fadab
feat(desktop): show pages in the main window, starting with login
maayjayy 97dee3a
feat(desktop): navigate from login to home on sign-in
maayjayy c0a790b
feat(desktop): add placeholder pages, nav bar and sign out
maayjayy ecfff19
style(desktop): simplify login view layout
maayjayy cbc7014
feat(desktop): add API client and dependency injection
maayjayy 94f6f36
feat(desktop): show API status on the home page
maayjayy f94a3cd
fix: handle all errors in Home API check
maayjayy 3833ade
fix: resolve a new ApiClient on each Home navigation
maayjayy 63031a6
docs(desktop): add XML documentation comments
maayjayy a84ab2a
refactor: make page titles read-only
maayjayy 83a4a65
refactor: move Home status messages into constants
maayjayy 97cbbc0
refactor: make home title read-only
maayjayy db88f88
fix: validate API config on startup
maayjayy 9c76152
fix: cancel the Home Api check when leaving the page
maayjayy e19eea0
feat(desktop): add design-time data for the IDE previewer
maayjayy 748647e
docs: fix typo in HomeView OnUnloaded comment
maayjayy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| using MoissonMontreal.Desktop.ViewModels; | ||
|
|
||
| namespace MoissonMontreal.Desktop.Design; | ||
|
|
||
| /// <summary> | ||
| /// Ready-made ViewModels for the IDE previewer. Never used when the app runs. | ||
| /// </summary> | ||
| public static class DesignData | ||
| { | ||
| /// <summary> | ||
| /// Main window ViewModel backed by a <see cref="FakeApiClient"/>. | ||
| /// </summary> | ||
| public static MainViewModel Main { get; } = new(() => new FakeApiClient()); | ||
|
|
||
| /// <summary> | ||
| /// Home page ViewModel backed by a <see cref="FakeApiClient"/>. | ||
| /// </summary> | ||
| public static HomeViewModel Home { get; } = new(new FakeApiClient()); | ||
|
|
||
| /// <summary> | ||
| /// Login page ViewModel whose sign-in does nothing. | ||
| /// </summary> | ||
| public static LoginViewModel Login { get; } = new(() => { }); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using MoissonMontreal.Desktop.Services; | ||
|
|
||
| namespace MoissonMontreal.Desktop.Design; | ||
|
|
||
| /// <summary> | ||
| /// Fake API client for the IDE previewer. Always reports the API as online, without any network call. | ||
| /// </summary> | ||
| public class FakeApiClient : IApiClient | ||
| { | ||
| /// <inheritdoc/> | ||
| public Task<bool> IsApiReachableAsync(CancellationToken cancellationToken = default) | ||
| { | ||
| return Task.FromResult(true); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| using System.Threading.Tasks; | ||
| using System.Net.Http; | ||
| using System.Threading; | ||
|
|
||
| namespace MoissonMontreal.Desktop.Services; | ||
|
|
||
| /// <summary> | ||
| /// Calls the central API over HTTP. | ||
| /// </summary> | ||
| public class ApiClient : IApiClient | ||
| { | ||
| /// <summary> | ||
| /// HTTP client configured in <c>App</c> with the API's base address and timeout. | ||
| /// </summary> | ||
| private readonly HttpClient _httpClient; | ||
|
|
||
| /// <summary> | ||
| /// Creates the client. | ||
| /// </summary> | ||
| /// <param name="httpClient">HTTP client provided by the dependency injection container.</param> | ||
| public ApiClient(HttpClient httpClient) | ||
| { | ||
| _httpClient = httpClient; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public async Task<bool> IsApiReachableAsync(CancellationToken cancellationToken = default) | ||
| { | ||
| try | ||
| { | ||
| // Any endpoint that answers successfully proves the API is reachable. | ||
| using var response = await _httpClient.GetAsync("WeatherForecast", cancellationToken); | ||
| return response.IsSuccessStatusCode; | ||
| } | ||
| catch (HttpRequestException) | ||
| { | ||
| return false; | ||
| } | ||
| catch (TaskCanceledException) | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace MoissonMontreal.Desktop.Services; | ||
|
|
||
| /// <summary> | ||
| /// Talks to the central Moisson Montréal API. | ||
| /// </summary> | ||
| public interface IApiClient | ||
| { | ||
| /// <summary> | ||
| /// Checks whether the API answers. | ||
| /// </summary> | ||
| /// <param name="cancellationToken">Lets the caller stop the check early (for example when leaving the page). If cancelled, the method returns <c>false</c>.</param> | ||
| /// <returns><c>true</c> if the API answered successfully; <c>false</c> if it is unreachable or returned an error.</returns> | ||
| Task<bool> IsApiReachableAsync(CancellationToken cancellationToken = default); | ||
| } | ||
|
|
||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
src/MoissonMontreal.Desktop/ViewModels/DonationsViewModel.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| namespace MoissonMontreal.Desktop.ViewModels; | ||
|
|
||
| /// <summary> | ||
| /// Logic of the donations page (placeholder for now). | ||
| /// </summary> | ||
| public class DonationsViewModel : ViewModelBase | ||
| { | ||
| /// <summary> | ||
| /// Title shown at the top of the donations page. | ||
| /// </summary> | ||
| public string Title => "Donations"; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| namespace MoissonMontreal.Desktop.ViewModels; | ||
|
|
||
| /// <summary> | ||
| /// Logic of the donors page (placeholder for now). | ||
| /// </summary> | ||
| public class DonorsViewModel : ViewModelBase | ||
| { | ||
| /// <summary> | ||
| /// Title shown at the top of the donors page. | ||
| /// </summary> | ||
| public string Title => "Donors"; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| using System; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using CommunityToolkit.Mvvm.ComponentModel; | ||
| using CommunityToolkit.Mvvm.Input; | ||
| using MoissonMontreal.Desktop.Services; | ||
|
|
||
| namespace MoissonMontreal.Desktop.ViewModels; | ||
|
|
||
| /// <summary> | ||
| /// Logic of the home page: shows whether the API is reachable. | ||
| /// </summary> | ||
| public partial class HomeViewModel : ViewModelBase | ||
| { | ||
| /// <summary> | ||
| /// Client used to check whether the API is reachable. | ||
| /// </summary> | ||
| private readonly IApiClient _apiClient; | ||
|
|
||
| /// <summary> | ||
| /// Status message shown while the API check is running. | ||
| /// </summary> | ||
| private const string CheckingApiMessage = "Checking the API..."; | ||
|
|
||
| /// <summary> | ||
| /// Status message shown when the API cannot be reached. | ||
| /// </summary> | ||
| private const string ServerUnavailableMessage = "Server unavailable"; | ||
|
|
||
| /// <summary> | ||
| /// Status message shown when the API answered successfully. | ||
| /// </summary> | ||
| private const string ApiOnlineMessage = "API online"; | ||
|
|
||
| /// <summary> | ||
| /// Title shown at the top of the home page. | ||
| /// </summary> | ||
| public string Title => "Welcome"; | ||
|
|
||
| /// <summary> | ||
| /// API status message shown to the user. | ||
| /// </summary> | ||
| [ObservableProperty] | ||
| public partial string ApiStatus { get; set; } = CheckingApiMessage; | ||
|
|
||
| /// <summary> | ||
| /// Creates the home page. The API check starts when the view is loaded. | ||
| /// </summary> | ||
| /// <param name="apiClient">Client used to call the API.</param> | ||
| public HomeViewModel(IApiClient apiClient) | ||
| { | ||
| _apiClient = apiClient; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Checks the API and updates <see cref="ApiStatus"/>. Any error shows <see cref="ServerUnavailableMessage"/> instead of crashing the app. | ||
| /// </summary> | ||
| /// <param name="cancellationToken">Cancelled when the user leaves the home page, so the check doesn't keep running.</param> | ||
| [RelayCommand] | ||
| private async Task CheckApiAsync(CancellationToken cancellationToken) | ||
| { | ||
| ApiStatus = CheckingApiMessage; | ||
| try | ||
| { | ||
| var isReachable = await _apiClient.IsApiReachableAsync(cancellationToken); | ||
| ApiStatus = isReachable ? ApiOnlineMessage : ServerUnavailableMessage; | ||
| } | ||
| catch (Exception) | ||
| { | ||
| ApiStatus = ServerUnavailableMessage; | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The typed client registered with
AddHttpClient<IApiClient, ApiClient>is transient, butMainViewModelis 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
IHttpClientFactoryor aFunc<IApiClient>intoMainViewModel, or registerHomeViewModelin the container and resolve it on each navigation.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 3833ade:
MainViewModelnow takes aFunc<IApiClient>instead of anIApiClient, registered asservices.AddSingleton<Func<IApiClient>>(sp => () => sp.GetRequiredService<IApiClient>()).ShowHomecalls it on each navigation, so everyHomeViewModelgets a freshApiClientfromIHttpClientFactoryand the handler gets rotated.