diff --git a/TransactionProcessor.Mobile.BusinessLogic.Tests/ViewModelTests/Support/ViewLogsPageViewModelTests.cs b/TransactionProcessor.Mobile.BusinessLogic.Tests/ViewModelTests/Support/ViewLogsPageViewModelTests.cs index a852369d..d65fd47d 100644 --- a/TransactionProcessor.Mobile.BusinessLogic.Tests/ViewModelTests/Support/ViewLogsPageViewModelTests.cs +++ b/TransactionProcessor.Mobile.BusinessLogic.Tests/ViewModelTests/Support/ViewLogsPageViewModelTests.cs @@ -52,6 +52,19 @@ public async Task SupportPageViewModel_UploadLogsCommand_Execute_IsExecuted(){ this.ViewModel.LogMessages.Count.ShouldBe(1); } + [Fact] + public async Task ViewLogsPageViewModel_LoadLogMessages_RaisesPropertyChanged(){ + this.Mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List(){ + new LogMessage() + })); + String? changedPropertyName = null; + this.ViewModel.PropertyChanged += (_, args) => changedPropertyName = args.PropertyName; + + await this.ViewModel.LoadLogMessages(); + + changedPropertyName.ShouldBe(nameof(ViewLogsPageViewModel.LogMessages)); + } + [Fact] public void ViewLogsPageViewModel_BackButtonCommand_Execute_IsExecuted() { @@ -59,4 +72,4 @@ public void ViewLogsPageViewModel_BackButtonCommand_Execute_IsExecuted() this.NavigationService.Verify(n => n.GoBack(), Times.Once); } -} \ No newline at end of file +} diff --git a/TransactionProcessor.Mobile.BusinessLogic/ViewModels/Support/ViewLogsPageViewModel.cs b/TransactionProcessor.Mobile.BusinessLogic/ViewModels/Support/ViewLogsPageViewModel.cs index 76b38509..abfb764b 100644 --- a/TransactionProcessor.Mobile.BusinessLogic/ViewModels/Support/ViewLogsPageViewModel.cs +++ b/TransactionProcessor.Mobile.BusinessLogic/ViewModels/Support/ViewLogsPageViewModel.cs @@ -12,6 +12,7 @@ public class ViewLogsPageViewModel : ExtendedBaseViewModel #region Fields private readonly IMediator Mediator; + private List logMessages = new(); #endregion @@ -31,7 +32,10 @@ public ViewLogsPageViewModel(IMediator mediator, #region Properties - public List LogMessages { get; private set; } + public List LogMessages { + get => this.logMessages; + private set => this.SetProperty(ref this.logMessages, value); + } #endregion @@ -59,4 +63,4 @@ public async Task LoadLogMessages() { } #endregion -} \ No newline at end of file +} diff --git a/TransactionProcessor.Mobile/Extensions/MauiAppBuilderExtensions.cs b/TransactionProcessor.Mobile/Extensions/MauiAppBuilderExtensions.cs index 44ec364c..cf794de8 100644 --- a/TransactionProcessor.Mobile/Extensions/MauiAppBuilderExtensions.cs +++ b/TransactionProcessor.Mobile/Extensions/MauiAppBuilderExtensions.cs @@ -60,8 +60,8 @@ public static MauiAppBuilder ConfigureAppServices(this MauiAppBuilder builder) { return factory.CreateClient("default"); }); - builder.Services.AddSingleton(new Func(configSetting => { - IApplicationCache applicationCache = MauiProgram.Container.Services.GetService(); + builder.Services.AddSingleton>(serviceProvider => configSetting => { + IApplicationCache applicationCache = serviceProvider.GetService(); if (applicationCache != null) return configSetting switch { @@ -75,7 +75,7 @@ public static MauiAppBuilder ConfigureAppServices(this MauiAppBuilder builder) { }; return null; - })); + }); builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -103,50 +103,50 @@ public static MauiAppBuilder ConfigureAppServices(this MauiAppBuilder builder) { } private static MauiAppBuilder RegisterConfigurationService(this MauiAppBuilder builder) { - builder.Services.AddSingleton(new Func(useTrainingMode => + builder.Services.AddSingleton>(serviceProvider => useTrainingMode => { return useTrainingMode switch { true => new TrainingConfigurationService(), - _ => MauiProgram.Container.Services.GetService() + _ => serviceProvider.GetService() } ?? throw new InvalidOperationException("Failed to resolve IConfigurationService."); - })); + }); return builder; } private static MauiAppBuilder RegisterAuthenticationService(this MauiAppBuilder builder) { - builder.Services.AddSingleton(new Func(useTrainingMode => { + builder.Services.AddSingleton>(serviceProvider => useTrainingMode => { return useTrainingMode switch { true => new TrainingAuthenticationService(), - _ => MauiProgram.Container.Services.GetService() + _ => serviceProvider.GetService() } ?? throw new InvalidOperationException("Failed to resolve IConfigurationService."); - })); + }); return builder; } private static MauiAppBuilder RegisterTransactionService(this MauiAppBuilder builder) { - builder.Services.AddSingleton(new Func(useTrainingMode => + builder.Services.AddSingleton>(serviceProvider => useTrainingMode => { return useTrainingMode switch { true => new TrainingTransactionService(), - _ => MauiProgram.Container.Services.GetService() + _ => serviceProvider.GetService() } ?? throw new InvalidOperationException("Failed to resolve ITransactionService."); - })); + }); return builder; } private static MauiAppBuilder RegisterMerchantService(this MauiAppBuilder builder) { - builder.Services.AddSingleton(new Func(useTrainingMode => + builder.Services.AddSingleton>(serviceProvider => useTrainingMode => { return useTrainingMode switch { true => new TrainingMerchantService(), - _ => MauiProgram.Container.Services.GetService() + _ => serviceProvider.GetService() } ?? throw new InvalidOperationException("Failed to resolve ITransactionService."); - })); + }); return builder; } @@ -236,6 +236,7 @@ public static MauiAppBuilder ConfigureViewModels(this MauiAppBuilder builder) { } public static MauiAppBuilder ConfigurePages(this MauiAppBuilder builder) { + builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); diff --git a/TransactionProcessor.Mobile/MauiProgram.cs b/TransactionProcessor.Mobile/MauiProgram.cs index 6e10506e..3455bc4c 100644 --- a/TransactionProcessor.Mobile/MauiProgram.cs +++ b/TransactionProcessor.Mobile/MauiProgram.cs @@ -16,7 +16,6 @@ namespace TransactionProcessor.Mobile { public static class MauiProgram { - public static MauiApp Container { get; private set; } = default!; public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); @@ -37,14 +36,14 @@ public static MauiApp CreateMauiApp() builder.Logging.SetMinimumLevel(LogLevel.Trace); - Container = builder.Build(); + MauiApp app = builder.Build(); - var serialiser = Container.Services.GetRequiredService(); + var serialiser = app.Services.GetRequiredService(); StringSerialiser.Initialise(serialiser); Logger.Initialise(new ConsoleLogger()); - return Container; + return app; } } diff --git a/TransactionProcessor.Mobile/Pages/AppHome/HomePage.xaml.cs b/TransactionProcessor.Mobile/Pages/AppHome/HomePage.xaml.cs index 6cb56ccf..b1d4c213 100644 --- a/TransactionProcessor.Mobile/Pages/AppHome/HomePage.xaml.cs +++ b/TransactionProcessor.Mobile/Pages/AppHome/HomePage.xaml.cs @@ -12,7 +12,8 @@ public partial class HomePage : NoBackWithoutLogoutPage public HomePage(HomePageViewModel vm, IDialogService dialogService, IApplicationCache applicationCache, - INavigationService navigationService) { + INavigationService navigationService) + : base(navigationService, dialogService, applicationCache) { this.InitializeComponent(); this.BindingContext = vm; } @@ -33,4 +34,4 @@ protected override async void OnAppearing() { } #endregion -} \ No newline at end of file +} diff --git a/TransactionProcessor.Mobile/Pages/Common/NoBackWithoutLogoutPage.cs b/TransactionProcessor.Mobile/Pages/Common/NoBackWithoutLogoutPage.cs index 1ad5b97e..819464ae 100644 --- a/TransactionProcessor.Mobile/Pages/Common/NoBackWithoutLogoutPage.cs +++ b/TransactionProcessor.Mobile/Pages/Common/NoBackWithoutLogoutPage.cs @@ -11,6 +11,19 @@ namespace TransactionProcessor.Mobile.Pages.Common; public class NoBackWithoutLogoutPage : ContentPage { + private readonly INavigationService navigationService; + private readonly IDialogService dialogService; + private readonly IApplicationCache applicationCache; + + protected NoBackWithoutLogoutPage(INavigationService navigationService, + IDialogService dialogService, + IApplicationCache applicationCache) + { + this.navigationService = navigationService; + this.dialogService = dialogService; + this.applicationCache = applicationCache; + } + protected override Boolean OnBackButtonPressed() { Type type = this.GetType().UnderlyingSystemType; @@ -28,24 +41,19 @@ protected override Boolean OnBackButtonPressed() { } private async Task ShowHomePage() { - INavigationService navigationService = MauiProgram.Container.Services.GetRequiredService(); - await navigationService.GoToHome(); + await this.navigationService.GoToHome(); } private async Task ShowLoginPage() { - IDialogService dialogService = MauiProgram.Container.Services.GetRequiredService(); - IApplicationCache applicationCache = MauiProgram.Container.Services.GetRequiredService(); - INavigationService navigationService = MauiProgram.Container.Services.GetRequiredService(); - - Boolean leave = await dialogService.ShowDialog("Title", "Logout Message", "yes", "no"); + Boolean leave = await this.dialogService.ShowDialog("Title", "Logout Message", "yes", "no"); if (leave) { - applicationCache.SetIsLoggedIn(false); - applicationCache.ClearAccessToken(); - applicationCache.ClearMerchantDetails(); + this.applicationCache.SetIsLoggedIn(false); + this.applicationCache.ClearAccessToken(); + this.applicationCache.ClearMerchantDetails(); - await navigationService.GoToLoginPage(); + await this.navigationService.GoToLoginPage(); } } -} \ No newline at end of file +} diff --git a/TransactionProcessor.Mobile/Pages/LoadingPage.xaml.cs b/TransactionProcessor.Mobile/Pages/LoadingPage.xaml.cs index f49ab3d4..663fb513 100644 --- a/TransactionProcessor.Mobile/Pages/LoadingPage.xaml.cs +++ b/TransactionProcessor.Mobile/Pages/LoadingPage.xaml.cs @@ -3,8 +3,11 @@ namespace TransactionProcessor.Mobile.Pages; public partial class LoadingPage : ContentPage { - public LoadingPage() { + private readonly IApplicationCache applicationCache; + + public LoadingPage(IApplicationCache applicationCache) { InitializeComponent(); + this.applicationCache = applicationCache; } protected override async void OnNavigatedTo(NavigatedToEventArgs args) { @@ -20,8 +23,7 @@ protected override async void OnNavigatedTo(NavigatedToEventArgs args) { async Task isAuthenticated() { await Task.Delay(2000); - IApplicationCache applicationCache = MauiProgram.Container.Services.GetService(); - Boolean isLoggedIn = applicationCache.GetIsLoggedIn(); + Boolean isLoggedIn = this.applicationCache.GetIsLoggedIn(); return isLoggedIn; } -} \ No newline at end of file +} diff --git a/TransactionProcessor.Mobile/Pages/MyAccount/MyAccountPage.xaml.cs b/TransactionProcessor.Mobile/Pages/MyAccount/MyAccountPage.xaml.cs index 8e03adff..ef51822e 100644 --- a/TransactionProcessor.Mobile/Pages/MyAccount/MyAccountPage.xaml.cs +++ b/TransactionProcessor.Mobile/Pages/MyAccount/MyAccountPage.xaml.cs @@ -1,5 +1,7 @@ using TransactionProcessor.Mobile.BusinessLogic.Common; using TransactionProcessor.Mobile.BusinessLogic.Models; +using TransactionProcessor.Mobile.BusinessLogic.Services; +using TransactionProcessor.Mobile.BusinessLogic.UIServices; using TransactionProcessor.Mobile.BusinessLogic.ViewModels.MyAccount; using TransactionProcessor.Mobile.Pages.Common; @@ -9,7 +11,11 @@ public partial class MyAccountPage : NoBackWithoutLogoutPage { private MyAccountPageViewModel viewModel => BindingContext as MyAccountPageViewModel; - public MyAccountPage(MyAccountPageViewModel vm) + public MyAccountPage(MyAccountPageViewModel vm, + INavigationService navigationService, + IDialogService dialogService, + IApplicationCache applicationCache) + : base(navigationService, dialogService, applicationCache) { this.InitializeComponent(); diff --git a/TransactionProcessor.Mobile/Pages/Reports/ReportsPage.xaml.cs b/TransactionProcessor.Mobile/Pages/Reports/ReportsPage.xaml.cs index 017557de..cfe23a99 100644 --- a/TransactionProcessor.Mobile/Pages/Reports/ReportsPage.xaml.cs +++ b/TransactionProcessor.Mobile/Pages/Reports/ReportsPage.xaml.cs @@ -1,5 +1,7 @@ using TransactionProcessor.Mobile.BusinessLogic.Common; using TransactionProcessor.Mobile.BusinessLogic.Models; +using TransactionProcessor.Mobile.BusinessLogic.Services; +using TransactionProcessor.Mobile.BusinessLogic.UIServices; using TransactionProcessor.Mobile.BusinessLogic.ViewModels.Reports; using TransactionProcessor.Mobile.Pages.Common; @@ -9,7 +11,11 @@ public partial class ReportsPage : NoBackWithoutLogoutPage { private ReportsPageViewModel viewModel => BindingContext as ReportsPageViewModel; - public ReportsPage(ReportsPageViewModel vm) + public ReportsPage(ReportsPageViewModel vm, + INavigationService navigationService, + IDialogService dialogService, + IApplicationCache applicationCache) + : base(navigationService, dialogService, applicationCache) { this.InitializeComponent(); BindingContext = vm; diff --git a/TransactionProcessor.Mobile/Pages/Support/SupportPage.xaml.cs b/TransactionProcessor.Mobile/Pages/Support/SupportPage.xaml.cs index d55b883f..a5e397e7 100644 --- a/TransactionProcessor.Mobile/Pages/Support/SupportPage.xaml.cs +++ b/TransactionProcessor.Mobile/Pages/Support/SupportPage.xaml.cs @@ -1,5 +1,7 @@ using System.Windows.Input; using CommunityToolkit.Mvvm.Input; +using TransactionProcessor.Mobile.BusinessLogic.Services; +using TransactionProcessor.Mobile.BusinessLogic.UIServices; using TransactionProcessor.Mobile.BusinessLogic.ViewModels.Support; using TransactionProcessor.Mobile.Pages.Common; @@ -9,7 +11,11 @@ public partial class SupportPage : NoBackWithoutLogoutPage { private SupportPageViewModel viewModel => BindingContext as SupportPageViewModel; - public SupportPage(SupportPageViewModel vm) { + public SupportPage(SupportPageViewModel vm, + INavigationService navigationService, + IDialogService dialogService, + IApplicationCache applicationCache) + : base(navigationService, dialogService, applicationCache) { this.InitializeComponent(); BindingContext = vm; } diff --git a/TransactionProcessor.Mobile/Pages/Support/ViewLogsPage.xaml.cs b/TransactionProcessor.Mobile/Pages/Support/ViewLogsPage.xaml.cs index 318dc5c8..11a86a7a 100644 --- a/TransactionProcessor.Mobile/Pages/Support/ViewLogsPage.xaml.cs +++ b/TransactionProcessor.Mobile/Pages/Support/ViewLogsPage.xaml.cs @@ -12,9 +12,7 @@ public ViewLogsPage(ViewLogsPageViewModel vm) { } protected override async void OnAppearing() { - ViewLogsPageViewModel vm = MauiProgram.Container.Services.GetRequiredService(); - await vm.LoadLogMessages(); - this.BindingContext = vm; + await this.viewModel.LoadLogMessages(); } } -} \ No newline at end of file +} diff --git a/TransactionProcessor.Mobile/Pages/Transactions/TransactionsPage.xaml.cs b/TransactionProcessor.Mobile/Pages/Transactions/TransactionsPage.xaml.cs index e955e547..4d557baa 100644 --- a/TransactionProcessor.Mobile/Pages/Transactions/TransactionsPage.xaml.cs +++ b/TransactionProcessor.Mobile/Pages/Transactions/TransactionsPage.xaml.cs @@ -1,4 +1,6 @@ using TransactionProcessor.Mobile.BusinessLogic.ViewModels.Transactions; +using TransactionProcessor.Mobile.BusinessLogic.Services; +using TransactionProcessor.Mobile.BusinessLogic.UIServices; using TransactionProcessor.Mobile.Pages.Common; namespace TransactionProcessor.Mobile.Pages.Transactions; @@ -7,7 +9,11 @@ public partial class TransactionsPage : NoBackWithoutLogoutPage { private TransactionsPageViewModel viewModel => BindingContext as TransactionsPageViewModel; - public TransactionsPage(TransactionsPageViewModel vm) + public TransactionsPage(TransactionsPageViewModel vm, + INavigationService navigationService, + IDialogService dialogService, + IApplicationCache applicationCache) + : base(navigationService, dialogService, applicationCache) { this.InitializeComponent(); BindingContext = vm; @@ -17,4 +23,4 @@ protected override async void OnAppearing() { base.OnAppearing(); } -} \ No newline at end of file +} diff --git a/TransactionProcessor.Mobile/UIServices/ShellNavigationService.cs b/TransactionProcessor.Mobile/UIServices/ShellNavigationService.cs index 94e7562c..5dcde6b2 100644 --- a/TransactionProcessor.Mobile/UIServices/ShellNavigationService.cs +++ b/TransactionProcessor.Mobile/UIServices/ShellNavigationService.cs @@ -14,11 +14,14 @@ namespace TransactionProcessor.Mobile.UIServices; public class ShellNavigationService : INavigationService { private readonly INavigationParameterService NavigationParameterService; + private readonly IServiceProvider ServiceProvider; #region Methods - public ShellNavigationService(INavigationParameterService navigationParameterService) { + public ShellNavigationService(INavigationParameterService navigationParameterService, + IServiceProvider serviceProvider) { this.NavigationParameterService = navigationParameterService; + this.ServiceProvider = serviceProvider; } public async Task QuitApplication(){ @@ -172,7 +175,7 @@ public async Task GoToViewLogsPage() { } public async Task GoToMyAccountAddresses() { - MyAccountAddressesPage p = (MyAccountAddressesPage)MauiProgram.Container.Services.GetService(typeof(MyAccountAddressesPage)); + MyAccountAddressesPage p = this.ServiceProvider.GetRequiredService(); await this.NavigateTo(p); }