mirror of
https://github.com/bitwarden/mobile
synced 2026-01-06 18:43:43 +00:00
Merge branch 'feature/maui-migration' of https://github.com/bitwarden/mobile into feature/maui-migration
# Conflicts: # src/Core/Pages/Settings/OtherSettingsPageViewModel.cs
This commit is contained in:
@@ -1,13 +1,10 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
using Bit.App.Utilities;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@@ -29,32 +26,32 @@ namespace Bit.App.Pages
|
||||
var environmentService = ServiceContainer.Resolve<IEnvironmentService>();
|
||||
var clipboardService = ServiceContainer.Resolve<IClipboardService>();
|
||||
|
||||
ToggleSubmitCrashLogsCommand = CreateDefaultAsyncCommnad(ToggleSubmitCrashLogsAsync);
|
||||
ToggleSubmitCrashLogsCommand = CreateDefaultAsyncRelayCommand(ToggleSubmitCrashLogsAsync, allowsMultipleExecutions: false);
|
||||
|
||||
GoToHelpCenterCommand = CreateDefaultAsyncCommnad(
|
||||
GoToHelpCenterCommand = CreateDefaultAsyncRelayCommand(
|
||||
() => LaunchUriAsync(AppResources.LearnMoreAboutHowToUseBitwardenOnTheHelpCenter,
|
||||
AppResources.ContinueToHelpCenter,
|
||||
ExternalLinksConstants.HELP_CENTER));
|
||||
ExternalLinksConstants.HELP_CENTER), allowsMultipleExecutions: false);
|
||||
|
||||
ContactBitwardenSupportCommand = CreateDefaultAsyncCommnad(
|
||||
ContactBitwardenSupportCommand = CreateDefaultAsyncRelayCommand(
|
||||
() => LaunchUriAsync(AppResources.ContactSupportDescriptionLong,
|
||||
AppResources.ContinueToContactSupport,
|
||||
ExternalLinksConstants.CONTACT_SUPPORT));
|
||||
ExternalLinksConstants.CONTACT_SUPPORT), allowsMultipleExecutions: false);
|
||||
|
||||
GoToWebVaultCommand = CreateDefaultAsyncCommnad(
|
||||
GoToWebVaultCommand = CreateDefaultAsyncRelayCommand(
|
||||
() => LaunchUriAsync(AppResources.ExploreMoreFeaturesOfYourBitwardenAccountOnTheWebApp,
|
||||
AppResources.ContinueToWebApp,
|
||||
environmentService.GetWebVaultUrl()));
|
||||
environmentService.GetWebVaultUrl()), allowsMultipleExecutions: false);
|
||||
|
||||
GoToLearnAboutOrgsCommand = CreateDefaultAsyncCommnad(
|
||||
GoToLearnAboutOrgsCommand = CreateDefaultAsyncRelayCommand(
|
||||
() => LaunchUriAsync(AppResources.LearnAboutOrganizationsDescriptionLong,
|
||||
string.Format(AppResources.ContinueToX, ExternalLinksConstants.BITWARDEN_WEBSITE),
|
||||
ExternalLinksConstants.HELP_ABOUT_ORGANIZATIONS));
|
||||
ExternalLinksConstants.HELP_ABOUT_ORGANIZATIONS), allowsMultipleExecutions: false);
|
||||
|
||||
RateTheAppCommand = CreateDefaultAsyncCommnad(RateAppAsync);
|
||||
RateTheAppCommand = CreateDefaultAsyncRelayCommand(RateAppAsync, allowsMultipleExecutions: false);
|
||||
|
||||
CopyAppInfoCommand = CreateDefaultAsyncCommnad(
|
||||
() => clipboardService.CopyTextAsync(AppInfo));
|
||||
CopyAppInfoCommand = CreateDefaultAsyncRelayCommand(
|
||||
() => clipboardService.CopyTextAsync(AppInfo), allowsMultipleExecutions: false);
|
||||
}
|
||||
|
||||
public bool ShouldSubmitCrashLogs
|
||||
@@ -80,7 +77,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncCommand ToggleSubmitCrashLogsCommand { get; }
|
||||
public AsyncRelayCommand ToggleSubmitCrashLogsCommand { get; }
|
||||
public ICommand GoToHelpCenterCommand { get; }
|
||||
public ICommand ContactBitwardenSupportCommand { get; }
|
||||
public ICommand GoToWebVaultCommand { get; }
|
||||
@@ -97,7 +94,7 @@ namespace Bit.App.Pages
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
TriggerPropertyChanged(nameof(ShouldSubmitCrashLogs));
|
||||
ToggleSubmitCrashLogsCommand.RaiseCanExecuteChanged();
|
||||
ToggleSubmitCrashLogsCommand.NotifyCanExecuteChanged();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@@ -64,7 +58,7 @@ namespace Bit.App.Pages
|
||||
() => _inited,
|
||||
ex => HandleException(ex));
|
||||
|
||||
ToggleShowWebsiteIconsCommand = CreateDefaultAsyncCommnad(ToggleShowWebsiteIconsAsync, () => _inited);
|
||||
ToggleShowWebsiteIconsCommand = CreateDefaultAsyncRelayCommand(ToggleShowWebsiteIconsAsync, () => _inited, allowsMultipleExecutions: false);
|
||||
}
|
||||
|
||||
public PickerViewModel<string> LanguagePickerViewModel { get; }
|
||||
@@ -87,7 +81,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public bool IsShowWebsiteIconsEnabled => ToggleShowWebsiteIconsCommand.CanExecute(null);
|
||||
|
||||
public AsyncCommand ToggleShowWebsiteIconsCommand { get; }
|
||||
public AsyncRelayCommand ToggleShowWebsiteIconsCommand { get; }
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
@@ -102,10 +96,10 @@ namespace Bit.App.Pages
|
||||
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
ToggleShowWebsiteIconsCommand.RaiseCanExecuteChanged();
|
||||
LanguagePickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
|
||||
ThemePickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
|
||||
DefaultDarkThemePickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
|
||||
ToggleShowWebsiteIconsCommand.NotifyCanExecuteChanged();
|
||||
LanguagePickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
|
||||
ThemePickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
|
||||
DefaultDarkThemePickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Input;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui;
|
||||
using Bit.App.Utilities;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@@ -16,8 +12,7 @@ namespace Bit.App.Pages
|
||||
private bool _useDrawOver;
|
||||
private bool _askToAddLogin;
|
||||
|
||||
public bool SupportsAndroidAutofillServices => // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
|
||||
Device.RuntimePlatform == Device.Android && _deviceActionService.SupportsAutofillServices();
|
||||
public bool SupportsAndroidAutofillServices => DeviceInfo.Platform == DevicePlatform.Android && _deviceActionService.SupportsAutofillServices();
|
||||
|
||||
public bool UseAutofillServices
|
||||
{
|
||||
@@ -45,8 +40,7 @@ Device.RuntimePlatform == Device.Android && _deviceActionService.SupportsAutofil
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowUseAccessibilityToggle => // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
|
||||
Device.RuntimePlatform == Device.Android;
|
||||
public bool ShowUseAccessibilityToggle => DeviceInfo.Platform == DevicePlatform.Android;
|
||||
|
||||
public string UseAccessibilityDescription => _deviceActionService.GetAutofillAccessibilityDescription();
|
||||
|
||||
@@ -90,21 +84,21 @@ Device.RuntimePlatform == Device.Android;
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncCommand ToggleUseAutofillServicesCommand { get; private set; }
|
||||
public AsyncCommand ToggleUseInlineAutofillCommand { get; private set; }
|
||||
public AsyncCommand ToggleUseAccessibilityCommand { get; private set; }
|
||||
public AsyncCommand ToggleUseDrawOverCommand { get; private set; }
|
||||
public AsyncCommand ToggleAskToAddLoginCommand { get; private set; }
|
||||
public AsyncRelayCommand ToggleUseAutofillServicesCommand { get; private set; }
|
||||
public AsyncRelayCommand ToggleUseInlineAutofillCommand { get; private set; }
|
||||
public AsyncRelayCommand ToggleUseAccessibilityCommand { get; private set; }
|
||||
public AsyncRelayCommand ToggleUseDrawOverCommand { get; private set; }
|
||||
public AsyncRelayCommand ToggleAskToAddLoginCommand { get; private set; }
|
||||
public ICommand GoToBlockAutofillUrisCommand { get; private set; }
|
||||
|
||||
private void InitAndroidCommands()
|
||||
{
|
||||
ToggleUseAutofillServicesCommand = CreateDefaultAsyncCommnad(() => MainThread.InvokeOnMainThreadAsync(() => ToggleUseAutofillServices()), () => _inited);
|
||||
ToggleUseInlineAutofillCommand = CreateDefaultAsyncCommnad(() => MainThread.InvokeOnMainThreadAsync(() => ToggleUseInlineAutofillEnabledAsync()), () => _inited);
|
||||
ToggleUseAccessibilityCommand = CreateDefaultAsyncCommnad(ToggleUseAccessibilityAsync, () => _inited);
|
||||
ToggleUseDrawOverCommand = CreateDefaultAsyncCommnad(() => MainThread.InvokeOnMainThreadAsync(() => ToggleDrawOver()), () => _inited);
|
||||
ToggleAskToAddLoginCommand = CreateDefaultAsyncCommnad(ToggleAskToAddLoginAsync, () => _inited);
|
||||
GoToBlockAutofillUrisCommand = CreateDefaultAsyncCommnad(() => Page.Navigation.PushAsync(new BlockAutofillUrisPage()));
|
||||
ToggleUseAutofillServicesCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(() => ToggleUseAutofillServices()), () => _inited, allowsMultipleExecutions: false);
|
||||
ToggleUseInlineAutofillCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(() => ToggleUseInlineAutofillEnabledAsync()), () => _inited, allowsMultipleExecutions: false);
|
||||
ToggleUseAccessibilityCommand = CreateDefaultAsyncRelayCommand(ToggleUseAccessibilityAsync, () => _inited, allowsMultipleExecutions: false);
|
||||
ToggleUseDrawOverCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(() => ToggleDrawOver()), () => _inited, allowsMultipleExecutions: false);
|
||||
ToggleAskToAddLoginCommand = CreateDefaultAsyncRelayCommand(ToggleAskToAddLoginAsync, () => _inited, allowsMultipleExecutions: false);
|
||||
GoToBlockAutofillUrisCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushAsync(new BlockAutofillUrisPage()), allowsMultipleExecutions: false);
|
||||
}
|
||||
|
||||
private async Task InitAndroidAutofillSettingsAsync()
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
using Bit.App.Utilities;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@@ -36,7 +33,7 @@ namespace Bit.App.Pages
|
||||
() => _inited,
|
||||
ex => HandleException(ex));
|
||||
|
||||
ToggleCopyTotpAutomaticallyCommand = CreateDefaultAsyncCommnad(ToggleCopyTotpAutomaticallyAsync, () => _inited);
|
||||
ToggleCopyTotpAutomaticallyCommand = CreateDefaultAsyncRelayCommand(ToggleCopyTotpAutomaticallyAsync, () => _inited, allowsMultipleExecutions: false);
|
||||
|
||||
InitAndroidCommands();
|
||||
InitIOSCommands();
|
||||
@@ -56,7 +53,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public PickerViewModel<UriMatchType> DefaultUriMatchDetectionPickerViewModel { get; }
|
||||
|
||||
public AsyncCommand ToggleCopyTotpAutomaticallyCommand { get; private set; }
|
||||
public AsyncRelayCommand ToggleCopyTotpAutomaticallyCommand { get; private set; }
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
@@ -72,11 +69,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
TriggerPropertyChanged(nameof(CopyTotpAutomatically));
|
||||
|
||||
ToggleUseAutofillServicesCommand.RaiseCanExecuteChanged();
|
||||
ToggleUseInlineAutofillCommand.RaiseCanExecuteChanged();
|
||||
ToggleUseAccessibilityCommand.RaiseCanExecuteChanged();
|
||||
ToggleUseDrawOverCommand.RaiseCanExecuteChanged();
|
||||
DefaultUriMatchDetectionPickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
|
||||
ToggleUseAutofillServicesCommand.NotifyCanExecuteChanged();
|
||||
ToggleUseInlineAutofillCommand.NotifyCanExecuteChanged();
|
||||
ToggleUseAccessibilityCommand.NotifyCanExecuteChanged();
|
||||
ToggleUseDrawOverCommand.NotifyCanExecuteChanged();
|
||||
DefaultUriMatchDetectionPickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
using System.Windows.Input;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public partial class AutofillSettingsPageViewModel
|
||||
{
|
||||
public bool SupportsiOSAutofill => // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
|
||||
Device.RuntimePlatform == Device.iOS && _deviceActionService.SupportsAutofillServices();
|
||||
public bool SupportsiOSAutofill => DeviceInfo.Platform == DevicePlatform.iOS && _deviceActionService.SupportsAutofillServices();
|
||||
|
||||
public ICommand GoToPasswordAutofillCommand { get; private set; }
|
||||
public ICommand GoToAppExtensionCommand { get; private set; }
|
||||
|
||||
private void InitIOSCommands()
|
||||
{
|
||||
GoToPasswordAutofillCommand = CreateDefaultAsyncCommnad(() => Page.Navigation.PushModalAsync(new NavigationPage(new AutofillPage())));
|
||||
GoToAppExtensionCommand = CreateDefaultAsyncCommnad(() => Page.Navigation.PushModalAsync(new NavigationPage(new ExtensionPage())));
|
||||
GoToPasswordAutofillCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new AutofillPage())), allowsMultipleExecutions: false);
|
||||
GoToAppExtensionCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new ExtensionPage())), allowsMultipleExecutions: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@ namespace Bit.App.Pages
|
||||
_stateService = ServiceContainer.Resolve<IStateService>();
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>();
|
||||
|
||||
AddUriCommand = new AsyncCommand(AddUriAsync,
|
||||
AddUriCommand = CreateDefaultAsyncRelayCommand(AddUriAsync,
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
EditUriCommand = new AsyncCommand<BlockAutofillUriItemViewModel>(EditUriAsync,
|
||||
EditUriCommand = CreateDefaultAsyncRelayCommand<BlockAutofillUriItemViewModel>(EditUriAsync,
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using Microsoft.Maui.ApplicationModel;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui;
|
||||
using Bit.App.Utilities;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@@ -34,11 +35,11 @@ namespace Bit.App.Pages
|
||||
PageTitle = AppResources.PendingLogInRequests;
|
||||
LoginRequests = new ObservableRangeCollection<PasswordlessLoginResponse>();
|
||||
|
||||
AnswerRequestCommand = new AsyncCommand<PasswordlessLoginResponse>(PasswordlessLoginAsync,
|
||||
AnswerRequestCommand = CreateDefaultAsyncRelayCommand<PasswordlessLoginResponse>(PasswordlessLoginAsync,
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
DeclineAllRequestsCommand = new AsyncCommand(DeclineAllRequestsAsync,
|
||||
DeclineAllRequestsCommand = CreateDefaultAsyncRelayCommand(DeclineAllRequestsAsync,
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
@@ -47,9 +48,9 @@ namespace Bit.App.Pages
|
||||
|
||||
public ICommand RefreshCommand { get; }
|
||||
|
||||
public AsyncCommand<PasswordlessLoginResponse> AnswerRequestCommand { get; }
|
||||
public AsyncRelayCommand<PasswordlessLoginResponse> AnswerRequestCommand { get; }
|
||||
|
||||
public AsyncCommand DeclineAllRequestsCommand { get; }
|
||||
public AsyncRelayCommand DeclineAllRequestsCommand { get; }
|
||||
|
||||
public ObservableRangeCollection<PasswordlessLoginResponse> LoginRequests { get; }
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
Title="{u:I18n AllowScreenCapture}"
|
||||
IsToggled="{Binding IsScreenCaptureAllowed}"
|
||||
IsEnabled="{Binding CanToggleeScreenCaptureAllowed}"
|
||||
IsVisible="{OnPlatform Android=True, iOS=False}"
|
||||
AutomationId="AllowScreenCaptureSwitch"
|
||||
StyleClass="settings-item-view"
|
||||
HorizontalOptions="FillAndExpand" />
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui;
|
||||
using Bit.App.Utilities;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -43,19 +35,21 @@ namespace Bit.App.Pages
|
||||
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
|
||||
_logger = ServiceContainer.Resolve<ILogger>();
|
||||
|
||||
SyncCommand = CreateDefaultAsyncCommnad(SyncAsync, () => _inited);
|
||||
ToggleIsScreenCaptureAllowedCommand = CreateDefaultAsyncCommnad(ToggleIsScreenCaptureAllowedAsync, () => _inited);
|
||||
ToggleShouldConnectToWatchCommand = CreateDefaultAsyncCommnad(ToggleShouldConnectToWatchAsync, () => _inited);
|
||||
SyncCommand = CreateDefaultAsyncRelayCommand(SyncAsync, CanExecuteIfInited, allowsMultipleExecutions: false);
|
||||
ToggleIsScreenCaptureAllowedCommand = CreateDefaultAsyncRelayCommand(ToggleIsScreenCaptureAllowedAsync, CanExecuteIfInited, allowsMultipleExecutions: false);
|
||||
ToggleShouldConnectToWatchCommand = CreateDefaultAsyncRelayCommand(ToggleShouldConnectToWatchAsync, CanExecuteIfInited, allowsMultipleExecutions: false);
|
||||
|
||||
ClearClipboardPickerViewModel = new PickerViewModel<int>(
|
||||
_deviceActionService,
|
||||
_logger,
|
||||
OnClearClipboardChangingAsync,
|
||||
AppResources.ClearClipboard,
|
||||
() => _inited,
|
||||
CanExecuteIfInited,
|
||||
ex => HandleException(ex));
|
||||
}
|
||||
|
||||
private bool CanExecuteIfInited() => _inited;
|
||||
|
||||
public bool EnableSyncOnRefresh
|
||||
{
|
||||
get => _syncOnRefresh;
|
||||
@@ -104,9 +98,9 @@ namespace Bit.App.Pages
|
||||
|
||||
public bool CanToggleShouldConnectToWatch => ToggleShouldConnectToWatchCommand.CanExecute(null);
|
||||
|
||||
public AsyncCommand SyncCommand { get; }
|
||||
public AsyncCommand ToggleIsScreenCaptureAllowedCommand { get; }
|
||||
public AsyncCommand ToggleShouldConnectToWatchCommand { get; }
|
||||
public AsyncRelayCommand SyncCommand { get; }
|
||||
public AsyncRelayCommand ToggleIsScreenCaptureAllowedCommand { get; }
|
||||
public AsyncRelayCommand ToggleShouldConnectToWatchCommand { get; }
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
@@ -125,10 +119,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
TriggerPropertyChanged(nameof(IsScreenCaptureAllowed));
|
||||
TriggerPropertyChanged(nameof(ShouldConnectToWatch));
|
||||
SyncCommand.RaiseCanExecuteChanged();
|
||||
ClearClipboardPickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
|
||||
ToggleIsScreenCaptureAllowedCommand.RaiseCanExecuteChanged();
|
||||
ToggleShouldConnectToWatchCommand.RaiseCanExecuteChanged();
|
||||
SyncCommand.NotifyCanExecuteChanged();
|
||||
ClearClipboardPickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
|
||||
ToggleIsScreenCaptureAllowedCommand.NotifyCanExecuteChanged();
|
||||
ToggleShouldConnectToWatchCommand.NotifyCanExecuteChanged();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -142,8 +136,7 @@ namespace Bit.App.Pages
|
||||
[30] = AppResources.ThirtySeconds,
|
||||
[60] = AppResources.OneMinute
|
||||
};
|
||||
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
|
||||
if (Device.RuntimePlatform != Device.iOS)
|
||||
if (DeviceInfo.Platform != DevicePlatform.iOS)
|
||||
{
|
||||
clearClipboardOptions.Add(120, AppResources.TwoMinutes);
|
||||
clearClipboardOptions.Add(300, AppResources.FiveMinutes);
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<controls:CustomLabel
|
||||
Text="{u:I18n SessionTimeout}"
|
||||
StyleClass="settings-header" />
|
||||
|
||||
|
||||
<Frame
|
||||
IsVisible="{Binding ShowVaultTimeoutPolicyInfo}"
|
||||
Padding="10"
|
||||
@@ -109,8 +109,10 @@
|
||||
|
||||
<controls:SettingChooserItemView
|
||||
Title="{u:I18n SessionTimeoutAction}"
|
||||
Subtitle="{Binding SetUpUnlockMethodLabel}"
|
||||
DisplayValue="{Binding VaultTimeoutActionPickerViewModel.SelectedValue}"
|
||||
ChooseCommand="{Binding VaultTimeoutActionPickerViewModel.SelectOptionCommand}"
|
||||
IsEnabled="{Binding IsVaultTimeoutActionLockAllowed}"
|
||||
AutomationId="VaultTimeoutActionChooser"
|
||||
StyleClass="settings-item-view"
|
||||
HorizontalOptions="FillAndExpand"/>
|
||||
@@ -147,6 +149,7 @@
|
||||
|
||||
<controls:CustomLabel
|
||||
Text="{u:I18n LockNow}"
|
||||
IsVisible="{Binding IsVaultTimeoutActionLockAllowed}"
|
||||
StyleClass="settings-navigatable-label"
|
||||
AutomationId="LockNowLabel">
|
||||
<Label.GestureRecognizers>
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Pages.Accounts;
|
||||
using Bit.Core.Resources.Localization;
|
||||
@@ -12,10 +8,7 @@ using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@@ -77,19 +70,19 @@ namespace Bit.App.Pages
|
||||
_logger,
|
||||
OnVaultTimeoutActionChangingAsync,
|
||||
AppResources.SessionTimeoutAction,
|
||||
() => _inited && !HasVaultTimeoutActionPolicy,
|
||||
() => _inited && !HasVaultTimeoutActionPolicy && IsVaultTimeoutActionLockAllowed,
|
||||
ex => HandleException(ex));
|
||||
|
||||
ToggleUseThisDeviceToApproveLoginRequestsCommand = CreateDefaultAsyncCommnad(ToggleUseThisDeviceToApproveLoginRequestsAsync, () => _inited);
|
||||
GoToPendingLogInRequestsCommand = CreateDefaultAsyncCommnad(() => Page.Navigation.PushModalAsync(new NavigationPage(new LoginPasswordlessRequestsListPage())));
|
||||
ToggleCanUnlockWithBiometricsCommand = CreateDefaultAsyncCommnad(ToggleCanUnlockWithBiometricsAsync, () => _inited);
|
||||
ToggleCanUnlockWithPinCommand = CreateDefaultAsyncCommnad(ToggleCanUnlockWithPinAsync, () => _inited);
|
||||
ShowAccountFingerprintPhraseCommand = CreateDefaultAsyncCommnad(ShowAccountFingerprintPhraseAsync);
|
||||
GoToTwoStepLoginCommand = CreateDefaultAsyncCommnad(() => GoToWebVaultSettingsAsync(AppResources.TwoStepLoginDescriptionLong, AppResources.ContinueToWebApp));
|
||||
GoToChangeMasterPasswordCommand = CreateDefaultAsyncCommnad(() => GoToWebVaultSettingsAsync(AppResources.ChangeMasterPasswordDescriptionLong, AppResources.ContinueToWebApp));
|
||||
LockCommand = CreateDefaultAsyncCommnad(() => _vaultTimeoutService.LockAsync(true, true));
|
||||
LogOutCommand = CreateDefaultAsyncCommnad(LogOutAsync);
|
||||
DeleteAccountCommand = CreateDefaultAsyncCommnad(() => Page.Navigation.PushModalAsync(new NavigationPage(new DeleteAccountPage())));
|
||||
ToggleUseThisDeviceToApproveLoginRequestsCommand = CreateDefaultAsyncRelayCommand(ToggleUseThisDeviceToApproveLoginRequestsAsync, () => _inited, allowsMultipleExecutions: false);
|
||||
GoToPendingLogInRequestsCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new LoginPasswordlessRequestsListPage())), allowsMultipleExecutions: false);
|
||||
ToggleCanUnlockWithBiometricsCommand = CreateDefaultAsyncRelayCommand(ToggleCanUnlockWithBiometricsAsync, () => _inited, allowsMultipleExecutions: false);
|
||||
ToggleCanUnlockWithPinCommand = CreateDefaultAsyncRelayCommand(ToggleCanUnlockWithPinAsync, () => _inited, allowsMultipleExecutions: false);
|
||||
ShowAccountFingerprintPhraseCommand = CreateDefaultAsyncRelayCommand(ShowAccountFingerprintPhraseAsync, allowsMultipleExecutions: false);
|
||||
GoToTwoStepLoginCommand = CreateDefaultAsyncRelayCommand(() => GoToWebVaultSettingsAsync(AppResources.TwoStepLoginDescriptionLong, AppResources.ContinueToWebApp), allowsMultipleExecutions: false);
|
||||
GoToChangeMasterPasswordCommand = CreateDefaultAsyncRelayCommand(() => GoToWebVaultSettingsAsync(AppResources.ChangeMasterPasswordDescriptionLong, AppResources.ContinueToWebApp), allowsMultipleExecutions: false);
|
||||
LockCommand = CreateDefaultAsyncRelayCommand(() => _vaultTimeoutService.LockAsync(true, true), allowsMultipleExecutions: false);
|
||||
LogOutCommand = CreateDefaultAsyncRelayCommand(LogOutAsync, allowsMultipleExecutions: false);
|
||||
DeleteAccountCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new DeleteAccountPage())), allowsMultipleExecutions: false);
|
||||
}
|
||||
|
||||
public bool UseThisDeviceToApproveLoginRequests
|
||||
@@ -114,8 +107,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
var biometricName = AppResources.Biometrics;
|
||||
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
if (DeviceInfo.Platform == DevicePlatform.iOS)
|
||||
{
|
||||
biometricName = _deviceActionService.SupportsFaceBiometric()
|
||||
? AppResources.FaceID
|
||||
@@ -131,6 +123,7 @@ namespace Bit.App.Pages
|
||||
get => _canUnlockWithBiometrics;
|
||||
set
|
||||
{
|
||||
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
|
||||
if (SetProperty(ref _canUnlockWithBiometrics, value))
|
||||
{
|
||||
((ICommand)ToggleCanUnlockWithBiometricsCommand).Execute(null);
|
||||
@@ -143,6 +136,7 @@ namespace Bit.App.Pages
|
||||
get => _canUnlockWithPin;
|
||||
set
|
||||
{
|
||||
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
|
||||
if (SetProperty(ref _canUnlockWithPin, value))
|
||||
{
|
||||
((ICommand)ToggleCanUnlockWithPinCommand).Execute(null);
|
||||
@@ -150,6 +144,10 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsVaultTimeoutActionLockAllowed => _hasMasterPassword || _canUnlockWithBiometrics || _canUnlockWithPin;
|
||||
|
||||
public string SetUpUnlockMethodLabel => IsVaultTimeoutActionLockAllowed ? null : AppResources.SetUpAnUnlockOptionToChangeYourVaultTimeoutAction;
|
||||
|
||||
public TimeSpan? CustomVaultTimeoutTime
|
||||
{
|
||||
get => _customVaultTimeoutTime;
|
||||
@@ -166,6 +164,7 @@ namespace Bit.App.Pages
|
||||
MainThread.BeginInvokeOnMainThread(() => SetProperty(ref _customVaultTimeoutTime, oldValue));
|
||||
});
|
||||
}
|
||||
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,22 +204,19 @@ 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 => // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
|
||||
Device.RuntimePlatform != Device.iOS;
|
||||
private bool IncludeLinksWithSubscriptionInfo => DeviceInfo.Platform != DevicePlatform.iOS;
|
||||
|
||||
private bool HasVaultTimeoutActionPolicy => !string.IsNullOrEmpty(_vaultTimeoutActionPolicy);
|
||||
|
||||
public PickerViewModel<int> VaultTimeoutPickerViewModel { get; }
|
||||
public PickerViewModel<VaultTimeoutAction> VaultTimeoutActionPickerViewModel { get; }
|
||||
|
||||
public AsyncCommand ToggleUseThisDeviceToApproveLoginRequestsCommand { get; }
|
||||
public AsyncRelayCommand ToggleUseThisDeviceToApproveLoginRequestsCommand { get; }
|
||||
public ICommand GoToPendingLogInRequestsCommand { get; }
|
||||
public AsyncCommand ToggleCanUnlockWithBiometricsCommand { get; }
|
||||
public AsyncCommand ToggleCanUnlockWithPinCommand { get; }
|
||||
public AsyncRelayCommand ToggleCanUnlockWithBiometricsCommand { get; }
|
||||
public AsyncRelayCommand ToggleCanUnlockWithPinCommand { get; }
|
||||
public ICommand ShowAccountFingerprintPhraseCommand { get; }
|
||||
public ICommand GoToTwoStepLoginCommand { get; }
|
||||
public ICommand GoToChangeMasterPasswordCommand { get; }
|
||||
@@ -256,11 +252,12 @@ Device.RuntimePlatform != Device.iOS;
|
||||
TriggerPropertyChanged(nameof(VaultTimeoutPolicyDescription));
|
||||
TriggerPropertyChanged(nameof(ShowChangeMasterPassword));
|
||||
TriggerUpdateCustomVaultTimeoutPicker();
|
||||
ToggleUseThisDeviceToApproveLoginRequestsCommand.RaiseCanExecuteChanged();
|
||||
ToggleCanUnlockWithBiometricsCommand.RaiseCanExecuteChanged();
|
||||
ToggleCanUnlockWithPinCommand.RaiseCanExecuteChanged();
|
||||
VaultTimeoutPickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
|
||||
VaultTimeoutActionPickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
|
||||
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
|
||||
ToggleUseThisDeviceToApproveLoginRequestsCommand.NotifyCanExecuteChanged();
|
||||
ToggleCanUnlockWithBiometricsCommand.NotifyCanExecuteChanged();
|
||||
ToggleCanUnlockWithPinCommand.NotifyCanExecuteChanged();
|
||||
VaultTimeoutPickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
|
||||
VaultTimeoutActionPickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -275,7 +272,7 @@ Device.RuntimePlatform != Device.iOS;
|
||||
_maximumVaultTimeoutPolicy = maximumVaultTimeoutPolicy?.GetInt(Policy.MINUTES_KEY);
|
||||
_vaultTimeoutActionPolicy = maximumVaultTimeoutPolicy?.GetString(Policy.ACTION_KEY);
|
||||
|
||||
MainThread.BeginInvokeOnMainThread(VaultTimeoutActionPickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged);
|
||||
MainThread.BeginInvokeOnMainThread(VaultTimeoutActionPickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged);
|
||||
}
|
||||
|
||||
private async Task InitVaultTimeoutPickerAsync()
|
||||
@@ -308,6 +305,7 @@ Device.RuntimePlatform != Device.iOS;
|
||||
{
|
||||
_customVaultTimeoutTime = TimeSpan.FromMinutes(vaultTimeout);
|
||||
}
|
||||
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
|
||||
}
|
||||
|
||||
private async Task InitVaultTimeoutActionPickerAsync()
|
||||
@@ -327,6 +325,7 @@ Device.RuntimePlatform != Device.iOS;
|
||||
}
|
||||
|
||||
VaultTimeoutActionPickerViewModel.Init(options, timeoutAction, IsVaultTimeoutActionLockAllowed ? VaultTimeoutAction.Lock : VaultTimeoutAction.Logout);
|
||||
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
|
||||
}
|
||||
|
||||
private async Task ToggleUseThisDeviceToApproveLoginRequestsAsync()
|
||||
@@ -363,15 +362,15 @@ Device.RuntimePlatform != Device.iOS;
|
||||
{
|
||||
if (!_canUnlockWithBiometrics)
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(() => TriggerPropertyChanged(nameof(CanUnlockWithBiometrics)));
|
||||
await UpdateVaultTimeoutActionIfNeededAsync();
|
||||
await _biometricsService.SetCanUnlockWithBiometricsAsync(CanUnlockWithBiometrics);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
|
||||
if (!_supportsBiometric
|
||||
||
|
||||
!await _platformUtilsService.AuthenticateBiometricAsync(null, Device.RuntimePlatform == Device.Android ? "." : null))
|
||||
!await _platformUtilsService.AuthenticateBiometricAsync(null, DeviceInfo.Platform == DevicePlatform.Android ? "." : null))
|
||||
{
|
||||
_canUnlockWithBiometrics = false;
|
||||
MainThread.BeginInvokeOnMainThread(() => TriggerPropertyChanged(nameof(CanUnlockWithBiometrics)));
|
||||
@@ -379,11 +378,12 @@ Device.RuntimePlatform != Device.iOS;
|
||||
}
|
||||
|
||||
await _biometricsService.SetCanUnlockWithBiometricsAsync(CanUnlockWithBiometrics);
|
||||
await InitVaultTimeoutActionPickerAsync();
|
||||
}
|
||||
|
||||
public async Task ToggleCanUnlockWithPinAsync()
|
||||
{
|
||||
if (!CanUnlockWithPin)
|
||||
if (!_canUnlockWithPin)
|
||||
{
|
||||
await _vaultTimeoutService.ClearAsync();
|
||||
await UpdateVaultTimeoutActionIfNeededAsync();
|
||||
@@ -407,10 +407,12 @@ Device.RuntimePlatform != Device.iOS;
|
||||
AppResources.No);
|
||||
|
||||
await _userPinService.SetupPinAsync(newPin, requireMasterPasswordOnRestart);
|
||||
await InitVaultTimeoutActionPickerAsync();
|
||||
}
|
||||
|
||||
private async Task UpdateVaultTimeoutActionIfNeededAsync()
|
||||
{
|
||||
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
|
||||
if (IsVaultTimeoutActionLockAllowed)
|
||||
{
|
||||
return;
|
||||
@@ -471,6 +473,16 @@ Device.RuntimePlatform != Device.iOS;
|
||||
TriggerPropertyChanged(nameof(CustomVaultTimeoutTime));
|
||||
}
|
||||
|
||||
private void TriggerVaultTimeoutActionLockAllowedPropertyChanged()
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
TriggerPropertyChanged(nameof(IsVaultTimeoutActionLockAllowed));
|
||||
TriggerPropertyChanged(nameof(SetUpUnlockMethodLabel));
|
||||
VaultTimeoutActionPickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
|
||||
});
|
||||
}
|
||||
|
||||
private int? GetRawVaultTimeoutFrom(int vaultTimeoutPickerKey)
|
||||
{
|
||||
if (vaultTimeoutPickerKey == NEVER_SESSION_TIMEOUT_VALUE)
|
||||
@@ -505,7 +517,7 @@ Device.RuntimePlatform != Device.iOS;
|
||||
|
||||
await _vaultTimeoutService.SetVaultTimeoutOptionsAsync(CurrentVaultTimeout, timeoutActionKey);
|
||||
_messagingService.Send(AppHelpers.VAULT_TIMEOUT_ACTION_CHANGED_MESSAGE_COMMAND);
|
||||
|
||||
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@@ -7,7 +8,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public SettingsPageViewModel()
|
||||
{
|
||||
ExecuteSettingItemCommand = new AsyncCommand<SettingsPageListItem>(item => item.ExecuteAsync(),
|
||||
ExecuteSettingItemCommand = CreateDefaultAsyncRelayCommand<SettingsPageListItem>(item => item.ExecuteAsync(),
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
@@ -24,7 +25,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public List<SettingsPageListItem> SettingsItems { get; }
|
||||
|
||||
public AsyncCommand<SettingsPageListItem> ExecuteSettingItemCommand { get; }
|
||||
public AsyncRelayCommand<SettingsPageListItem> ExecuteSettingItemCommand { get; }
|
||||
|
||||
private async Task NavigateToAsync(Page page)
|
||||
{
|
||||
|
||||
@@ -22,15 +22,15 @@ namespace Bit.App.Pages
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>();
|
||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>();
|
||||
|
||||
GoToFoldersCommand = new AsyncCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new FoldersPage())),
|
||||
GoToFoldersCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new FoldersPage())),
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
GoToExportVaultCommand = new AsyncCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new ExportVaultPage())),
|
||||
GoToExportVaultCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new ExportVaultPage())),
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
GoToImportItemsCommand = new AsyncCommand(GoToImportItemsAsync,
|
||||
GoToImportItemsCommand = CreateDefaultAsyncRelayCommand(GoToImportItemsAsync,
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user