Wired up view page functionality. Expanded LabeledValueCell. Created custom group template for vault list page.

This commit is contained in:
Kyle Spearrin 2016-05-14 01:34:42 -04:00
parent d288116b39
commit 4e906f9370
12 changed files with 155 additions and 49 deletions

View file

@ -11,6 +11,9 @@ namespace Bit.App.Controls
public static readonly BindableProperty EnableSelectionProperty = public static readonly BindableProperty EnableSelectionProperty =
BindableProperty.Create(nameof(EnableSelection), typeof(bool), typeof(ExtendedTableView), true); BindableProperty.Create(nameof(EnableSelection), typeof(bool), typeof(ExtendedTableView), true);
public static readonly BindableProperty SeparatorColorProperty =
BindableProperty.Create(nameof(SeparatorColor), typeof(Color), typeof(ExtendedTableView), Color.FromHex("d2d6de"));
public bool EnableScrolling public bool EnableScrolling
{ {
get { return (bool)GetValue(EnableScrollingProperty); } get { return (bool)GetValue(EnableScrollingProperty); }
@ -23,6 +26,12 @@ namespace Bit.App.Controls
set { SetValue(EnableSelectionProperty, value); } set { SetValue(EnableSelectionProperty, value); }
} }
public Color SeparatorColor
{
get { return (Color)GetValue(SeparatorColorProperty); }
set { SetValue(SeparatorColorProperty, value); }
}
public int EstimatedRowHeight { get; set; } public int EstimatedRowHeight { get; set; }
} }
} }

View file

@ -9,20 +9,13 @@ namespace Bit.App.Controls
{ {
public class LabeledValueCell : ViewCell public class LabeledValueCell : ViewCell
{ {
private readonly IUserDialogs _userDialogs;
private readonly IClipboardService _clipboardService;
public LabeledValueCell( public LabeledValueCell(
string labelText, string labelText = null,
string valueText = null, string valueText = null,
bool copyValue = false, string button1Text = null,
bool password = false, string button2Text = null)
bool launch = false)
{ {
_userDialogs = Resolver.Resolve<IUserDialogs>(); StackLayout = new StackLayout
_clipboardService = Resolver.Resolve<IClipboardService>();
var stackLayout = new StackLayout
{ {
Padding = new Thickness(15, 15, 15, 0), Padding = new Thickness(15, 15, 15, 0),
BackgroundColor = Color.White BackgroundColor = Color.White
@ -37,7 +30,7 @@ namespace Bit.App.Controls
TextColor = Color.FromHex("777777") TextColor = Color.FromHex("777777")
}; };
stackLayout.Children.Add(Label); StackLayout.Children.Add(Label);
} }
Value = new Label Value = new Label
@ -52,46 +45,42 @@ namespace Bit.App.Controls
{ {
Orientation = StackOrientation.Horizontal Orientation = StackOrientation.Horizontal
}; };
valueStackLayout.Children.Add(Value); valueStackLayout.Children.Add(Value);
if(copyValue) if(button1Text != null)
{ {
var copyButton = new Button Button1 = new Button
{ {
Text = AppResources.Copy, Text = button1Text,
HorizontalOptions = LayoutOptions.End, HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center
Command = new Command(() => Copy())
}; };
valueStackLayout.Children.Add(copyButton); valueStackLayout.Children.Add(Button1);
} }
if(launch) if(button2Text != null)
{ {
var launchButton = new Button Button2 = new Button
{ {
Text = AppResources.Launch, Text = button2Text,
HorizontalOptions = LayoutOptions.End, HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center
Command = new Command(() => Device.OpenUri(new Uri(Value.Text)))
}; };
valueStackLayout.Children.Add(launchButton); valueStackLayout.Children.Add(Button2);
} }
stackLayout.Children.Add(valueStackLayout); StackLayout.Children.Add(valueStackLayout);
View = stackLayout; View = StackLayout;
} }
public StackLayout StackLayout { get; private set; }
public Label Label { get; private set; } public Label Label { get; private set; }
public Label Value { get; private set; } public Label Value { get; private set; }
public Button Button1 { get; private set; }
private void Copy() public Button Button2 { get; private set; }
{
_clipboardService.CopyToClipboard(Value.Text);
_userDialogs.SuccessToast(string.Format(AppResources.ValueHasBeenCopied, Label.Text));
}
} }
} }

View file

@ -27,6 +27,7 @@ namespace Bit.App.Models.Page
PropertyChanged(this, new PropertyChangedEventArgs(nameof(PageTitle))); PropertyChanged(this, new PropertyChangedEventArgs(nameof(PageTitle)));
} }
} }
public string PageTitle => Name ?? AppResources.SiteNoName;
public string Username public string Username
{ {
get { return _username; } get { return _username; }
@ -34,8 +35,10 @@ namespace Bit.App.Models.Page
{ {
_username = value; _username = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Username))); PropertyChanged(this, new PropertyChangedEventArgs(nameof(Username)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowUsername)));
} }
} }
public bool ShowUsername => !string.IsNullOrWhiteSpace(Username);
public string Password public string Password
{ {
get { return _password; } get { return _password; }
@ -53,8 +56,31 @@ namespace Bit.App.Models.Page
{ {
_uri = value; _uri = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Uri))); PropertyChanged(this, new PropertyChangedEventArgs(nameof(Uri)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(UriHost)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowUri)));
} }
} }
public string UriHost
{
get
{
if(ShowUri)
{
try
{
return new Uri(Uri).Host;
}
catch
{
return Uri;
}
}
return null;
}
}
public bool ShowUri => !string.IsNullOrWhiteSpace(Uri);
public string Notes public string Notes
{ {
get { return _notes; } get { return _notes; }
@ -65,7 +91,7 @@ namespace Bit.App.Models.Page
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowNotes))); PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowNotes)));
} }
} }
public string PageTitle => Name ?? AppResources.SiteNoName; public bool ShowNotes => !string.IsNullOrWhiteSpace(Notes);
public bool ShowPassword public bool ShowPassword
{ {
get { return _showPassword; } get { return _showPassword; }
@ -79,7 +105,6 @@ namespace Bit.App.Models.Page
} }
public string MaskedPassword => ShowPassword ? Password : Password == null ? null : new string('●', Password.Length); public string MaskedPassword => ShowPassword ? Password : Password == null ? null : new string('●', Password.Length);
public string ShowHideText => ShowPassword ? AppResources.Hide : AppResources.Show; public string ShowHideText => ShowPassword ? AppResources.Hide : AppResources.Show;
public bool ShowNotes => !string.IsNullOrWhiteSpace(Notes);
public void Update(Site site) public void Update(Site site)
{ {

View file

@ -44,7 +44,7 @@ namespace Bit.App.Pages
} }
var folderCell = new FormPickerCell(AppResources.Folder, folderOptions.ToArray()); var folderCell = new FormPickerCell(AppResources.Folder, folderOptions.ToArray());
var notesCell = new FormEditorCell(height:90); var notesCell = new FormEditorCell(height: 90);
var mainTable = new ExtendedTableView var mainTable = new ExtendedTableView
{ {
@ -89,9 +89,9 @@ namespace Bit.App.Pages
return; return;
} }
if(string.IsNullOrWhiteSpace(uriCell.Entry.Text)) if(string.IsNullOrWhiteSpace(passwordCell.Entry.Text))
{ {
await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.URI), AppResources.Ok); await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.Password), AppResources.Ok);
return; return;
} }
@ -127,7 +127,10 @@ namespace Bit.App.Pages
Title = AppResources.AddSite; Title = AppResources.AddSite;
Content = scrollView; Content = scrollView;
ToolbarItems.Add(saveToolBarItem); ToolbarItems.Add(saveToolBarItem);
ToolbarItems.Add(new DismissModalToolBarItem(this, "Cancel")); if(Device.OS == TargetPlatform.iOS)
{
ToolbarItems.Add(new DismissModalToolBarItem(this, "Cancel"));
}
if(!_connectivity.IsConnected) if(!_connectivity.IsConnected)
{ {

View file

@ -111,9 +111,9 @@ namespace Bit.App.Pages
return; return;
} }
if(string.IsNullOrWhiteSpace(uriCell.Entry.Text)) if(string.IsNullOrWhiteSpace(passwordCell.Entry.Text))
{ {
await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.URI), AppResources.Ok); await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.Password), AppResources.Ok);
return; return;
} }
@ -150,7 +150,10 @@ namespace Bit.App.Pages
Title = "Edit Site"; Title = "Edit Site";
Content = scrollView; Content = scrollView;
ToolbarItems.Add(saveToolBarItem); ToolbarItems.Add(saveToolBarItem);
ToolbarItems.Add(new DismissModalToolBarItem(this, "Cancel")); if(Device.OS == TargetPlatform.iOS)
{
ToolbarItems.Add(new DismissModalToolBarItem(this, "Cancel"));
}
if(!_connectivity.IsConnected) if(!_connectivity.IsConnected)
{ {

View file

@ -35,8 +35,15 @@ namespace Bit.App.Pages
{ {
ToolbarItems.Add(new AddSiteToolBarItem(this)); ToolbarItems.Add(new AddSiteToolBarItem(this));
var listView = new ListView { IsGroupingEnabled = true, ItemsSource = Folders }; var listView = new ListView
{
IsGroupingEnabled = true,
ItemsSource = Folders,
HasUnevenRows = true,
SeparatorColor = Color.FromHex("E0E0E0")
};
listView.GroupDisplayBinding = new Binding("Name"); listView.GroupDisplayBinding = new Binding("Name");
listView.GroupHeaderTemplate = new DataTemplate(() => new VaultListHeaderViewCell(this));
listView.ItemSelected += SiteSelected; listView.ItemSelected += SiteSelected;
listView.ItemTemplate = new DataTemplate(() => new VaultListViewCell(this)); listView.ItemTemplate = new DataTemplate(() => new VaultListViewCell(this));
@ -171,6 +178,53 @@ namespace Bit.App.Pages
this.SetBinding<VaultListPageModel.Site>(DetailProperty, s => s.Username); this.SetBinding<VaultListPageModel.Site>(DetailProperty, s => s.Username);
ContextActions.Add(moreAction); ContextActions.Add(moreAction);
ContextActions.Add(deleteAction); ContextActions.Add(deleteAction);
TextColor = Color.FromHex("333333");
DetailColor = Color.FromHex("777777");
}
}
private class VaultListHeaderViewCell : ViewCell
{
public VaultListHeaderViewCell(VaultListPage page)
{
var image = new Image
{
Source = ImageSource.FromFile("fa-folder-open.png"),
Margin = new Thickness(16, 8, 0, 8)
};
var label = new Label
{
FontSize = 14,
TextColor = Color.FromHex("777777"),
VerticalTextAlignment = TextAlignment.Center
};
label.SetBinding<VaultListPageModel.Folder>(Label.TextProperty, s => s.Name);
var stackLayout = new StackLayout
{
Orientation = StackOrientation.Horizontal,
BackgroundColor = Color.FromHex("ecf0f5")
};
stackLayout.Children.Add(image);
stackLayout.Children.Add(label);
var borderStackLayout = new StackLayout
{
Spacing = 0
};
var borderBoxTop = new BoxView { BackgroundColor = Color.FromHex("d2d6de"), HeightRequest = 0.5 };
var borderBoxBottom = new BoxView { BackgroundColor = Color.FromHex("d2d6de"), HeightRequest = 0.5 };
borderStackLayout.Children.Add(borderBoxTop);
borderStackLayout.Children.Add(stackLayout);
borderStackLayout.Children.Add(borderBoxBottom);
View = borderStackLayout;
Height = 30;
} }
} }
} }

View file

@ -32,26 +32,34 @@ namespace Bit.App.Pages
private void Init() private void Init()
{ {
ToolbarItems.Add(new EditSiteToolBarItem(this, _siteId)); ToolbarItems.Add(new EditSiteToolBarItem(this, _siteId));
ToolbarItems.Add(new DismissModalToolBarItem(this)); if(Device.OS == TargetPlatform.iOS)
{
ToolbarItems.Add(new DismissModalToolBarItem(this));
}
// Username // Username
var nameCell = new LabeledValueCell(AppResources.Name); var nameCell = new LabeledValueCell(AppResources.Name);
nameCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Name); nameCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Name);
// Username // Username
var usernameCell = new LabeledValueCell(AppResources.Username, copyValue: true); var usernameCell = new LabeledValueCell(AppResources.Username, button1Text: AppResources.Copy);
usernameCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Username); usernameCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Username);
usernameCell.Button1.Command = new Command(() => Copy(Model.Username, AppResources.Username));
// Password // Password
var passwordCell = new LabeledValueCell(AppResources.Password, copyValue: true); var passwordCell = new LabeledValueCell(AppResources.Password, button1Text: AppResources.Show, button2Text: AppResources.Copy);
passwordCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Password); passwordCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.MaskedPassword);
passwordCell.Button1.SetBinding<VaultViewSitePageModel>(Button.TextProperty, s => s.ShowHideText);
passwordCell.Button1.Command = new Command(() => Model.ShowPassword = !Model.ShowPassword);
passwordCell.Button2.Command = new Command(() => Copy(Model.Password, AppResources.Password));
// URI // URI
var uriCell = new LabeledValueCell(AppResources.URI, launch: true); var uriCell = new LabeledValueCell(AppResources.Website, button1Text: AppResources.Launch);
uriCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Uri); uriCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.UriHost);
uriCell.Button1.Command = new Command(() => Device.OpenUri(new Uri(uriCell.Value.Text)));
// Notes // Notes
var notesCell = new LabeledValueCell(AppResources.Notes); var notesCell = new LabeledValueCell();
notesCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Notes); notesCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Notes);
Table = new ExtendedTableView Table = new ExtendedTableView

View file

@ -23,6 +23,7 @@ namespace Bit.iOS.Controls
SetSelection(view); SetSelection(view);
UpdateRowHeight(view); UpdateRowHeight(view);
UpdateEstimatedRowHeight(view); UpdateEstimatedRowHeight(view);
UpdateSeparatorColor(view);
} }
} }
@ -87,5 +88,10 @@ namespace Bit.iOS.Controls
Control.EstimatedRowHeight = 0; Control.EstimatedRowHeight = 0;
} }
} }
private void UpdateSeparatorColor(ExtendedTableView view)
{
Control.SeparatorColor = view.SeparatorColor.ToUIColor(UIColor.Gray);
}
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

View file

@ -330,6 +330,15 @@
<ItemGroup> <ItemGroup>
<ITunesArtwork Include="Resources\iTunesArtwork%402x.png" /> <ITunesArtwork Include="Resources\iTunesArtwork%402x.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\fa-folder-open.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\fa-folder-open%402x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\fa-folder-open%403x.png" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup> <PropertyGroup>