vault groupings page list view stubbed

This commit is contained in:
Kyle Spearrin 2019-03-29 12:52:57 -04:00
parent d84eece715
commit 69ac98b2f6
9 changed files with 278 additions and 43 deletions

View file

@ -1,38 +1,15 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Bit.Core.Utilities;
namespace Bit.App.Pages
{
public abstract class BaseViewModel : INotifyPropertyChanged
public abstract class BaseViewModel : ExtendedViewModel
{
private string _pageTitle = string.Empty;
public event PropertyChangedEventHandler PropertyChanged;
public string PageTitle
{
get => _pageTitle;
set => SetProperty(ref _pageTitle, value);
}
protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if(EqualityComparer<T>.Default.Equals(backingStore, value))
{
return false;
}
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View file

@ -6,27 +6,72 @@
xmlns:pages="clr-namespace:Bit.App.Pages"
x:DataType="pages:VaultGroupingsPageViewModel"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
<pages:VaultGroupingsPageViewModel />
</ContentPage.BindingContext>
<StackLayout>
<!-- Place new controls here -->
<Label
Text="Vault Groupings!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Label
Text="&#xf2b9;"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand">
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate
x:Key="cipherTemplate"
x:DataType="pages:VaultGroupingsPageListItem">
<ViewCell>
<StackLayout Padding="10">
<Label
Text="{Binding Cipher.Name}"
LineBreakMode="NoWrap"
FontSize="16" />
</StackLayout>
</ViewCell>
</DataTemplate>
<Label.FontFamily>
<OnPlatform x:TypeArguments="x:String"
Android="FontAwesome.ttf#FontAwesome"
iOS="FontAwesome" />
</Label.FontFamily>
</Label>
<DataTemplate
x:Key="folderTemplate"
x:DataType="pages:VaultGroupingsPageListItem">
<ViewCell>
<StackLayout Padding="10">
<Label
Text="{Binding Folder.Name}"
LineBreakMode="NoWrap"
FontSize="16" />
</StackLayout>
</ViewCell>
</DataTemplate>
<DataTemplate
x:Key="collectionTemplate"
x:DataType="pages:VaultGroupingsPageListItem">
<ViewCell>
<StackLayout Padding="10">
<Label
Text="{Binding Collection.Name}"
LineBreakMode="NoWrap"
FontSize="16" />
</StackLayout>
</ViewCell>
</DataTemplate>
<pages:ListItemDataTemplateSelector
x:Key="listItemDataTemplateSelector"
CipherTemplate="{StaticResource cipherTemplate}"
FolderTemplate="{StaticResource folderTemplate}"
CollectionTemplate="{StaticResource collectionTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<ListView
x:Name="ItemsListView"
ItemsSource="{Binding Items}"
VerticalOptions="FillAndExpand"
HasUnevenRows="true"
RefreshCommand="{Binding LoadCommand}"
IsPullToRefreshEnabled="true"
IsRefreshing="{Binding Loading, Mode=OneWay}"
CachingStrategy="RecycleElement"
ItemTemplate="{StaticResource listItemDataTemplateSelector}">
</ListView>
</StackLayout>
</ContentPage>

View file

@ -10,9 +10,42 @@ namespace Bit.App.Pages
{
public partial class VaultGroupingsPage : ContentPage
{
private VaultGroupingsPageViewModel _viewModel;
public VaultGroupingsPage()
{
InitializeComponent();
_viewModel = BindingContext as VaultGroupingsPageViewModel;
}
protected async override void OnAppearing()
{
base.OnAppearing();
await _viewModel.LoadAsync();
}
}
public class ListItemDataTemplateSelector : DataTemplateSelector
{
public DataTemplate CipherTemplate { get; set; }
public DataTemplate FolderTemplate { get; set; }
public DataTemplate CollectionTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
if(item is VaultGroupingsPageListItem listItem)
{
if(listItem.Collection != null)
{
return CollectionTemplate;
}
else if(listItem.Folder != null)
{
return FolderTemplate;
}
return CipherTemplate;
}
return null;
}
}
}

View file

@ -1,14 +1,93 @@
using System;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class VaultGroupingsPageViewModel : BaseViewModel
{
private bool _loading = false;
public VaultGroupingsPageViewModel()
{
PageTitle = "My Vault";
Items = new ExtendedObservableCollection<VaultGroupingsPageListItem>();
LoadCommand = new Command(async () => await LoadAsync());
}
public bool Loading
{
get => _loading;
set => SetProperty(ref _loading, value);
}
public ExtendedObservableCollection<VaultGroupingsPageListItem> Items { get; set; }
public Command LoadCommand { get; set; }
public Task LoadAsync()
{
if(Loading)
{
return Task.FromResult(0);
}
Loading = true;
try
{
Items.ResetWithRange(new List<VaultGroupingsPageListItem>
{
new VaultGroupingsPageListItem
{
Cipher = new CipherView { Name = "Cipher 1" }
},
new VaultGroupingsPageListItem
{
Cipher = new CipherView { Name = "Cipher 2" }
},
new VaultGroupingsPageListItem
{
Cipher = new CipherView { Name = "Cipher 3" }
},
new VaultGroupingsPageListItem
{
Cipher = new CipherView { Name = "Cipher 4" }
},
new VaultGroupingsPageListItem
{
Folder = new FolderView { Name = "Folder 1" }
},
new VaultGroupingsPageListItem
{
Folder = new FolderView { Name = "Folder 2" }
},
new VaultGroupingsPageListItem
{
Folder = new FolderView { Name = "Folder 3" }
},
new VaultGroupingsPageListItem
{
Collection = new Core.Models.View.CollectionView { Name = "Collection 1" }
},
new VaultGroupingsPageListItem
{
Collection = new Core.Models.View.CollectionView { Name = "Collection 2" }
},
});
}
finally
{
Loading = false;
}
return Task.FromResult(0);
}
}
public class VaultGroupingsPageListItem
{
public FolderView Folder { get; set; }
public Core.Models.View.CollectionView Collection { get; set; }
public CipherView Cipher { get; set; }
}
}

View file

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Bit.Core.Models.View
{
public class CipherView
{
public string Name { get; set; }
public string Subtitle { get; set; }
}
}

View file

@ -0,0 +1,10 @@
using System;
namespace Bit.Core.Models.View
{
public class CollectionView
{
public Guid OrganizationId { get; set; }
public string Name { get; set; }
}
}

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Bit.Core.Models.View
{
public class FolderView
{
public string Name { get; set; }
}
}

View file

@ -0,0 +1,37 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
namespace Bit.Core.Utilities
{
public class ExtendedObservableCollection<T> : ObservableCollection<T>
{
public ExtendedObservableCollection()
: base() { }
public ExtendedObservableCollection(IEnumerable<T> collection)
: base(collection) { }
public ExtendedObservableCollection(List<T> list)
: base(list) { }
public void AddRange(IEnumerable<T> range)
{
foreach(var item in range)
{
Items.Add(item);
}
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void ResetWithRange(IEnumerable<T> range)
{
Items.Clear();
AddRange(range);
}
}
}

View file

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Bit.Core.Utilities
{
public abstract class ExtendedViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if(EqualityComparer<T>.Default.Equals(backingStore, value))
{
return false;
}
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}