diff --git a/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj b/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj index 22430ca4..524cd17e 100644 --- a/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj +++ b/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj @@ -28,6 +28,8 @@ + + diff --git a/TransactionMobile.Maui.BusinessLogic/UIServices/IDialogService.cs b/TransactionMobile.Maui.BusinessLogic/UIServices/IDialogService.cs new file mode 100644 index 00000000..bf68ee55 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/UIServices/IDialogService.cs @@ -0,0 +1,44 @@ +namespace TransactionMobile.Maui.BusinessLogic.UIServices; + +public interface IDialogService +{ + #region Methods + + Task ShowDialog(String title, + String message, + String acceptString); + + Task ShowDialog(String title, + String message, + String acceptString, + String cancelString); + + Task ShowErrorToast(String message, + Action? action, + String? actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken); + + Task ShowInformationToast(String message, + Action? action, + String? actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken); + + Task ShowPrompt(String title, + String message, + String acceptString, + String cancelString, + String placeHolder = "", + Int32 maxLength = -1, + Keyboard keyboard = null, + String initialValue = ""); + + Task ShowWarningToast(String message, + Action? action, + String? actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken); + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs new file mode 100644 index 00000000..cd354890 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs @@ -0,0 +1,92 @@ +namespace TransactionMobile.Maui.BusinessLogic.ViewModels; + +using Microsoft.AppCenter; +using Microsoft.AppCenter.Distribute; +using Models; +using MvvmHelpers; +using Services; +using UIServices; + +public class HomePageViewModel : BaseViewModel +{ + #region Fields + + private readonly IDialogService DialogService; + + private readonly IMemoryCacheService MemoryCacheService; + + #endregion + + #region Constructors + + public HomePageViewModel(IMemoryCacheService memoryCacheService, + IDialogService dialogService) { + this.MemoryCacheService = memoryCacheService; + this.DialogService = dialogService; + } + + #endregion + + #region Methods + + public async Task Initialise(CancellationToken cancellationToken) { + this.MemoryCacheService.TryGetValue("Configuration", out Configuration configuration); + + if (configuration.EnableAutoUpdates) { + await Distribute.SetEnabledAsync(true); + Distribute.CheckForUpdate(); + Distribute.ReleaseAvailable = this.OnReleaseAvailable; + Distribute.UpdateTrack = UpdateTrack.Public; + } + else { + Distribute.DisableAutomaticCheckForUpdate(); + } + + if (this.IsIOS() == false) { + // TODO: Move the keys to config service + AppCenter.Configure("android=f920cc96-de56-42fe-87d4-b49105761205;" + "ios=dd940171-ca8c-4219-9851-f83769464f37;" + + "uwp=3ad27ea3-3f24-4579-a88a-530025bd00d4;" + "macos=244fdee2-f897-431a-8bab-5081fc90b329;"); + AppCenter.Start(typeof(Distribute)); + } + } + + private Boolean IsIOS() => DeviceInfo.Current.Platform == DevicePlatform.iOS; + + private Boolean OnReleaseAvailable(ReleaseDetails releaseDetails) { + // Look at releaseDetails public properties to get version information, release notes text or release notes URL + String versionName = releaseDetails.ShortVersion; + String versionCodeOrBuildNumber = releaseDetails.Version; + String releaseNotes = releaseDetails.ReleaseNotes; + Uri releaseNotesUrl = releaseDetails.ReleaseNotesUrl; + + // custom dialog + String title = "Version " + versionName + " available!"; + Task answer; + + // On mandatory update, user can't postpone + if (releaseDetails.MandatoryUpdate) { + answer = this.DialogService.ShowDialog(title, releaseNotes, "Download and Install"); + } + else { + answer = this.DialogService.ShowDialog(title, releaseNotes, "Download and Install", "Later"); + } + + answer.ContinueWith(task => { + // If mandatory or if answer was positive + if (releaseDetails.MandatoryUpdate || (task as Task).Result) { + // Notify SDK that user selected update + Distribute.NotifyUpdateAction(UpdateAction.Update); + } + else { + // Notify SDK that user selected postpone (for 1 day) + // This method call is ignored by the SDK if the update is mandatory + Distribute.NotifyUpdateAction(UpdateAction.Postpone); + } + }); + + // Return true if you're using your own dialog, false otherwise + return true; + } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs index 39f9430c..b2a20f3d 100644 --- a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs @@ -125,6 +125,7 @@ private async Task LoginCommandExecute() this.MemoryCacheService.Set("IsLoggedIn", true); await this.NavigationService.GoToHome(); + } private async void AccessTokenExpired(Object key, diff --git a/TransactionMobile.Maui.UiTests/Features/Login.feature.cs b/TransactionMobile.Maui.UiTests/Features/Login.feature.cs index 40b52487..33446da6 100644 --- a/TransactionMobile.Maui.UiTests/Features/Login.feature.cs +++ b/TransactionMobile.Maui.UiTests/Features/Login.feature.cs @@ -85,14 +85,12 @@ public virtual void FeatureBackground() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Login as Merchant")] - [NUnit.Framework.CategoryAttribute("PRTest")] public void LoginAsMerchant() { - string[] tagsOfScenario = new string[] { - "PRTest"}; + string[] tagsOfScenario = ((string[])(null)); System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Login as Merchant", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 7 +#line 6 this.ScenarioInitialize(scenarioInfo); #line hidden if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) @@ -105,22 +103,22 @@ public void LoginAsMerchant() #line 4 this.FeatureBackground(); #line hidden -#line 8 +#line 7 testRunner.Given("I am on the Login Screen", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); #line hidden -#line 9 +#line 8 testRunner.And("the application is in training mode", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); #line hidden -#line 10 +#line 9 testRunner.When("I enter \'merchantuser@testmerchant1.co.uk\' as the Email Address", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); #line hidden -#line 11 +#line 10 testRunner.And("I enter \'123456\' as the Password", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); #line hidden -#line 12 +#line 11 testRunner.And("I tap on Login", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); #line hidden -#line 13 +#line 12 testRunner.Then("the Merchant Home Page is displayed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden } @@ -129,9 +127,11 @@ public void LoginAsMerchant() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Logout")] + [NUnit.Framework.CategoryAttribute("PRTest")] public void Logout() { - string[] tagsOfScenario = ((string[])(null)); + string[] tagsOfScenario = new string[] { + "PRTest"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Logout", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 15 diff --git a/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs b/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs index 3640565e..407352e4 100644 --- a/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs +++ b/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs @@ -26,6 +26,7 @@ using TransactionMobile.Maui.Pages.Support; using System; using BusinessLogic.ViewModels.MyAccount; + using Pages.AppHome; using Pages.MyAccount; public static class MauiAppBuilderExtensions @@ -182,7 +183,7 @@ public static MauiAppBuilder ConfigureTrainingServices(this MauiAppBuilder build public static MauiAppBuilder ConfigureUIServices(this MauiAppBuilder builder) { - // builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); return builder; @@ -232,6 +233,8 @@ public static MauiAppBuilder ConfigureViewModels(this MauiAppBuilder builder) builder.Services.AddTransient(); + builder.Services.AddTransient(); + return builder; } @@ -258,6 +261,8 @@ public static MauiAppBuilder ConfigurePages(this MauiAppBuilder builder) builder.Services.AddTransient(); + builder.Services.AddTransient(); + return builder; } diff --git a/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs b/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs index eb959471..8ca53558 100644 --- a/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs +++ b/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs @@ -1,13 +1,30 @@ namespace TransactionMobile.Maui.Pages.AppHome; +using BusinessLogic.ViewModels; + public partial class HomePage : ContentPage { - public HomePage() - { - InitializeComponent(); - } + #region Constructors + + public HomePage(HomePageViewModel vm) { + this.InitializeComponent(); + this.BindingContext = vm; + } + + #endregion + + #region Properties - protected override void OnAppearing() { + private HomePageViewModel viewModel => this.BindingContext as HomePageViewModel; + + #endregion + + #region Methods + + protected override async void OnAppearing() { base.OnAppearing(); + await this.viewModel.Initialise(CancellationToken.None); } + + #endregion } \ No newline at end of file diff --git a/TransactionMobile.Maui/Pages/Transactions/MobileTopup/MobileTopupSelectProductPage.xaml.cs b/TransactionMobile.Maui/Pages/Transactions/MobileTopup/MobileTopupSelectProductPage.xaml.cs index 0ff595db..256366ea 100644 --- a/TransactionMobile.Maui/Pages/Transactions/MobileTopup/MobileTopupSelectProductPage.xaml.cs +++ b/TransactionMobile.Maui/Pages/Transactions/MobileTopup/MobileTopupSelectProductPage.xaml.cs @@ -6,49 +6,51 @@ namespace TransactionMobile.Maui.Pages.Transactions.MobileTopup; public partial class MobileTopupSelectProductPage : ContentPage { - private MobileTopupSelectProductPageViewModel viewModel => BindingContext as MobileTopupSelectProductPageViewModel; + #region Constructors - public MobileTopupSelectProductPage(MobileTopupSelectProductPageViewModel vm) - { - InitializeComponent(); - BindingContext = vm; + public MobileTopupSelectProductPage(MobileTopupSelectProductPageViewModel vm) { + this.InitializeComponent(); + this.BindingContext = vm; } - protected override async void OnAppearing() - { + #endregion + + #region Properties + + private MobileTopupSelectProductPageViewModel viewModel => this.BindingContext as MobileTopupSelectProductPageViewModel; + + #endregion + + #region Methods + + protected override async void OnAppearing() { base.OnAppearing(); - await viewModel.Initialise(CancellationToken.None); - this.LoadProducts(viewModel); + await this.viewModel.Initialise(CancellationToken.None); + this.LoadProducts(this.viewModel); } - private void LoadProducts(MobileTopupSelectProductPageViewModel viewModel) - { + private void LoadProducts(MobileTopupSelectProductPageViewModel viewModel) { this.ProductsList.Clear(); - + Int32 rowCount = 0; - foreach (ContractProductModel modelProduct in viewModel.Products) - { - Button button = new Button - { - Text = modelProduct.ProductDisplayText, - HorizontalOptions = LayoutOptions.FillAndExpand, - AutomationId = modelProduct.ProductDisplayText, - }; + foreach (ContractProductModel modelProduct in viewModel.Products) { + Button button = new Button { + Text = modelProduct.ProductDisplayText, + HorizontalOptions = LayoutOptions.FillAndExpand, + AutomationId = modelProduct.ProductDisplayText, + }; button.SetDynamicResource(VisualElement.StyleProperty, "MobileTopupButtonStyle"); - Binding commandParameter = new Binding() - { - Source = new ItemSelected - { - SelectedItem = modelProduct, - SelectedItemIndex = rowCount - } - }; + Binding commandParameter = new Binding { + Source = new ItemSelected { + SelectedItem = modelProduct, + SelectedItemIndex = rowCount + } + }; - Binding command = new Binding - { - Source = viewModel.ProductSelectedCommand - }; + Binding command = new Binding { + Source = viewModel.ProductSelectedCommand + }; button.SetBinding(Button.CommandProperty, command); button.SetBinding(Button.CommandParameterProperty, commandParameter); @@ -58,4 +60,6 @@ private void LoadProducts(MobileTopupSelectProductPageViewModel viewModel) rowCount++; } } + + #endregion } \ No newline at end of file diff --git a/TransactionMobile.Maui/Platforms/Android/AndroidManifest.xml b/TransactionMobile.Maui/Platforms/Android/AndroidManifest.xml index 5a333cd1..654ff35c 100644 --- a/TransactionMobile.Maui/Platforms/Android/AndroidManifest.xml +++ b/TransactionMobile.Maui/Platforms/Android/AndroidManifest.xml @@ -1,6 +1,16 @@  - - - - - + + + + + + + + + \ No newline at end of file diff --git a/TransactionMobile.Maui/TransactionMobile.Maui.csproj b/TransactionMobile.Maui/TransactionMobile.Maui.csproj index e14274ac..62f07bcd 100644 --- a/TransactionMobile.Maui/TransactionMobile.Maui.csproj +++ b/TransactionMobile.Maui/TransactionMobile.Maui.csproj @@ -55,6 +55,8 @@ + + @@ -200,27 +202,35 @@ apk - None + SdkOnly + link - None + SdkOnly + link - None + SdkOnly + link - None + SdkOnly + link - None + SdkOnly + link - None + SdkOnly + link - None + SdkOnly + link - None + SdkOnly + link diff --git a/TransactionMobile.Maui/UIServices/DialogService.cs b/TransactionMobile.Maui/UIServices/DialogService.cs index 022f159b..2868f5e9 100644 --- a/TransactionMobile.Maui/UIServices/DialogService.cs +++ b/TransactionMobile.Maui/UIServices/DialogService.cs @@ -1,63 +1,75 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace TransactionMobile.Maui.UIServices +namespace TransactionMobile.Maui.UIServices { - //using CommunityToolkit.Maui.Alerts; - //using CommunityToolkit.Maui.Core; - - //public interface IDialogService - //{ - // Task ShowInfoToast(String message, - // Action? action, - // String? actionButtonText, - // TimeSpan? duration, - // CancellationToken cancellationToken); - //} - - //public class DialogService : IDialogService - //{ - // public async Task ShowInfoToast(String message, Action? action, String? actionButtonText, TimeSpan? duration, CancellationToken cancellationToken) - // { - // await Application.Current.MainPage.DisplaySnackbar(message, - // action, - // actionButtonText, - // duration, - // SnackBarOptionsHelper.GetInfoSnackbarOptions()); - // } - //} - - //public static class SnackBarOptionsHelper - //{ - // public static SnackbarOptions GetInfoSnackbarOptions() - // { - // return new SnackbarOptions - // { - // BackgroundColor = Colors.Blue, - // TextColor = Colors.White - // }; - // } - - // public static SnackbarOptions GetWarningSnackbarOptions() - // { - // return new SnackbarOptions - // { - // BackgroundColor = Colors.Orange, - // TextColor = Colors.White - // }; - // } - - // public static SnackbarOptions GetErrorSnackbarOptions() - // { - // return new SnackbarOptions - // { - // BackgroundColor = Colors.Red, - // TextColor = Colors.White - // }; - // } - //} - -} + using BusinessLogic.UIServices; + using CommunityToolkit.Maui.Alerts; + + public class DialogService : IDialogService + { + #region Methods + + public async Task ShowDialog(String title, + String message, + String cancelString) { + await Application.Current.MainPage.DisplayAlert(title, message, cancelString); + } + + public async Task ShowDialog(String title, + String message, + String acceptString, + String cancelString) { + return await Application.Current.MainPage.DisplayAlert(title, message, acceptString, cancelString); + } + + public async Task ShowErrorToast(String message, + Action action, + String actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken) { + await Application.Current.MainPage.DisplaySnackbar(message, + action, + actionButtonText, + duration, + SnackBarOptionsHelper.GetInfoSnackbarOptions, + cancellationToken); + } + + public async Task ShowInformationToast(String message, + Action action, + String actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken) { + await Application.Current.MainPage.DisplaySnackbar(message, + action, + actionButtonText, + duration, + SnackBarOptionsHelper.GetInfoSnackbarOptions, + cancellationToken); + } + + public async Task ShowPrompt(String title, + String message, + String acceptString, + String cancelString, + String placeHolder = "", + Int32 maxLength = -1, + Keyboard keyboard = null, + String initialValue = "") { + return await Application.Current.MainPage.DisplayPromptAsync(title, message, acceptString, cancelString, placeHolder, maxLength, keyboard, initialValue); + } + + public async Task ShowWarningToast(String message, + Action action, + String actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken) { + await Application.Current.MainPage.DisplaySnackbar(message, + action, + actionButtonText, + duration, + SnackBarOptionsHelper.GetInfoSnackbarOptions, + cancellationToken); + } + + #endregion + } +} \ No newline at end of file diff --git a/TransactionMobile.Maui/UIServices/SnackBarOptionsHelper.cs b/TransactionMobile.Maui/UIServices/SnackBarOptionsHelper.cs new file mode 100644 index 00000000..6c7cd866 --- /dev/null +++ b/TransactionMobile.Maui/UIServices/SnackBarOptionsHelper.cs @@ -0,0 +1,28 @@ +namespace TransactionMobile.Maui.UIServices; + +using CommunityToolkit.Maui.Core; + +public static class SnackBarOptionsHelper +{ + #region Properties + + public static SnackbarOptions GetErrorSnackbarOptions => + new SnackbarOptions { + BackgroundColor = Colors.Red, + TextColor = Colors.White, + }; + + public static SnackbarOptions GetInfoSnackbarOptions => + new SnackbarOptions { + BackgroundColor = Colors.Blue, + TextColor = Colors.White + }; + + public static SnackbarOptions GetWarningSnackbarOptions => + new SnackbarOptions { + BackgroundColor = Colors.Orange, + TextColor = Colors.White + }; + + #endregion +} \ No newline at end of file