From 7a65bf7fd7b44424073201c2c574d45b64b9ec9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bispo?= Date: Mon, 6 Nov 2023 15:28:54 +0000 Subject: [PATCH] [PM-3340] Update timeout action for users without master password. (#2818) * [PM-3340] Update timeout action for users without master password. * [PM-3340] PR fixes and refactor * [PM-3340] Raise command can execute. * [PM-3340] Fix converter name * [PM-3340] Fix variable naming --------- Co-authored-by: Federico Maccaroni --- .../Settings/BaseSettingControlView.cs | 8 +++-- .../Settings/SettingChooserItemView.xaml.cs | 2 +- .../Pages/Settings/SecuritySettingsPage.xaml | 5 ++- .../Settings/SecuritySettingsPageViewModel.cs | 32 ++++++++++++++++--- src/App/Resources/AppResources.Designer.cs | 9 ++++++ src/App/Resources/AppResources.resx | 3 ++ src/App/Styles/ControlTemplates.xaml | 2 ++ src/App/Utilities/BoolToColorConverter.cs | 26 +++++++++++++++ 8 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 src/App/Utilities/BoolToColorConverter.cs diff --git a/src/App/Controls/Settings/BaseSettingControlView.cs b/src/App/Controls/Settings/BaseSettingControlView.cs index 714ebb143..b7cd718b0 100644 --- a/src/App/Controls/Settings/BaseSettingControlView.cs +++ b/src/App/Controls/Settings/BaseSettingControlView.cs @@ -1,14 +1,16 @@ -using Xamarin.Forms; +using System.Runtime.CompilerServices; +using Bit.App.Utilities; +using Xamarin.Forms; namespace Bit.App.Controls { public class BaseSettingItemView : ContentView { public static readonly BindableProperty TitleProperty = BindableProperty.Create( - nameof(Title), typeof(string), typeof(SwitchItemView), null, BindingMode.OneWay); + nameof(Title), typeof(string), typeof(SwitchItemView), null); public static readonly BindableProperty SubtitleProperty = BindableProperty.Create( - nameof(Subtitle), typeof(string), typeof(SwitchItemView), null, BindingMode.OneWay); + nameof(Subtitle), typeof(string), typeof(SwitchItemView), null); public string Title { diff --git a/src/App/Controls/Settings/SettingChooserItemView.xaml.cs b/src/App/Controls/Settings/SettingChooserItemView.xaml.cs index 3ab7dadf5..51df928b1 100644 --- a/src/App/Controls/Settings/SettingChooserItemView.xaml.cs +++ b/src/App/Controls/Settings/SettingChooserItemView.xaml.cs @@ -6,7 +6,7 @@ namespace Bit.App.Controls public partial class SettingChooserItemView : BaseSettingItemView { public static readonly BindableProperty DisplayValueProperty = BindableProperty.Create( - nameof(DisplayValue), typeof(string), typeof(SettingChooserItemView), null, BindingMode.OneWay); + nameof(DisplayValue), typeof(string), typeof(SettingChooserItemView), null); public static readonly BindableProperty ChooseCommandProperty = BindableProperty.Create( nameof(ChooseCommand), typeof(ICommand), typeof(ExternalLinkItemView)); diff --git a/src/App/Pages/Settings/SecuritySettingsPage.xaml b/src/App/Pages/Settings/SecuritySettingsPage.xaml index c3f16753e..ac7fb3397 100644 --- a/src/App/Pages/Settings/SecuritySettingsPage.xaml +++ b/src/App/Pages/Settings/SecuritySettingsPage.xaml @@ -67,7 +67,7 @@ - + @@ -148,6 +150,7 @@ diff --git a/src/App/Pages/Settings/SecuritySettingsPageViewModel.cs b/src/App/Pages/Settings/SecuritySettingsPageViewModel.cs index 8e98aa1fe..a33ae27ae 100644 --- a/src/App/Pages/Settings/SecuritySettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SecuritySettingsPageViewModel.cs @@ -76,7 +76,7 @@ namespace Bit.App.Pages _logger, OnVaultTimeoutActionChangingAsync, AppResources.SessionTimeoutAction, - _ => _inited && !HasVaultTimeoutActionPolicy, + _ => _inited && !HasVaultTimeoutActionPolicy && IsVaultTimeoutActionLockAllowed, ex => HandleException(ex)); ToggleUseThisDeviceToApproveLoginRequestsCommand = CreateDefaultAsyncCommnad(ToggleUseThisDeviceToApproveLoginRequestsAsync, _ => _inited); @@ -129,6 +129,7 @@ namespace Bit.App.Pages get => _canUnlockWithBiometrics; set { + TriggerVaultTimeoutActionLockAllowedPropertyChanged(); if (SetProperty(ref _canUnlockWithBiometrics, value)) { ((ICommand)ToggleCanUnlockWithBiometricsCommand).Execute(null); @@ -141,6 +142,7 @@ namespace Bit.App.Pages get => _canUnlockWithPin; set { + TriggerVaultTimeoutActionLockAllowedPropertyChanged(); if (SetProperty(ref _canUnlockWithPin, value)) { ((ICommand)ToggleCanUnlockWithPinCommand).Execute(null); @@ -148,6 +150,10 @@ namespace Bit.App.Pages } } + public bool IsVaultTimeoutActionLockAllowed => _hasMasterPassword || _canUnlockWithBiometrics || _canUnlockWithPin; + + public string SetUpUnlockMethodLabel => IsVaultTimeoutActionLockAllowed ? null : AppResources.SetUpAnUnlockOptionToChangeYourVaultTimeoutAction; + public TimeSpan? CustomVaultTimeoutTime { get => _customVaultTimeoutTime; @@ -164,6 +170,7 @@ namespace Bit.App.Pages MainThread.BeginInvokeOnMainThread(() => SetProperty(ref _customVaultTimeoutTime, oldValue)); }); } + TriggerVaultTimeoutActionLockAllowedPropertyChanged(); } } @@ -203,8 +210,6 @@ namespace Bit.App.Pages public bool ShowChangeMasterPassword { get; private set; } - private bool IsVaultTimeoutActionLockAllowed => _hasMasterPassword || _canUnlockWithBiometrics || _canUnlockWithPin; - private int? CurrentVaultTimeout => GetRawVaultTimeoutFrom(VaultTimeoutPickerViewModel.SelectedKey); private bool IncludeLinksWithSubscriptionInfo => Device.RuntimePlatform != Device.iOS; @@ -253,6 +258,7 @@ namespace Bit.App.Pages TriggerPropertyChanged(nameof(VaultTimeoutPolicyDescription)); TriggerPropertyChanged(nameof(ShowChangeMasterPassword)); TriggerUpdateCustomVaultTimeoutPicker(); + TriggerVaultTimeoutActionLockAllowedPropertyChanged(); ToggleUseThisDeviceToApproveLoginRequestsCommand.RaiseCanExecuteChanged(); ToggleCanUnlockWithBiometricsCommand.RaiseCanExecuteChanged(); ToggleCanUnlockWithPinCommand.RaiseCanExecuteChanged(); @@ -305,6 +311,7 @@ namespace Bit.App.Pages { _customVaultTimeoutTime = TimeSpan.FromMinutes(vaultTimeout); } + TriggerVaultTimeoutActionLockAllowedPropertyChanged(); } private async Task InitVaultTimeoutActionPickerAsync() @@ -324,6 +331,7 @@ namespace Bit.App.Pages } VaultTimeoutActionPickerViewModel.Init(options, timeoutAction, IsVaultTimeoutActionLockAllowed ? VaultTimeoutAction.Lock : VaultTimeoutAction.Logout); + TriggerVaultTimeoutActionLockAllowedPropertyChanged(); } private async Task ToggleUseThisDeviceToApproveLoginRequestsAsync() @@ -360,6 +368,7 @@ namespace Bit.App.Pages { if (!_canUnlockWithBiometrics) { + MainThread.BeginInvokeOnMainThread(() => TriggerPropertyChanged(nameof(CanUnlockWithBiometrics))); await UpdateVaultTimeoutActionIfNeededAsync(); await _biometricsService.SetCanUnlockWithBiometricsAsync(CanUnlockWithBiometrics); return; @@ -375,11 +384,12 @@ namespace Bit.App.Pages } await _biometricsService.SetCanUnlockWithBiometricsAsync(CanUnlockWithBiometrics); + await InitVaultTimeoutActionPickerAsync(); } public async Task ToggleCanUnlockWithPinAsync() { - if (!CanUnlockWithPin) + if (!_canUnlockWithPin) { await _vaultTimeoutService.ClearAsync(); await UpdateVaultTimeoutActionIfNeededAsync(); @@ -403,10 +413,12 @@ namespace Bit.App.Pages AppResources.No); await _userPinService.SetupPinAsync(newPin, requireMasterPasswordOnRestart); + await InitVaultTimeoutActionPickerAsync(); } private async Task UpdateVaultTimeoutActionIfNeededAsync() { + TriggerVaultTimeoutActionLockAllowedPropertyChanged(); if (IsVaultTimeoutActionLockAllowed) { return; @@ -467,6 +479,16 @@ namespace Bit.App.Pages TriggerPropertyChanged(nameof(CustomVaultTimeoutTime)); } + private void TriggerVaultTimeoutActionLockAllowedPropertyChanged() + { + MainThread.BeginInvokeOnMainThread(() => + { + TriggerPropertyChanged(nameof(IsVaultTimeoutActionLockAllowed)); + TriggerPropertyChanged(nameof(SetUpUnlockMethodLabel)); + VaultTimeoutActionPickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged(); + }); + } + private int? GetRawVaultTimeoutFrom(int vaultTimeoutPickerKey) { if (vaultTimeoutPickerKey == NEVER_SESSION_TIMEOUT_VALUE) @@ -501,7 +523,7 @@ namespace Bit.App.Pages await _vaultTimeoutService.SetVaultTimeoutOptionsAsync(CurrentVaultTimeout, timeoutActionKey); _messagingService.Send(AppHelpers.VAULT_TIMEOUT_ACTION_CHANGED_MESSAGE_COMMAND); - + TriggerVaultTimeoutActionLockAllowedPropertyChanged(); return true; } diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs index bdb644256..859bd87b9 100644 --- a/src/App/Resources/AppResources.Designer.cs +++ b/src/App/Resources/AppResources.Designer.cs @@ -6227,6 +6227,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Set up an unlock option to change your vault timeout action.. + /// + public static string SetUpAnUnlockOptionToChangeYourVaultTimeoutAction { + get { + return ResourceManager.GetString("SetUpAnUnlockOptionToChangeYourVaultTimeoutAction", resourceCulture); + } + } + /// /// Looks up a localized string similar to Set up TOTP. /// diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx index 0709e6934..6cd5fad9d 100644 --- a/src/App/Resources/AppResources.resx +++ b/src/App/Resources/AppResources.resx @@ -2862,4 +2862,7 @@ Do you want to switch to this account? Account logged out. + + Set up an unlock option to change your vault timeout action. + diff --git a/src/App/Styles/ControlTemplates.xaml b/src/App/Styles/ControlTemplates.xaml index e61b80eda..6d75dddf9 100644 --- a/src/App/Styles/ControlTemplates.xaml +++ b/src/App/Styles/ControlTemplates.xaml @@ -6,6 +6,7 @@ x:Class="Bit.App.Styles.ControlTemplates"> + diff --git a/src/App/Utilities/BoolToColorConverter.cs b/src/App/Utilities/BoolToColorConverter.cs new file mode 100644 index 000000000..7fb4ffc32 --- /dev/null +++ b/src/App/Utilities/BoolToColorConverter.cs @@ -0,0 +1,26 @@ +using System; +using Xamarin.Forms; + +namespace Bit.App.Utilities +{ + public class BoolEnablementToTextColorConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, + System.Globalization.CultureInfo culture) + { + if (targetType == typeof(Color) && value is bool valueBool) + { + return valueBool ? ThemeManager.GetResourceColor("TextColor") : + ThemeManager.GetResourceColor("MutedColor"); + } + throw new InvalidOperationException("The value must be a boolean with a Color target."); + } + + public object ConvertBack(object value, Type targetType, object parameter, + System.Globalization.CultureInfo culture) + { + throw new NotSupportedException(); + } + } +} +