mirror of
https://github.com/bitwarden/mobile.git
synced 2024-12-17 15:27:43 +01:00
cipher searching
This commit is contained in:
parent
128935eb9f
commit
4ed12a859b
@ -41,17 +41,17 @@ namespace Bit.App.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void RequestFocus(Entry entry)
|
protected void RequestFocus(InputView input)
|
||||||
{
|
{
|
||||||
if(Device.RuntimePlatform == Device.iOS)
|
if(Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
entry.Focus();
|
input.Focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await Task.Delay(AndroidShowModalAnimationDelay);
|
await Task.Delay(AndroidShowModalAnimationDelay);
|
||||||
Device.BeginInvokeOnMainThread(() => entry.Focus());
|
Device.BeginInvokeOnMainThread(() => input.Focus());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
|
||||||
<u:DateTimeConverter x:Key="dateTime" />
|
<u:DateTimeConverter x:Key="dateTime" />
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
@ -30,32 +29,34 @@
|
|||||||
Spacing="0"
|
Spacing="0"
|
||||||
Padding="0">
|
Padding="0">
|
||||||
<controls:MiButton
|
<controls:MiButton
|
||||||
|
StyleClass="btn-title"
|
||||||
Text=""
|
Text=""
|
||||||
BackgroundColor="Transparent"
|
VerticalOptions="CenterAndExpand"
|
||||||
Padding="0"
|
Clicked="BackButton_Clicked" />
|
||||||
WidthRequest="37"
|
|
||||||
FontSize="25"
|
|
||||||
VerticalOptions="CenterAndExpand" />
|
|
||||||
<SearchBar
|
<SearchBar
|
||||||
|
x:Name="_searchBar"
|
||||||
HorizontalOptions="FillAndExpand"
|
HorizontalOptions="FillAndExpand"
|
||||||
BackgroundColor="Transparent"
|
BackgroundColor="Transparent"
|
||||||
Placeholder="{u:I18n SearchVault}" />
|
TextChanged="SearchBar_TextChanged"
|
||||||
|
SearchButtonPressed="SearchBar_SearchButtonPressed"
|
||||||
|
Placeholder="{Binding PageTitle}" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</NavigationPage.TitleView>
|
</NavigationPage.TitleView>
|
||||||
|
|
||||||
<StackLayout x:Name="_mainLayout">
|
<StackLayout x:Name="_mainLayout">
|
||||||
<Label IsVisible="{Binding ShowNoData}"
|
<Label IsVisible="{Binding ShowNoData}"
|
||||||
Text="{Binding NoDataText}"
|
Text="{u:I18n NoItemsToList}"
|
||||||
Margin="20, 0"
|
Margin="20, 0"
|
||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
HorizontalTextAlignment="Center"></Label>
|
HorizontalTextAlignment="Center"></Label>
|
||||||
<ListView x:Name="_listView"
|
<ListView x:Name="_listView"
|
||||||
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding ShowList}"
|
||||||
ItemsSource="{Binding Ciphers}"
|
ItemsSource="{Binding Ciphers}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
HasUnevenRows="true"
|
HasUnevenRows="true"
|
||||||
CachingStrategy="RecycleElement"
|
CachingStrategy="RecycleElement"
|
||||||
|
ItemSelected="RowSelected"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
<ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:CipherView">
|
<DataTemplate x:DataType="views:CipherView">
|
||||||
|
@ -1,25 +1,91 @@
|
|||||||
using System;
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Models.View;
|
||||||
|
using System;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public partial class CiphersPage : BaseContentPage
|
public partial class CiphersPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private CiphersPageViewModel _vm;
|
private CiphersPageViewModel _vm;
|
||||||
|
private bool _hasFocused;
|
||||||
|
|
||||||
public CiphersPage()
|
public CiphersPage(Func<CipherView, bool> filter, bool folder = false, bool collection = false,
|
||||||
|
bool type = false)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
SetActivityIndicator();
|
|
||||||
_vm = BindingContext as CiphersPageViewModel;
|
_vm = BindingContext as CiphersPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
|
_vm.Filter = filter;
|
||||||
|
if(folder)
|
||||||
|
{
|
||||||
|
_vm.PageTitle = AppResources.SearchFolder;
|
||||||
|
}
|
||||||
|
else if(collection)
|
||||||
|
{
|
||||||
|
_vm.PageTitle = AppResources.SearchCollection;
|
||||||
|
}
|
||||||
|
else if(type)
|
||||||
|
{
|
||||||
|
_vm.PageTitle = AppResources.SearchType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_vm.PageTitle = AppResources.SearchVault;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async void OnAppearing()
|
public SearchBar SearchBar => _searchBar;
|
||||||
|
|
||||||
|
protected override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
await LoadOnAppearedAsync(_mainLayout, true, async () => {
|
if(!_hasFocused)
|
||||||
await _vm.LoadAsync();
|
{
|
||||||
});
|
_hasFocused = true;
|
||||||
|
RequestFocus(_searchBar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
|
{
|
||||||
|
var oldLength = e.OldTextValue?.Length ?? 0;
|
||||||
|
var newLength = e.NewTextValue?.Length ?? 0;
|
||||||
|
if(oldLength < 2 && newLength < 2 && oldLength < newLength)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_vm.Search(e.NewTextValue, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SearchBar_SearchButtonPressed(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_vm.Search((sender as SearchBar).Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BackButton_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
GoBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnBackButtonPressed()
|
||||||
|
{
|
||||||
|
GoBack();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GoBack()
|
||||||
|
{
|
||||||
|
Navigation.PopModalAsync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
||||||
|
{
|
||||||
|
((ListView)sender).SelectedItem = null;
|
||||||
|
if(e.SelectedItem is CipherView cipher)
|
||||||
|
{
|
||||||
|
await _vm.SelectCipherAsync(cipher);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@ -11,23 +14,26 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
|
private readonly ISearchService _searchService;
|
||||||
|
private CancellationTokenSource _searchCancellationTokenSource;
|
||||||
|
|
||||||
private string _searchText;
|
private string _searchText;
|
||||||
private string _noDataText;
|
|
||||||
private bool _showNoData;
|
private bool _showNoData;
|
||||||
|
private bool _showList;
|
||||||
|
|
||||||
public CiphersPageViewModel()
|
public CiphersPageViewModel()
|
||||||
{
|
{
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
|
_searchService = ServiceContainer.Resolve<ISearchService>("searchService");
|
||||||
|
|
||||||
PageTitle = AppResources.SearchVault;
|
|
||||||
Ciphers = new ExtendedObservableCollection<CipherView>();
|
Ciphers = new ExtendedObservableCollection<CipherView>();
|
||||||
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
|
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Command CipherOptionsCommand { get; set; }
|
public Command CipherOptionsCommand { get; set; }
|
||||||
public ExtendedObservableCollection<CipherView> Ciphers { get; set; }
|
public ExtendedObservableCollection<CipherView> Ciphers { get; set; }
|
||||||
|
public Func<CipherView, bool> Filter { get; set; }
|
||||||
|
|
||||||
public string SearchText
|
public string SearchText
|
||||||
{
|
{
|
||||||
@ -35,23 +41,67 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _searchText, value);
|
set => SetProperty(ref _searchText, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string NoDataText
|
|
||||||
{
|
|
||||||
get => _noDataText;
|
|
||||||
set => SetProperty(ref _noDataText, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ShowNoData
|
public bool ShowNoData
|
||||||
{
|
{
|
||||||
get => _showNoData;
|
get => _showNoData;
|
||||||
set => SetProperty(ref _showNoData, value);
|
set => SetProperty(ref _showNoData, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadAsync()
|
public bool ShowList
|
||||||
{
|
{
|
||||||
var ciphers = await _cipherService.GetAllDecryptedAsync();
|
get => _showList;
|
||||||
Ciphers.ResetWithRange(ciphers);
|
set => SetProperty(ref _showList, value);
|
||||||
ShowNoData = Ciphers.Count == 0;
|
}
|
||||||
|
|
||||||
|
public void Search(string searchText, int? timeout = null)
|
||||||
|
{
|
||||||
|
var previousCts = _searchCancellationTokenSource;
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
List<CipherView> 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<CipherView>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ciphers == null)
|
||||||
|
{
|
||||||
|
ciphers = new List<CipherView>();
|
||||||
|
}
|
||||||
|
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)
|
private async void CipherOptionsAsync(CipherView cipher)
|
||||||
|
@ -10,7 +10,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly GroupingsPageViewModel _viewModel;
|
private readonly GroupingsPageViewModel _vm;
|
||||||
|
|
||||||
public GroupingsPage()
|
public GroupingsPage()
|
||||||
: this(true)
|
: this(true)
|
||||||
@ -23,15 +23,15 @@ namespace Bit.App.Pages
|
|||||||
SetActivityIndicator();
|
SetActivityIndicator();
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_viewModel = BindingContext as GroupingsPageViewModel;
|
_vm = BindingContext as GroupingsPageViewModel;
|
||||||
_viewModel.Page = this;
|
_vm.Page = this;
|
||||||
_viewModel.MainPage = mainPage;
|
_vm.MainPage = mainPage;
|
||||||
_viewModel.Type = type;
|
_vm.Type = type;
|
||||||
_viewModel.FolderId = folderId;
|
_vm.FolderId = folderId;
|
||||||
_viewModel.CollectionId = collectionId;
|
_vm.CollectionId = collectionId;
|
||||||
if(pageTitle != null)
|
if(pageTitle != null)
|
||||||
{
|
{
|
||||||
_viewModel.PageTitle = pageTitle;
|
_vm.PageTitle = pageTitle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,14 +52,14 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if(!_syncService.SyncInProgress)
|
if(!_syncService.SyncInProgress)
|
||||||
{
|
{
|
||||||
await _viewModel.LoadAsync();
|
await _vm.LoadAsync();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await Task.Delay(5000);
|
await Task.Delay(5000);
|
||||||
if(!_viewModel.Loaded)
|
if(!_vm.Loaded)
|
||||||
{
|
{
|
||||||
await _viewModel.LoadAsync();
|
await _vm.LoadAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -73,6 +73,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
||||||
{
|
{
|
||||||
|
((ListView)sender).SelectedItem = null;
|
||||||
if(!(e.SelectedItem is GroupingsPageListItem item))
|
if(!(e.SelectedItem is GroupingsPageListItem item))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -80,25 +81,26 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
if(item.Cipher != null)
|
if(item.Cipher != null)
|
||||||
{
|
{
|
||||||
await _viewModel.SelectCipherAsync(item.Cipher);
|
await _vm.SelectCipherAsync(item.Cipher);
|
||||||
}
|
}
|
||||||
else if(item.Folder != null)
|
else if(item.Folder != null)
|
||||||
{
|
{
|
||||||
await _viewModel.SelectFolderAsync(item.Folder);
|
await _vm.SelectFolderAsync(item.Folder);
|
||||||
}
|
}
|
||||||
else if(item.Collection != null)
|
else if(item.Collection != null)
|
||||||
{
|
{
|
||||||
await _viewModel.SelectCollectionAsync(item.Collection);
|
await _vm.SelectCollectionAsync(item.Collection);
|
||||||
}
|
}
|
||||||
else if(item.Type != null)
|
else if(item.Type != null)
|
||||||
{
|
{
|
||||||
await _viewModel.SelectTypeAsync(item.Type.Value);
|
await _vm.SelectTypeAsync(item.Type.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Search_Clicked(object sender, System.EventArgs e)
|
private async void Search_Clicked(object sender, System.EventArgs e)
|
||||||
{
|
{
|
||||||
await Navigation.PushModalAsync(new NavigationPage(new CiphersPage()), false);
|
await Navigation.PushModalAsync(new NavigationPage(
|
||||||
|
new CiphersPage(_vm.Filter,_vm.FolderId != null, _vm.CollectionId != null, _vm.Type != null)), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ using Bit.Core.Enums;
|
|||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -56,6 +57,7 @@ namespace Bit.App.Pages
|
|||||||
public CipherType? Type { get; set; }
|
public CipherType? Type { get; set; }
|
||||||
public string FolderId { get; set; }
|
public string FolderId { get; set; }
|
||||||
public string CollectionId { get; set; }
|
public string CollectionId { get; set; }
|
||||||
|
public Func<CipherView, bool> Filter { get; set; }
|
||||||
|
|
||||||
public List<CipherView> Ciphers { get; set; }
|
public List<CipherView> Ciphers { get; set; }
|
||||||
public List<CipherView> FavoriteCiphers { get; set; }
|
public List<CipherView> FavoriteCiphers { get; set; }
|
||||||
@ -249,6 +251,7 @@ namespace Bit.App.Pages
|
|||||||
_folderCounts.Clear();
|
_folderCounts.Clear();
|
||||||
_collectionCounts.Clear();
|
_collectionCounts.Clear();
|
||||||
_typeCounts.Clear();
|
_typeCounts.Clear();
|
||||||
|
Filter = null;
|
||||||
|
|
||||||
if(MainPage)
|
if(MainPage)
|
||||||
{
|
{
|
||||||
@ -267,7 +270,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if(Type != null)
|
if(Type != null)
|
||||||
{
|
{
|
||||||
Ciphers = _allCiphers.Where(c => c.Type == Type.Value).ToList();
|
Filter = c => c.Type == Type.Value;
|
||||||
}
|
}
|
||||||
else if(FolderId != null)
|
else if(FolderId != null)
|
||||||
{
|
{
|
||||||
@ -286,7 +289,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
PageTitle = AppResources.FolderNone;
|
PageTitle = AppResources.FolderNone;
|
||||||
}
|
}
|
||||||
Ciphers = _allCiphers.Where(c => c.FolderId == folderId).ToList();
|
Filter = c => c.FolderId == folderId;
|
||||||
}
|
}
|
||||||
else if(CollectionId != null)
|
else if(CollectionId != null)
|
||||||
{
|
{
|
||||||
@ -297,13 +300,13 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
PageTitle = collectionNode.Node.Name;
|
PageTitle = collectionNode.Node.Name;
|
||||||
}
|
}
|
||||||
Ciphers = _allCiphers.Where(c => c.CollectionIds?.Contains(CollectionId) ?? false).ToList();
|
Filter = c => c.CollectionIds?.Contains(CollectionId) ?? false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PageTitle = AppResources.AllItems;
|
PageTitle = AppResources.AllItems;
|
||||||
Ciphers = _allCiphers;
|
|
||||||
}
|
}
|
||||||
|
Ciphers = Filter != null ? _allCiphers.Where(Filter).ToList() : _allCiphers;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var c in _allCiphers)
|
foreach(var c in _allCiphers)
|
||||||
|
38
src/App/Resources/AppResources.Designer.cs
generated
38
src/App/Resources/AppResources.Designer.cs
generated
@ -2472,6 +2472,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to There are no items to list..
|
||||||
|
/// </summary>
|
||||||
|
public static string NoItemsToList {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("NoItemsToList", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to No passwords to list..
|
/// Looks up a localized string similar to No passwords to list..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -2851,7 +2860,34 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Search Vault.
|
/// Looks up a localized string similar to Search collection.
|
||||||
|
/// </summary>
|
||||||
|
public static string SearchCollection {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SearchCollection", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Search folder.
|
||||||
|
/// </summary>
|
||||||
|
public static string SearchFolder {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SearchFolder", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Search type.
|
||||||
|
/// </summary>
|
||||||
|
public static string SearchType {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SearchType", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Search vault.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string SearchVault {
|
public static string SearchVault {
|
||||||
get {
|
get {
|
||||||
|
@ -656,7 +656,7 @@
|
|||||||
<value>Re-type Master Password</value>
|
<value>Re-type Master Password</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SearchVault" xml:space="preserve">
|
<data name="SearchVault" xml:space="preserve">
|
||||||
<value>Search Vault</value>
|
<value>Search vault</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Security" xml:space="preserve">
|
<data name="Security" xml:space="preserve">
|
||||||
<value>Security</value>
|
<value>Security</value>
|
||||||
@ -1396,4 +1396,16 @@
|
|||||||
<data name="NoPasswordsToList" xml:space="preserve">
|
<data name="NoPasswordsToList" xml:space="preserve">
|
||||||
<value>No passwords to list.</value>
|
<value>No passwords to list.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="NoItemsToList" xml:space="preserve">
|
||||||
|
<value>There are no items to list.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SearchCollection" xml:space="preserve">
|
||||||
|
<value>Search collection</value>
|
||||||
|
</data>
|
||||||
|
<data name="SearchFolder" xml:space="preserve">
|
||||||
|
<value>Search folder</value>
|
||||||
|
</data>
|
||||||
|
<data name="SearchType" xml:space="preserve">
|
||||||
|
<value>Search type</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -48,16 +48,6 @@
|
|||||||
<Setter Property="FontAttributes"
|
<Setter Property="FontAttributes"
|
||||||
Value="Bold" />
|
Value="Bold" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style TargetType="SearchBar">
|
|
||||||
<Setter Property="BackgroundColor"
|
|
||||||
Value="Transparent" />
|
|
||||||
<Setter Property="TextColor"
|
|
||||||
Value="{StaticResource HeaderEntryTextColor}" />
|
|
||||||
<Setter Property="CancelButtonColor"
|
|
||||||
Value="{StaticResource HeaderEntryTextColor}" />
|
|
||||||
<Setter Property="PlaceholderColor"
|
|
||||||
Value="{StaticResource HeaderEntryPlaceholderColor}" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<Style TargetType="Button"
|
<Style TargetType="Button"
|
||||||
@ -79,6 +69,31 @@
|
|||||||
Value="{StaticResource DisabledIconColor}" />
|
Value="{StaticResource DisabledIconColor}" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<!-- Title -->
|
||||||
|
<Style TargetType="Button"
|
||||||
|
Class="btn-title"
|
||||||
|
ApplyToDerivedTypes="True">
|
||||||
|
<Setter Property="BackgroundColor"
|
||||||
|
Value="Transparent" />
|
||||||
|
<Setter Property="Padding"
|
||||||
|
Value="0" />
|
||||||
|
<Setter Property="WidthRequest"
|
||||||
|
Value="37" />
|
||||||
|
<Setter Property="FontSize"
|
||||||
|
Value="25" />
|
||||||
|
<Setter Property="TextColor"
|
||||||
|
Value="{StaticResource TitleTextColor}" />
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="SearchBar">
|
||||||
|
<Setter Property="BackgroundColor"
|
||||||
|
Value="Transparent" />
|
||||||
|
<Setter Property="TextColor"
|
||||||
|
Value="{StaticResource TitleEntryTextColor}" />
|
||||||
|
<Setter Property="CancelButtonColor"
|
||||||
|
Value="{StaticResource TitleEntryTextColor}" />
|
||||||
|
<Setter Property="PlaceholderColor"
|
||||||
|
Value="{StaticResource TitleEntryPlaceholderColor}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
<!-- List -->
|
<!-- List -->
|
||||||
<Style TargetType="ListView"
|
<Style TargetType="ListView"
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
<Color x:Key="BoxBorderColor">#f0f0f0</Color>
|
<Color x:Key="BoxBorderColor">#f0f0f0</Color>
|
||||||
<Color x:Key="BoxHeaderTextColor">#3c8dbc</Color>
|
<Color x:Key="BoxHeaderTextColor">#3c8dbc</Color>
|
||||||
|
|
||||||
|
<Color x:Key="HeaderTextColor">#ffffff</Color>
|
||||||
<Color x:Key="HeaderEntryTextColor">#ffffff</Color>
|
<Color x:Key="HeaderEntryTextColor">#ffffff</Color>
|
||||||
<Color x:Key="HeaderEntryPlaceholderColor">#707070</Color>
|
<Color x:Key="HeaderEntryPlaceholderColor">#707070</Color>
|
||||||
|
|
||||||
|
@ -18,8 +18,9 @@
|
|||||||
<Color x:Key="BoxBorderColor">#dddddd</Color>
|
<Color x:Key="BoxBorderColor">#dddddd</Color>
|
||||||
<Color x:Key="BoxHeaderTextColor">#3c8dbc</Color>
|
<Color x:Key="BoxHeaderTextColor">#3c8dbc</Color>
|
||||||
|
|
||||||
<Color x:Key="HeaderEntryTextColor">#ffffff</Color>
|
<Color x:Key="TitleTextColor">#ffffff</Color>
|
||||||
<Color x:Key="HeaderEntryPlaceholderColor">#c0dbeb</Color>
|
<Color x:Key="TitleEntryTextColor">#ffffff</Color>
|
||||||
|
<Color x:Key="TitleEntryPlaceholderColor">#c0dbeb</Color>
|
||||||
|
|
||||||
<Color x:Key="ListItemBorderColor">#f0f0f0</Color>
|
<Color x:Key="ListItemBorderColor">#f0f0f0</Color>
|
||||||
<Color x:Key="ListHeaderTextColor">#3c8dbc</Color>
|
<Color x:Key="ListHeaderTextColor">#3c8dbc</Color>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
|
|
||||||
@ -11,7 +12,8 @@ namespace Bit.Core.Abstractions
|
|||||||
Task IndexCiphersAsync();
|
Task IndexCiphersAsync();
|
||||||
bool IsSearchable(string query);
|
bool IsSearchable(string query);
|
||||||
Task<List<CipherView>> SearchCiphersAsync(string query, Func<CipherView, bool> filter = null,
|
Task<List<CipherView>> SearchCiphersAsync(string query, Func<CipherView, bool> filter = null,
|
||||||
List<CipherView> ciphers = null);
|
List<CipherView> ciphers = null, CancellationToken ct = default(CancellationToken));
|
||||||
List<CipherView> SearchCiphersBasic(List<CipherView> ciphers, string query);
|
List<CipherView> SearchCiphersBasic(List<CipherView> ciphers, string query,
|
||||||
|
CancellationToken ct = default(CancellationToken));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,6 +3,7 @@ using Bit.Core.Models.View;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
@ -24,7 +25,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public bool IsSearchable(string query)
|
public bool IsSearchable(string query)
|
||||||
{
|
{
|
||||||
return true;
|
return (query?.Length ?? 0) > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task IndexCiphersAsync()
|
public Task IndexCiphersAsync()
|
||||||
@ -34,7 +35,7 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<CipherView>> SearchCiphersAsync(string query, Func<CipherView, bool> filter = null,
|
public async Task<List<CipherView>> SearchCiphersAsync(string query, Func<CipherView, bool> filter = null,
|
||||||
List<CipherView> ciphers = null)
|
List<CipherView> ciphers = null, CancellationToken ct = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var results = new List<CipherView>();
|
var results = new List<CipherView>();
|
||||||
if(query != null)
|
if(query != null)
|
||||||
@ -49,10 +50,14 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
ciphers = await _cipherService.GetAllDecryptedAsync();
|
ciphers = await _cipherService.GetAllDecryptedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ct.ThrowIfCancellationRequested();
|
||||||
if(filter != null)
|
if(filter != null)
|
||||||
{
|
{
|
||||||
ciphers = ciphers.Where(filter).ToList();
|
ciphers = ciphers.Where(filter).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ct.ThrowIfCancellationRequested();
|
||||||
if(!IsSearchable(query))
|
if(!IsSearchable(query))
|
||||||
{
|
{
|
||||||
return ciphers;
|
return ciphers;
|
||||||
@ -62,11 +67,14 @@ namespace Bit.Core.Services
|
|||||||
// TODO: advanced searching with index
|
// TODO: advanced searching with index
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CipherView> SearchCiphersBasic(List<CipherView> ciphers, string query)
|
public List<CipherView> SearchCiphersBasic(List<CipherView> ciphers, string query,
|
||||||
|
CancellationToken ct = default(CancellationToken))
|
||||||
{
|
{
|
||||||
|
ct.ThrowIfCancellationRequested();
|
||||||
query = query.Trim().ToLower();
|
query = query.Trim().ToLower();
|
||||||
return ciphers.Where(c =>
|
return ciphers.Where(c =>
|
||||||
{
|
{
|
||||||
|
ct.ThrowIfCancellationRequested();
|
||||||
if(c.Name?.ToLower().Contains(query) ?? false)
|
if(c.Name?.ToLower().Contains(query) ?? false)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
Loading…
Reference in New Issue
Block a user