[EC-371] Fix iOS extensions login more menu (#2016)

* EC-371 fix iOS extensions login more menu and HintPage to display correctly on extensions

* EC-371 fix merge
This commit is contained in:
Federico Maccaroni 2022-08-08 11:28:48 -03:00 committed by GitHub
parent 570b56364a
commit 3ef5b576ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 101 additions and 71 deletions

View File

@ -14,7 +14,7 @@
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<ToolbarItem Text="{u:I18n Submit}" Clicked="Submit_Clicked" />
<ToolbarItem Text="{u:I18n Submit}" Command="{Binding SubmitCommand}" />
</ContentPage.ToolbarItems>
<ScrollView>

View File

@ -1,5 +1,4 @@
using System;
using Xamarin.Forms;
using Xamarin.Forms;
namespace Bit.App.Pages
{
@ -24,14 +23,6 @@ namespace Bit.App.Pages
RequestFocus(_email);
}
private async void Submit_Clicked(object sender, EventArgs e)
{
if (DoOnce())
{
await _vm.SubmitAsync();
}
}
private async void Close_Clicked(object sender, System.EventArgs e)
{
if (DoOnce())

View File

@ -1,10 +1,11 @@
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Exceptions;
using Bit.Core.Utilities;
using Xamarin.Forms;
using Xamarin.CommunityToolkit.ObjectModel;
namespace Bit.App.Pages
{
@ -13,18 +14,26 @@ namespace Bit.App.Pages
private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IApiService _apiService;
private readonly ILogger _logger;
public HintPageViewModel()
{
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
_logger = ServiceContainer.Resolve<ILogger>();
PageTitle = AppResources.PasswordHint;
SubmitCommand = new Command(async () => await SubmitAsync());
SubmitCommand = new AsyncCommand(SubmitAsync,
onException: ex =>
{
_logger.Exception(ex);
_deviceActionService.DisplayAlertAsync(AppResources.AnErrorHasOccurred, AppResources.GenericErrorMessage, AppResources.Ok).FireAndForget();
},
allowsMultipleExecutions: false);
}
public Command SubmitCommand { get; }
public ICommand SubmitCommand { get; }
public string Email { get; set; }
public async Task SubmitAsync()
@ -37,14 +46,14 @@ namespace Bit.App.Pages
}
if (string.IsNullOrWhiteSpace(Email))
{
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
await _deviceActionService.DisplayAlertAsync(AppResources.AnErrorHasOccurred,
string.Format(AppResources.ValidationFieldRequired, AppResources.EmailAddress),
AppResources.Ok);
return;
}
if (!Email.Contains("@"))
{
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, AppResources.InvalidEmail, AppResources.Ok);
await _deviceActionService.DisplayAlertAsync(AppResources.AnErrorHasOccurred, AppResources.InvalidEmail, AppResources.Ok);
return;
}
@ -54,7 +63,7 @@ namespace Bit.App.Pages
await _apiService.PostPasswordHintAsync(
new Core.Models.Request.PasswordHintRequest { Email = Email });
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(null, AppResources.PasswordHintAlert, AppResources.Ok);
await _deviceActionService.DisplayAlertAsync(null, AppResources.PasswordHintAlert, AppResources.Ok);
await Page.Navigation.PopModalAsync();
}
catch (ApiException e)

View File

@ -55,6 +55,7 @@
<Entry
x:Name="_email"
Text="{Binding Email}"
IsEnabled="{Binding IsEmailEnabled}"
Keyboard="Email"
StyleClass="box-value">
<VisualStateManager.VisualStateGroups>

View File

@ -1,8 +1,8 @@
using System;
using System.Threading.Tasks;
using Bit.App.Models;
using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Xamarin.Forms;
@ -15,6 +15,8 @@ namespace Bit.App.Pages
private bool _inputFocused;
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
public LoginPage(string email = null, AppOptions appOptions = null)
{
_appOptions = appOptions;
@ -30,11 +32,10 @@ namespace Bit.App.Pages
await _accountListOverlay.HideAsync();
await Navigation.PopModalAsync();
};
if (!string.IsNullOrWhiteSpace(email))
{
_email.IsEnabled = false;
}
else
_vm.IsEmailEnabled = string.IsNullOrWhiteSpace(email);
_vm.IsIosExtension = _appOptions?.IosExtension ?? false;
if (_vm.IsEmailEnabled)
{
_vm.ShowCancelButton = true;
}
@ -53,7 +54,7 @@ namespace Bit.App.Pages
ToolbarItems.Add(_getPasswordHint);
}
if (Device.RuntimePlatform == Device.Android && !_email.IsEnabled)
if (Device.RuntimePlatform == Device.Android && !_vm.IsEmailEnabled)
{
ToolbarItems.Add(_removeAccount);
}
@ -110,7 +111,7 @@ namespace Bit.App.Pages
{
if (DoOnce())
{
await _vm.LogInAsync(true, _email.IsEnabled);
await _vm.LogInAsync(true, _vm.IsEmailEnabled);
}
}
@ -139,26 +140,16 @@ namespace Bit.App.Pages
}
}
private async void More_Clicked(object sender, System.EventArgs e)
private async void More_Clicked(object sender, EventArgs e)
{
await _accountListOverlay.HideAsync();
if (!DoOnce())
try
{
return;
await _accountListOverlay.HideAsync();
_vm.MoreCommand.Execute(null);
}
var buttons = _email.IsEnabled ? new[] { AppResources.GetPasswordHint }
: new[] { AppResources.GetPasswordHint, AppResources.RemoveAccount };
var selection = await DisplayActionSheet(AppResources.Options,
AppResources.Cancel, null, buttons);
if (selection == AppResources.GetPasswordHint)
catch (Exception ex)
{
await Navigation.PushModalAsync(new NavigationPage(new HintPage()));
}
else if (selection == AppResources.RemoveAccount)
{
await _vm.RemoveAccountAsync();
_logger.Value.Exception(ex);
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Resources;
@ -8,6 +9,7 @@ using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Exceptions;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
namespace Bit.App.Pages
@ -28,6 +30,7 @@ namespace Bit.App.Pages
private bool _showCancelButton;
private string _email;
private string _masterPassword;
private bool _isEmailEnabled;
public LoginPageViewModel()
{
@ -44,6 +47,7 @@ namespace Bit.App.Pages
PageTitle = AppResources.Bitwarden;
TogglePasswordCommand = new Command(TogglePassword);
LogInCommand = new Command(async () => await LogInAsync());
MoreCommand = new AsyncCommand(MoreAsync, onException: _logger.Exception, allowsMultipleExecutions: false);
AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger)
{
@ -81,10 +85,19 @@ namespace Bit.App.Pages
set => SetProperty(ref _masterPassword, value);
}
public bool IsEmailEnabled
{
get => _isEmailEnabled;
set => SetProperty(ref _isEmailEnabled, value);
}
public bool IsIosExtension { get; set; }
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
public Command LogInCommand { get; }
public Command TogglePasswordCommand { get; }
public ICommand MoreCommand { get; internal set; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
public Action StartTwoFactorAction { get; set; }
@ -201,6 +214,28 @@ namespace Bit.App.Pages
}
}
private async Task MoreAsync()
{
var buttons = IsEmailEnabled
? new[] { AppResources.GetPasswordHint }
: new[] { AppResources.GetPasswordHint, AppResources.RemoveAccount };
var selection = await _deviceActionService.DisplayActionSheetAsync(AppResources.Options, AppResources.Cancel, null, buttons);
if (selection == AppResources.GetPasswordHint)
{
var hintNavigationPage = new NavigationPage(new HintPage());
if (IsIosExtension)
{
ThemeManager.ApplyResourcesTo(hintNavigationPage);
}
await Page.Navigation.PushModalAsync(hintNavigationPage);
}
else if (selection == AppResources.RemoveAccount)
{
await RemoveAccountAsync();
}
}
public void TogglePassword()
{
ShowPassword = !ShowPassword;

View File

@ -147,11 +147,11 @@ namespace Bit.App.Utilities
return Application.Current.RequestedTheme == OSAppTheme.Dark;
}
public static void ApplyResourcesToPage(ContentPage page)
public static void ApplyResourcesTo(VisualElement element)
{
foreach (var resourceDict in Resources().MergedDictionaries)
{
page.Resources.Add(resourceDict);
element.Resources.Add(resourceDict);
}
}

View File

@ -430,7 +430,7 @@ namespace Bit.iOS.Autofill
var homePage = new HomePage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(homePage);
ThemeManager.ApplyResourcesTo(homePage);
if (homePage.BindingContext is HomeViewModel vm)
{
vm.StartLoginAction = () => DismissViewController(false, () => LaunchLoginFlow());
@ -453,7 +453,7 @@ namespace Bit.iOS.Autofill
var environmentPage = new EnvironmentPage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(environmentPage);
ThemeManager.ApplyResourcesTo(environmentPage);
if (environmentPage.BindingContext is EnvironmentPageViewModel vm)
{
vm.SubmitSuccessAction = () => DismissViewController(false, () => LaunchHomePage());
@ -471,7 +471,7 @@ namespace Bit.iOS.Autofill
var registerPage = new RegisterPage(null);
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(registerPage);
ThemeManager.ApplyResourcesTo(registerPage);
if (registerPage.BindingContext is RegisterPageViewModel vm)
{
vm.RegistrationSuccess = () => DismissViewController(false, () => LaunchLoginFlow(vm.Email));
@ -486,10 +486,11 @@ namespace Bit.iOS.Autofill
private void LaunchLoginFlow(string email = null)
{
var loginPage = new LoginPage(email);
var app = new App.App(new AppOptions { IosExtension = true });
var appOptions = new AppOptions { IosExtension = true };
var app = new App.App(appOptions);
var loginPage = new LoginPage(email, appOptions);
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(loginPage);
ThemeManager.ApplyResourcesTo(loginPage);
if (loginPage.BindingContext is LoginPageViewModel vm)
{
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(false));
@ -511,7 +512,7 @@ namespace Bit.iOS.Autofill
var loginPage = new LoginSsoPage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(loginPage);
ThemeManager.ApplyResourcesTo(loginPage);
if (loginPage.BindingContext is LoginSsoPageViewModel vm)
{
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(true));
@ -534,7 +535,7 @@ namespace Bit.iOS.Autofill
var twoFactorPage = new TwoFactorPage(authingWithSso);
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(twoFactorPage);
ThemeManager.ApplyResourcesTo(twoFactorPage);
if (twoFactorPage.BindingContext is TwoFactorPageViewModel vm)
{
vm.TwoFactorAuthSuccessAction = () => DismissLockAndContinue();
@ -561,7 +562,7 @@ namespace Bit.iOS.Autofill
var setPasswordPage = new SetPasswordPage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(setPasswordPage);
ThemeManager.ApplyResourcesTo(setPasswordPage);
if (setPasswordPage.BindingContext is SetPasswordPageViewModel vm)
{
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
@ -580,7 +581,7 @@ namespace Bit.iOS.Autofill
var updateTempPasswordPage = new UpdateTempPasswordPage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(updateTempPasswordPage);
ThemeManager.ApplyResourcesTo(updateTempPasswordPage);
if (updateTempPasswordPage.BindingContext is UpdateTempPasswordPageViewModel vm)
{
vm.UpdateTempPasswordSuccessAction = () => DismissViewController(false, () => LaunchHomePage());

View File

@ -339,7 +339,7 @@ namespace Bit.iOS.Core.Controllers
var loginPage = new LoginSsoPage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(loginPage);
ThemeManager.ApplyResourcesTo(loginPage);
if (loginPage.BindingContext is LoginSsoPageViewModel vm)
{
vm.SsoAuthSuccessAction = () => DoContinue();

View File

@ -328,7 +328,7 @@ namespace Bit.iOS.Core.Controllers
var loginPage = new LoginSsoPage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(loginPage);
ThemeManager.ApplyResourcesTo(loginPage);
if (loginPage.BindingContext is LoginSsoPageViewModel vm)
{
vm.SsoAuthSuccessAction = () => DoContinue();

View File

@ -83,12 +83,11 @@ namespace Bit.iOS.Core.Services
{
HideLoadingAsync().GetAwaiter().GetResult();
}
var vc = GetPresentedViewController();
if (vc is null)
{
return Task.CompletedTask;
}
}
var result = new TaskCompletionSource<int>();
@ -360,6 +359,7 @@ namespace Bit.iOS.Core.Services
{
return null;
}
var result = new TaskCompletionSource<string>();
var sheet = UIAlertController.Create(title, null, UIAlertControllerStyle.ActionSheet);
if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad)

View File

@ -451,7 +451,7 @@ namespace Bit.iOS.Extension
var homePage = new HomePage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(homePage);
ThemeManager.ApplyResourcesTo(homePage);
if (homePage.BindingContext is HomeViewModel vm)
{
vm.StartLoginAction = () => DismissViewController(false, () => LaunchLoginFlow());
@ -474,7 +474,7 @@ namespace Bit.iOS.Extension
var environmentPage = new EnvironmentPage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(environmentPage);
ThemeManager.ApplyResourcesTo(environmentPage);
if (environmentPage.BindingContext is EnvironmentPageViewModel vm)
{
vm.SubmitSuccessAction = () => DismissViewController(false, () => LaunchHomePage());
@ -492,7 +492,7 @@ namespace Bit.iOS.Extension
var registerPage = new RegisterPage(null);
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(registerPage);
ThemeManager.ApplyResourcesTo(registerPage);
if (registerPage.BindingContext is RegisterPageViewModel vm)
{
vm.RegistrationSuccess = () => DismissViewController(false, () => LaunchLoginFlow(vm.Email));
@ -507,10 +507,11 @@ namespace Bit.iOS.Extension
private void LaunchLoginFlow(string email = null)
{
var loginPage = new LoginPage(email);
var app = new App.App(new AppOptions { IosExtension = true });
var appOptions = new AppOptions { IosExtension = true };
var app = new App.App(appOptions);
var loginPage = new LoginPage(email, appOptions);
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(loginPage);
ThemeManager.ApplyResourcesTo(loginPage);
if (loginPage.BindingContext is LoginPageViewModel vm)
{
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(false));
@ -532,7 +533,7 @@ namespace Bit.iOS.Extension
var loginPage = new LoginSsoPage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(loginPage);
ThemeManager.ApplyResourcesTo(loginPage);
if (loginPage.BindingContext is LoginSsoPageViewModel vm)
{
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(true));
@ -555,7 +556,7 @@ namespace Bit.iOS.Extension
var twoFactorPage = new TwoFactorPage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(twoFactorPage);
ThemeManager.ApplyResourcesTo(twoFactorPage);
if (twoFactorPage.BindingContext is TwoFactorPageViewModel vm)
{
vm.TwoFactorAuthSuccessAction = () => DismissLockAndContinue();
@ -582,7 +583,7 @@ namespace Bit.iOS.Extension
var setPasswordPage = new SetPasswordPage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(setPasswordPage);
ThemeManager.ApplyResourcesTo(setPasswordPage);
if (setPasswordPage.BindingContext is SetPasswordPageViewModel vm)
{
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
@ -601,7 +602,7 @@ namespace Bit.iOS.Extension
var updateTempPasswordPage = new UpdateTempPasswordPage();
var app = new App.App(new AppOptions { IosExtension = true });
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesToPage(updateTempPasswordPage);
ThemeManager.ApplyResourcesTo(updateTempPasswordPage);
if (updateTempPasswordPage.BindingContext is UpdateTempPasswordPageViewModel vm)
{
vm.UpdateTempPasswordSuccessAction = () => DismissViewController(false, () => LaunchHomePage());

View File

@ -38,6 +38,7 @@ namespace Bit.iOS.ShareExtension
Lazy<UIStoryboard> _storyboard = new Lazy<UIStoryboard>(() => UIStoryboard.FromName(STORYBOARD_NAME, null));
private readonly Lazy<AppOptions> _appOptions = new Lazy<AppOptions>(() => new AppOptions { IosExtension = true });
private App.App _app = null;
private UIViewController _currentModalController;
private bool _presentingOnNavigationPage;
@ -279,10 +280,10 @@ namespace Bit.iOS.ShareExtension
{
if (_app is null)
{
var app = new App.App(new AppOptions { IosExtension = true });
var app = new App.App(_appOptions.Value);
ThemeManager.SetTheme(app.Resources);
}
ThemeManager.ApplyResourcesToPage(page);
ThemeManager.ApplyResourcesTo(page);
return _app;
}
@ -307,7 +308,7 @@ namespace Bit.iOS.ShareExtension
{
var environmentPage = new EnvironmentPage();
SetupAppAndApplyResources(environmentPage);
ThemeManager.ApplyResourcesToPage(environmentPage);
ThemeManager.ApplyResourcesTo(environmentPage);
if (environmentPage.BindingContext is EnvironmentPageViewModel vm)
{
vm.SubmitSuccessAction = () => DismissAndLaunch(() => LaunchHomePage());
@ -331,7 +332,7 @@ namespace Bit.iOS.ShareExtension
private void LaunchLoginFlow(string email = null)
{
var loginPage = new LoginPage(email);
var loginPage = new LoginPage(email, _appOptions.Value);
SetupAppAndApplyResources(loginPage);
if (loginPage.BindingContext is LoginPageViewModel vm)
{