mirror of
https://github.com/bitwarden/mobile
synced 2025-12-29 14:43:50 +00:00
122 lines
4.2 KiB
C#
122 lines
4.2 KiB
C#
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<TKey> : ExtendedViewModel
|
|
{
|
|
const string SELECTED_CHARACTER = "✓";
|
|
|
|
private readonly IDeviceActionService _deviceActionService;
|
|
private readonly ILogger _logger;
|
|
private readonly Func<TKey, Task<bool>> _onSelectionChangingAsync;
|
|
private readonly string _title;
|
|
|
|
public Dictionary<TKey, string> _items;
|
|
private TKey _selectedKey;
|
|
private TKey _defaultSelectedKeyIfFailsToFind;
|
|
private Func<TKey, Task> _afterSelectionChangedAsync;
|
|
|
|
public PickerViewModel(IDeviceActionService deviceActionService,
|
|
ILogger logger,
|
|
Func<TKey, Task<bool>> onSelectionChangingAsync,
|
|
string title,
|
|
Func<bool> canExecuteSelectOptionCommand = null,
|
|
Action<Exception> 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<TKey, string> 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<TKey>.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<TKey>.Default.Equals(optionKey, _selectedKey)
|
|
||
|
|
!await _onSelectionChangingAsync(optionKey))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_selectedKey = optionKey;
|
|
TriggerPropertyChanged(nameof(SelectedValue));
|
|
|
|
if (_afterSelectionChangedAsync != null)
|
|
{
|
|
await _afterSelectionChangedAsync(_selectedKey);
|
|
}
|
|
}
|
|
|
|
public void SetAfterSelectionChanged(Func<TKey, Task> afterSelectionChangedAsync) => _afterSelectionChangedAsync = afterSelectionChangedAsync;
|
|
|
|
private string CreateSelectableOption(string option, bool selected) => selected ? ToSelectedOption(option) : option;
|
|
|
|
private string ToSelectedOption(string option) => $"{SELECTED_CHARACTER} {option}";
|
|
}
|
|
}
|