1
0
mirror of https://github.com/bitwarden/mobile.git synced 2024-12-02 13:13:31 +01:00

support for showing groupings on ciphers list page

This commit is contained in:
Kyle Spearrin 2018-12-06 14:17:28 -05:00
parent 5cc1e2bb29
commit 0b1c0be0f0
3 changed files with 152 additions and 75 deletions

View File

@ -36,6 +36,8 @@ namespace Bit.App.Controls
}; };
CountLabel.SetBinding(Label.TextProperty, string.Format("{0}.Node.{1}", CountLabel.SetBinding(Label.TextProperty, string.Format("{0}.Node.{1}",
nameof(VaultListPageModel.GroupingOrCipher.Grouping), nameof(VaultListPageModel.Grouping.Count))); nameof(VaultListPageModel.GroupingOrCipher.Grouping), nameof(VaultListPageModel.Grouping.Count)));
CountLabel.SetBinding(VisualElement.IsVisibleProperty, string.Format("{0}.Node.{1}",
nameof(VaultListPageModel.GroupingOrCipher.Grouping), nameof(VaultListPageModel.Grouping.ShowCount)));
var stackLayout = new StackLayout var stackLayout = new StackLayout
{ {

View File

@ -217,7 +217,7 @@ namespace Bit.App.Models.Page
Count = count; Count = count;
} }
public Grouping(Folder folder, int count) public Grouping(Folder folder, int? count)
{ {
Id = folder.Id; Id = folder.Id;
Name = folder.Name?.Decrypt(); Name = folder.Name?.Decrypt();
@ -225,7 +225,7 @@ namespace Bit.App.Models.Page
Count = count; Count = count;
} }
public Grouping(Collection collection, int count) public Grouping(Collection collection, int? count)
{ {
Id = collection.Id; Id = collection.Id;
Name = collection.Name?.Decrypt(collection.OrganizationId); Name = collection.Name?.Decrypt(collection.OrganizationId);
@ -235,9 +235,10 @@ namespace Bit.App.Models.Page
public string Id { get; set; } public string Id { get; set; }
public string Name { get; set; } = AppResources.FolderNone; public string Name { get; set; } = AppResources.FolderNone;
public int Count { get; set; } public int? Count { get; set; }
public bool Folder { get; set; } public bool Folder { get; set; }
public bool Collection { get; set; } public bool Collection { get; set; }
public bool ShowCount => Count.HasValue;
} }
} }
} }

View File

@ -25,6 +25,8 @@ namespace Bit.App.Pages
private readonly IAppSettingsService _appSettingsService; private readonly IAppSettingsService _appSettingsService;
private readonly IGoogleAnalyticsService _googleAnalyticsService; private readonly IGoogleAnalyticsService _googleAnalyticsService;
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly IFolderService _folderService;
private readonly ICollectionService _collectionService;
private CancellationTokenSource _filterResultsCancellationTokenSource; private CancellationTokenSource _filterResultsCancellationTokenSource;
private readonly bool _favorites = false; private readonly bool _favorites = false;
private readonly bool _folder = false; private readonly bool _folder = false;
@ -52,13 +54,16 @@ namespace Bit.App.Pages
_appSettingsService = Resolver.Resolve<IAppSettingsService>(); _appSettingsService = Resolver.Resolve<IAppSettingsService>();
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>(); _googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
_deviceActionService = Resolver.Resolve<IDeviceActionService>(); _deviceActionService = Resolver.Resolve<IDeviceActionService>();
_folderService = Resolver.Resolve<IFolderService>();
_collectionService = Resolver.Resolve<ICollectionService>();
Init(); Init();
} }
public ExtendedObservableCollection<Section<Cipher>> PresentationSections { get; private set; } public ExtendedObservableCollection<Section<GroupingOrCipher>> PresentationSections { get; private set; }
= new ExtendedObservableCollection<Section<Cipher>>(); = new ExtendedObservableCollection<Section<GroupingOrCipher>>();
public Cipher[] Ciphers { get; set; } = new Cipher[] { }; public Cipher[] Ciphers { get; set; } = new Cipher[] { };
public GroupingOrCipher[] Groupings { get; set; } = new GroupingOrCipher[] { };
public ExtendedListView ListView { get; set; } public ExtendedListView ListView { get; set; }
public SearchBar Search { get; set; } public SearchBar Search { get; set; }
public ActivityIndicator LoadingIndicator { get; set; } public ActivityIndicator LoadingIndicator { get; set; }
@ -75,11 +80,10 @@ namespace Bit.App.Pages
IsGroupingEnabled = true, IsGroupingEnabled = true,
ItemsSource = PresentationSections, ItemsSource = PresentationSections,
HasUnevenRows = true, HasUnevenRows = true,
GroupHeaderTemplate = new DataTemplate(() => new SectionHeaderViewCell(nameof(Section<Cipher>.Name), GroupHeaderTemplate = new DataTemplate(() => new SectionHeaderViewCell(
nameof(Section<Cipher>.Count))), nameof(Section<GroupingOrCipher>.Name), nameof(Section<GroupingOrCipher>.Count))),
GroupShortNameBinding = new Binding(nameof(Section<Cipher>.Name)), GroupShortNameBinding = new Binding(nameof(Section<GroupingOrCipher>.Name)),
ItemTemplate = new DataTemplate(() => new VaultListViewCell( ItemTemplate = new GroupingOrCipherDataTemplateSelector(this)
(Cipher c) => Helpers.CipherMoreClickedAsync(this, c, !string.IsNullOrWhiteSpace(_uri))))
}; };
if(Device.RuntimePlatform == Device.iOS) if(Device.RuntimePlatform == Device.iOS)
@ -250,7 +254,7 @@ namespace Bit.App.Pages
if(string.IsNullOrWhiteSpace(searchFilter)) if(string.IsNullOrWhiteSpace(searchFilter))
{ {
LoadSections(Ciphers, ct); LoadSections(Ciphers, Groupings, ct);
} }
else else
{ {
@ -263,7 +267,7 @@ namespace Bit.App.Pages
.ToArray(); .ToArray();
ct.ThrowIfCancellationRequested(); ct.ThrowIfCancellationRequested();
LoadSections(filteredCiphers, ct); LoadSections(filteredCiphers, null, ct);
} }
} }
@ -291,7 +295,7 @@ namespace Bit.App.Pages
}); });
AddCipherItem?.InitEvents(); AddCipherItem?.InitEvents();
ListView.ItemSelected += CipherSelected; ListView.ItemSelected += GroupingOrCipherSelected;
Search.TextChanged += SearchBar_TextChanged; Search.TextChanged += SearchBar_TextChanged;
Search.SearchButtonPressed += SearchBar_SearchButtonPressed; Search.SearchButtonPressed += SearchBar_SearchButtonPressed;
_filterResultsCancellationTokenSource = FetchAndLoadVault(); _filterResultsCancellationTokenSource = FetchAndLoadVault();
@ -303,7 +307,7 @@ namespace Bit.App.Pages
MessagingCenter.Unsubscribe<Application, bool>(Application.Current, "SyncCompleted"); MessagingCenter.Unsubscribe<Application, bool>(Application.Current, "SyncCompleted");
AddCipherItem?.Dispose(); AddCipherItem?.Dispose();
ListView.ItemSelected -= CipherSelected; ListView.ItemSelected -= GroupingOrCipherSelected;
Search.TextChanged -= SearchBar_TextChanged; Search.TextChanged -= SearchBar_TextChanged;
Search.SearchButtonPressed -= SearchBar_SearchButtonPressed; Search.SearchButtonPressed -= SearchBar_SearchButtonPressed;
} }
@ -324,10 +328,28 @@ namespace Bit.App.Pages
if(_folder || !string.IsNullOrWhiteSpace(_folderId)) if(_folder || !string.IsNullOrWhiteSpace(_folderId))
{ {
ciphers = await _cipherService.GetAllByFolderAsync(_folderId); ciphers = await _cipherService.GetAllByFolderAsync(_folderId);
var folders = await _folderService.GetAllAsync();
var fGroupings = folders.Select(f => new Grouping(f, null)).OrderBy(g => g.Name).ToList();
var fTreeNodes = Helpers.GetAllNested(fGroupings);
var fTreeNode = Helpers.GetTreeNodeObject(fTreeNodes, _folderId);
if(fTreeNode.Children?.Any() ?? false)
{
Groupings = fTreeNode.Children.Select(n => new GroupingOrCipher(n)).ToArray();
}
} }
else if(!string.IsNullOrWhiteSpace(_collectionId)) else if(!string.IsNullOrWhiteSpace(_collectionId))
{ {
ciphers = await _cipherService.GetAllByCollectionAsync(_collectionId); ciphers = await _cipherService.GetAllByCollectionAsync(_collectionId);
var collections = await _collectionService.GetAllAsync();
var cGroupings = collections.Select(c => new Grouping(c, null)).OrderBy(g => g.Name).ToList();
var cTreeNodes = Helpers.GetAllNested(cGroupings);
var cTreeNode = Helpers.GetTreeNodeObject(cTreeNodes, _collectionId);
if(cTreeNode.Children?.Any() ?? false)
{
Groupings = cTreeNode.Children.Select(n => new GroupingOrCipher(n)).ToArray();
}
} }
else if(_favorites) else if(_favorites)
{ {
@ -363,11 +385,20 @@ namespace Bit.App.Pages
return cts; return cts;
} }
private void LoadSections(Cipher[] ciphers, CancellationToken ct) private void LoadSections(Cipher[] ciphers, GroupingOrCipher[] groupings, CancellationToken ct)
{ {
ct.ThrowIfCancellationRequested(); ct.ThrowIfCancellationRequested();
var sections = ciphers.GroupBy(c => c.NameGroup.ToUpperInvariant()) var sections = ciphers.GroupBy(c => c.NameGroup.ToUpperInvariant())
.Select(g => new Section<Cipher>(g.ToList(), g.Key)); .Select(g => new Section<GroupingOrCipher>(g.Select(g2 => new GroupingOrCipher(g2)).ToList(), g.Key))
.ToList();
if(groupings?.Any() ?? false)
{
sections.Insert(0, new Section<GroupingOrCipher>(groupings.ToList(),
_folder ? AppResources.Folders : AppResources.Collections));
}
ct.ThrowIfCancellationRequested(); ct.ThrowIfCancellationRequested();
Device.BeginInvokeOnMainThread(() => Device.BeginInvokeOnMainThread(() =>
{ {
@ -393,14 +424,33 @@ namespace Bit.App.Pages
}); });
} }
private async void CipherSelected(object sender, SelectedItemChangedEventArgs e) private async void GroupingOrCipherSelected(object sender, SelectedItemChangedEventArgs e)
{ {
var cipher = e.SelectedItem as Cipher; var groupingOrCipher = e.SelectedItem as GroupingOrCipher;
if(cipher == null) if(groupingOrCipher == null)
{ {
return; return;
} }
if(groupingOrCipher.Grouping != null)
{
Page page;
if(groupingOrCipher.Grouping.Node.Folder)
{
page = new VaultListCiphersPage(folder: true,
folderId: groupingOrCipher.Grouping.Node.Id, groupingName: groupingOrCipher.Grouping.Node.Name);
}
else
{
page = new VaultListCiphersPage(collectionId: groupingOrCipher.Grouping.Node.Id,
groupingName: groupingOrCipher.Grouping.Node.Name);
}
await Navigation.PushAsync(page);
}
else if(groupingOrCipher.Cipher != null)
{
var cipher = groupingOrCipher.Cipher;
string selection = null; string selection = null;
if(!string.IsNullOrWhiteSpace(_uri)) if(!string.IsNullOrWhiteSpace(_uri))
{ {
@ -464,8 +514,32 @@ namespace Bit.App.Pages
_deviceActionService.Autofill(cipher); _deviceActionService.Autofill(cipher);
} }
} }
}
((ListView)sender).SelectedItem = null; ((ListView)sender).SelectedItem = null;
} }
public class GroupingOrCipherDataTemplateSelector : DataTemplateSelector
{
public GroupingOrCipherDataTemplateSelector(VaultListCiphersPage page)
{
GroupingTemplate = new DataTemplate(() => new VaultGroupingViewCell());
CipherTemplate = new DataTemplate(() => new VaultListViewCell(
(Cipher c) => Helpers.CipherMoreClickedAsync(page, c, !string.IsNullOrWhiteSpace(page._uri)),
true));
}
public DataTemplate GroupingTemplate { get; set; }
public DataTemplate CipherTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
if(item == null)
{
return null;
}
return ((GroupingOrCipher)item).Cipher == null ? GroupingTemplate : CipherTemplate;
}
}
} }
} }