using Bit.App.Abstractions; using Bit.Core.Resources.Localization; using Bit.Core.Abstractions; using Bit.Core.Utilities; using CommunityToolkit.Mvvm.Input; namespace Bit.App.Pages { public class PickerViewModel : ExtendedViewModel { const string SELECTED_CHARACTER = "✓"; private readonly IDeviceActionService _deviceActionService; private readonly ILogger _logger; private readonly Func> _onSelectionChangingAsync; private readonly string _title; public Dictionary _items; private TKey _selectedKey; private TKey _defaultSelectedKeyIfFailsToFind; private Func _afterSelectionChangedAsync; public PickerViewModel(IDeviceActionService deviceActionService, ILogger logger, Func> onSelectionChangingAsync, string title, Func canExecuteSelectOptionCommand = null, Action onSelectOptionCommandException = null) { _deviceActionService = deviceActionService; _logger = logger; _onSelectionChangingAsync = onSelectionChangingAsync; _title = title; SelectOptionCommand = CreateDefaultAsyncRelayCommand(SelectOptionAsync, canExecuteSelectOptionCommand, onSelectOptionCommandException, allowsMultipleExecutions: false); } public AsyncRelayCommand SelectOptionCommand { get; } public TKey SelectedKey => _selectedKey; public string SelectedValue { get { if (_items == null) { return string.Empty; } if (_items.TryGetValue(_selectedKey, out var option)) { return option; } _selectedKey = _defaultSelectedKeyIfFailsToFind; return _items[_selectedKey]; } } public void Init(Dictionary items, TKey currentSelectedKey, TKey defaultSelectedKeyIfFailsToFind, bool logIfKeyNotFound = true) { _items = items; _defaultSelectedKeyIfFailsToFind = defaultSelectedKeyIfFailsToFind; Select(currentSelectedKey, logIfKeyNotFound); } public void Select(TKey key, bool logIfKeyNotFound = true) { if (!_items.ContainsKey(key)) { if (logIfKeyNotFound) { _logger.Error($"There is no {_title} options for key: {key}"); } key = _defaultSelectedKeyIfFailsToFind; } _selectedKey = key; MainThread.BeginInvokeOnMainThread(() => TriggerPropertyChanged(nameof(SelectedValue))); } private async Task SelectOptionAsync() { var selection = await _deviceActionService.DisplayActionSheetAsync(_title, AppResources.Cancel, null, _items.Select(o => CreateSelectableOption(o.Value, EqualityComparer.Default.Equals(o.Key, _selectedKey))) .ToArray() ); if (selection == null || selection == AppResources.Cancel) { return; } var sanitizedSelection = selection.Replace($"{SELECTED_CHARACTER} ", string.Empty); var optionKey = _items.First(o => o.Value == sanitizedSelection).Key; if (EqualityComparer.Default.Equals(optionKey, _selectedKey) || !await _onSelectionChangingAsync(optionKey)) { return; } _selectedKey = optionKey; TriggerPropertyChanged(nameof(SelectedValue)); if (_afterSelectionChangedAsync != null) { await _afterSelectionChangedAsync(_selectedKey); } } public void SetAfterSelectionChanged(Func afterSelectionChangedAsync) => _afterSelectionChangedAsync = afterSelectionChangedAsync; private string CreateSelectableOption(string option, bool selected) => selected ? ToSelectedOption(option) : option; private string ToSelectedOption(string option) => $"{SELECTED_CHARACTER} {option}"; } }