using Bit.App.Resources; using Bit.Core.Abstractions; using Bit.Core.Models.View; using Bit.Core.Utilities; using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Xamarin.Forms; namespace Bit.App.Pages { public class CiphersPageViewModel : BaseViewModel { private readonly IPlatformUtilsService _platformUtilsService; private readonly ICipherService _cipherService; private readonly ISearchService _searchService; private CancellationTokenSource _searchCancellationTokenSource; private string _searchText; private bool _showNoData; private bool _showList; public CiphersPageViewModel() { _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _cipherService = ServiceContainer.Resolve("cipherService"); _searchService = ServiceContainer.Resolve("searchService"); Ciphers = new ExtendedObservableCollection(); CipherOptionsCommand = new Command(CipherOptionsAsync); } public Command CipherOptionsCommand { get; set; } public ExtendedObservableCollection Ciphers { get; set; } public Func Filter { get; set; } public string SearchText { get => _searchText; set => SetProperty(ref _searchText, value); } public bool ShowNoData { get => _showNoData; set => SetProperty(ref _showNoData, value); } public bool ShowList { get => _showList; set => SetProperty(ref _showList, value); } public void Search(string searchText, int? timeout = null) { var previousCts = _searchCancellationTokenSource; var cts = new CancellationTokenSource(); Task.Run(async () => { List ciphers = null; var searchable = !string.IsNullOrWhiteSpace(searchText) && searchText.Length > 1; if(searchable) { if(timeout != null) { await Task.Delay(timeout.Value); } if(searchText != (Page as CiphersPage).SearchBar.Text) { return; } else { previousCts?.Cancel(); } try { ciphers = await _searchService.SearchCiphersAsync(searchText, Filter, null, cts.Token); cts.Token.ThrowIfCancellationRequested(); Ciphers.ResetWithRange(ciphers); ShowNoData = Ciphers.Count == 0; } catch(OperationCanceledException) { ciphers = new List(); } } if(ciphers == null) { ciphers = new List(); } Ciphers.ResetWithRange(ciphers); ShowNoData = searchable && Ciphers.Count == 0; ShowList = searchable && !ShowNoData; }, cts.Token); _searchCancellationTokenSource = cts; } public async Task SelectCipherAsync(CipherView cipher) { var page = new ViewPage(cipher.Id); await Page.Navigation.PushModalAsync(new NavigationPage(page)); } private async void CipherOptionsAsync(CipherView cipher) { var option = await Page.DisplayActionSheet(cipher.Name, AppResources.Cancel, null, "1", "2"); if(option == AppResources.Cancel) { return; } // TODO: process options } } }