mirror of
https://github.com/bitwarden/mobile.git
synced 2024-12-18 15:37:42 +01:00
custom fields add/edit
This commit is contained in:
parent
49c355e52f
commit
866a20ed5a
@ -348,37 +348,35 @@
|
||||
<controls:RepeaterView ItemsSource="{Binding Uris}">
|
||||
<controls:RepeaterView.ItemTemplate>
|
||||
<DataTemplate x:DataType="views:LoginUriView">
|
||||
<StackLayout Spacing="0" Padding="0">
|
||||
<Grid StyleClass="box-row">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label
|
||||
Text="{u:I18n URI}"
|
||||
StyleClass="box-label"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0" />
|
||||
<Entry
|
||||
Text="{Binding Uri}"
|
||||
Keyboard="Url"
|
||||
StyleClass="box-value"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0" />
|
||||
<controls:FaButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text=""
|
||||
Command="{Binding BindingContext.UriOptionsCommand, Source={x:Reference _page}}"
|
||||
CommandParameter="{Binding .}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2" />
|
||||
</Grid>
|
||||
</StackLayout>
|
||||
<Grid StyleClass="box-row">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label
|
||||
Text="{u:I18n URI}"
|
||||
StyleClass="box-label"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0" />
|
||||
<Entry
|
||||
Text="{Binding Uri}"
|
||||
Keyboard="Url"
|
||||
StyleClass="box-value"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0" />
|
||||
<controls:FaButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text=""
|
||||
Command="{Binding BindingContext.UriOptionsCommand, Source={x:Reference _page}}"
|
||||
CommandParameter="{Binding .}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</controls:RepeaterView.ItemTemplate>
|
||||
</controls:RepeaterView>
|
||||
@ -397,6 +395,84 @@
|
||||
HeightRequest="200" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
<StackLayout StyleClass="box">
|
||||
<StackLayout StyleClass="box-row-header">
|
||||
<Label Text="{u:I18n CustomFields}"
|
||||
StyleClass="box-header, box-header-platform" />
|
||||
</StackLayout>
|
||||
<controls:RepeaterView ItemsSource="{Binding Fields}">
|
||||
<controls:RepeaterView.ItemTemplate>
|
||||
<DataTemplate x:DataType="pages:AddEditPageFieldViewModel">
|
||||
<StackLayout Spacing="0" Padding="0">
|
||||
<Grid StyleClass="box-row">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label
|
||||
Text="{Binding Field.Name, Mode=OneWay}"
|
||||
IsVisible="{Binding IsBooleanType, Mode=OneWay, Converter={StaticResource inverseBool}}"
|
||||
StyleClass="box-label"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0" />
|
||||
<Label
|
||||
Text="{Binding Field.Name, Mode=OneWay}"
|
||||
IsVisible="{Binding IsBooleanType, Mode=OneWay}"
|
||||
StyleClass="box-value"
|
||||
VerticalOptions="FillAndExpand"
|
||||
VerticalTextAlignment="Center"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="2" />
|
||||
<Entry
|
||||
Text="{Binding Field.Value}"
|
||||
StyleClass="box-value"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
IsVisible="{Binding IsTextType}" />
|
||||
<controls:MonoEntry
|
||||
Text="{Binding Field.Value}"
|
||||
StyleClass="box-value"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
IsVisible="{Binding IsHiddenType}"
|
||||
IsPassword="{Binding ShowHiddenValue, Converter={StaticResource inverseBool}}" />
|
||||
<Switch
|
||||
IsToggled="{Binding BooleanValue}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
IsVisible="{Binding IsBooleanType}" />
|
||||
<controls:FaButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowHiddenValueIcon}"
|
||||
Command="{Binding ToggleHiddenValueCommand}"
|
||||
IsVisible="{Binding IsHiddenType}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2" />
|
||||
<controls:FaButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text=""
|
||||
Command="{Binding BindingContext.FieldOptionsCommand, Source={x:Reference _page}}"
|
||||
CommandParameter="{Binding .}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Grid.RowSpan="2" />
|
||||
</Grid>
|
||||
<BoxView StyleClass="box-row-separator" IsVisible="{Binding IsBooleanType}" />
|
||||
</StackLayout>
|
||||
</DataTemplate>
|
||||
</controls:RepeaterView.ItemTemplate>
|
||||
</controls:RepeaterView>
|
||||
<Button Text="{u:I18n NewCustomField}" StyleClass="box-button-row"
|
||||
Clicked="NewField_Clicked"></Button>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
|
||||
|
@ -61,5 +61,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_vm.AddUri();
|
||||
}
|
||||
|
||||
private void NewField_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
_vm.AddField();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ namespace Bit.App.Pages
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private CipherView _cipher;
|
||||
private List<AddEditPageFieldViewModel> _fields;
|
||||
private bool _showPassword;
|
||||
private bool _showCardCode;
|
||||
private int _typeSelectedIndex;
|
||||
@ -47,7 +46,14 @@ namespace Bit.App.Pages
|
||||
new KeyValuePair<UriMatchType?, string>(UriMatchType.StartsWith, AppResources.StartsWith),
|
||||
new KeyValuePair<UriMatchType?, string>(UriMatchType.RegularExpression, AppResources.RegEx),
|
||||
new KeyValuePair<UriMatchType?, string>(UriMatchType.Exact, AppResources.Exact),
|
||||
new KeyValuePair<UriMatchType?, string>(UriMatchType.Never, AppResources.Never),
|
||||
new KeyValuePair<UriMatchType?, string>(UriMatchType.Never, AppResources.Never)
|
||||
};
|
||||
private List<KeyValuePair<FieldType, string>> _fieldTypeOptions =
|
||||
new List<KeyValuePair<FieldType, string>>
|
||||
{
|
||||
new KeyValuePair<FieldType, string>(FieldType.Text, AppResources.FieldTypeText),
|
||||
new KeyValuePair<FieldType, string>(FieldType.Hidden, AppResources.FieldTypeHidden),
|
||||
new KeyValuePair<FieldType, string>(FieldType.Boolean, AppResources.FieldTypeBoolean)
|
||||
};
|
||||
|
||||
public AddEditPageViewModel()
|
||||
@ -63,7 +69,9 @@ namespace Bit.App.Pages
|
||||
ToggleCardCodeCommand = new Command(ToggleCardCode);
|
||||
CheckPasswordCommand = new Command(CheckPasswordAsync);
|
||||
UriOptionsCommand = new Command<LoginUriView>(UriOptions);
|
||||
FieldOptionsCommand = new Command<AddEditPageFieldViewModel>(FieldOptions);
|
||||
Uris = new ExtendedObservableCollection<LoginUriView>();
|
||||
Fields = new ExtendedObservableCollection<AddEditPageFieldViewModel>();
|
||||
|
||||
TypeOptions = new List<KeyValuePair<string, CipherType>>
|
||||
{
|
||||
@ -116,6 +124,7 @@ namespace Bit.App.Pages
|
||||
public Command ToggleCardCodeCommand { get; set; }
|
||||
public Command CheckPasswordCommand { get; set; }
|
||||
public Command UriOptionsCommand { get; set; }
|
||||
public Command FieldOptionsCommand { get; set; }
|
||||
public string CipherId { get; set; }
|
||||
public string OrganizationId { get; set; }
|
||||
public string FolderId { get; set; }
|
||||
@ -126,6 +135,7 @@ namespace Bit.App.Pages
|
||||
public List<KeyValuePair<string, string>> CardExpMonthOptions { get; set; }
|
||||
public List<KeyValuePair<string, string>> IdentityTitleOptions { get; set; }
|
||||
public ExtendedObservableCollection<LoginUriView> Uris { get; set; }
|
||||
public ExtendedObservableCollection<AddEditPageFieldViewModel> Fields { get; set; }
|
||||
public int TypeSelectedIndex
|
||||
{
|
||||
get => _typeSelectedIndex;
|
||||
@ -167,11 +177,6 @@ namespace Bit.App.Pages
|
||||
get => _cipher;
|
||||
set => SetProperty(ref _cipher, value, additionalPropertyNames: _additionalCipherProperties);
|
||||
}
|
||||
public List<AddEditPageFieldViewModel> Fields
|
||||
{
|
||||
get => _fields;
|
||||
set => SetProperty(ref _fields, value);
|
||||
}
|
||||
public bool ShowPassword
|
||||
{
|
||||
get => _showPassword;
|
||||
@ -217,7 +222,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
var cipher = await _cipherService.GetAsync(CipherId);
|
||||
Cipher = await cipher.DecryptAsync();
|
||||
Fields = Cipher.Fields?.Select(f => new AddEditPageFieldViewModel(f)).ToList();
|
||||
|
||||
if(Cipher.Card != null)
|
||||
{
|
||||
@ -244,7 +248,7 @@ namespace Bit.App.Pages
|
||||
Identity = new IdentityView(),
|
||||
SecureNote = new SecureNoteView()
|
||||
};
|
||||
Cipher.Login.Uris = new List<LoginUriView>();
|
||||
Cipher.Login.Uris = new List<LoginUriView> { new LoginUriView() };
|
||||
Cipher.SecureNote.Type = SecureNoteType.Generic;
|
||||
|
||||
TypeSelectedIndex = TypeOptions.FindIndex(k => k.Value == Cipher.Type);
|
||||
@ -258,6 +262,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
Uris.ResetWithRange(Cipher.Login.Uris);
|
||||
}
|
||||
if(Cipher.Fields != null)
|
||||
{
|
||||
Fields.ResetWithRange(Cipher.Fields?.Select(f => new AddEditPageFieldViewModel(f)));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> SubmitAsync()
|
||||
@ -270,6 +278,7 @@ namespace Bit.App.Pages
|
||||
return false;
|
||||
}
|
||||
|
||||
Cipher.Fields = Fields.Any() ? Fields.Select(f => f.Field).ToList() : null;
|
||||
Cipher.Login.Uris = Uris.ToList();
|
||||
if(!EditMode && Cipher.Type == CipherType.Login && (Cipher.Login.Uris?.Count ?? 0) == 1 &&
|
||||
string.IsNullOrWhiteSpace(Cipher.Login.Uris.First().Uri))
|
||||
@ -371,6 +380,61 @@ namespace Bit.App.Pages
|
||||
Uris.Add(new LoginUriView());
|
||||
}
|
||||
|
||||
public async void FieldOptions(AddEditPageFieldViewModel field)
|
||||
{
|
||||
if(!(Page as AddEditPage).DoOnce())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var selection = await Page.DisplayActionSheet(AppResources.Options, AppResources.Cancel, null,
|
||||
AppResources.Edit, AppResources.MoveUp, AppResources.MoveDown, AppResources.Remove);
|
||||
if(selection == AppResources.Remove)
|
||||
{
|
||||
Fields.Remove(field);
|
||||
}
|
||||
else if(selection == AppResources.Edit)
|
||||
{
|
||||
var name = "new name";
|
||||
// TODO: prompt for name
|
||||
field.Field.Name = name;
|
||||
field.TriggerFieldChanged();
|
||||
}
|
||||
else if(selection == AppResources.MoveUp)
|
||||
{
|
||||
var currentIndex = Fields.IndexOf(field);
|
||||
if(currentIndex > 0)
|
||||
{
|
||||
Fields.Move(currentIndex, currentIndex - 1);
|
||||
}
|
||||
}
|
||||
else if(selection == AppResources.MoveDown)
|
||||
{
|
||||
var currentIndex = Fields.IndexOf(field);
|
||||
if(currentIndex < Fields.Count - 1)
|
||||
{
|
||||
Fields.Move(currentIndex, currentIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async void AddField()
|
||||
{
|
||||
var typeSelection = await Page.DisplayActionSheet(AppResources.SelectTypeField, AppResources.Cancel, null,
|
||||
_fieldTypeOptions.Select(f => f.Value).ToArray());
|
||||
if(typeSelection != null && typeSelection != AppResources.Cancel)
|
||||
{
|
||||
var name = "new field name";
|
||||
// TODO: prompt for name
|
||||
|
||||
if(Fields == null)
|
||||
{
|
||||
Fields = new ExtendedObservableCollection<AddEditPageFieldViewModel>();
|
||||
}
|
||||
var type = _fieldTypeOptions.FirstOrDefault(f => f.Value == typeSelection).Key;
|
||||
Fields.Add(new AddEditPageFieldViewModel(new FieldView { Type = type, Name = name }));
|
||||
}
|
||||
}
|
||||
|
||||
public void TogglePassword()
|
||||
{
|
||||
ShowPassword = !ShowPassword;
|
||||
@ -448,25 +512,25 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private FieldView _field;
|
||||
private bool _showHiddenValue;
|
||||
private bool _booleanValue;
|
||||
private string[] _additionalFieldProperties = new string[]
|
||||
{
|
||||
nameof(IsBooleanType),
|
||||
nameof(IsHiddenType),
|
||||
nameof(IsTextType),
|
||||
};
|
||||
|
||||
public AddEditPageFieldViewModel(FieldView field)
|
||||
{
|
||||
Field = field;
|
||||
ToggleHiddenValueCommand = new Command(ToggleHiddenValue);
|
||||
BooleanValue = IsBooleanType && field.Value == "true";
|
||||
}
|
||||
|
||||
public FieldView Field
|
||||
{
|
||||
get => _field;
|
||||
set => SetProperty(ref _field, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ValueText),
|
||||
nameof(IsBooleanType),
|
||||
nameof(IsHiddenType),
|
||||
nameof(IsTextType),
|
||||
nameof(ShowCopyButton),
|
||||
});
|
||||
set => SetProperty(ref _field, value, additionalPropertyNames: _additionalFieldProperties);
|
||||
}
|
||||
|
||||
public bool ShowHiddenValue
|
||||
@ -479,19 +543,31 @@ namespace Bit.App.Pages
|
||||
});
|
||||
}
|
||||
|
||||
public Command ToggleHiddenValueCommand { get; set; }
|
||||
public bool BooleanValue
|
||||
{
|
||||
get => _booleanValue;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _booleanValue, value);
|
||||
Field.Value = value ? "true" : "false";
|
||||
}
|
||||
}
|
||||
|
||||
public string ValueText => IsBooleanType ? (_field.Value == "true" ? "" : "") : _field.Value;
|
||||
public Command ToggleHiddenValueCommand { get; set; }
|
||||
|
||||
public string ShowHiddenValueIcon => _showHiddenValue ? "" : "";
|
||||
public bool IsTextType => _field.Type == Core.Enums.FieldType.Text;
|
||||
public bool IsBooleanType => _field.Type == Core.Enums.FieldType.Boolean;
|
||||
public bool IsHiddenType => _field.Type == Core.Enums.FieldType.Hidden;
|
||||
public bool ShowCopyButton => _field.Type != Core.Enums.FieldType.Boolean &&
|
||||
!string.IsNullOrWhiteSpace(_field.Value);
|
||||
public bool IsTextType => _field.Type == FieldType.Text;
|
||||
public bool IsBooleanType => _field.Type == FieldType.Boolean;
|
||||
public bool IsHiddenType => _field.Type == FieldType.Hidden;
|
||||
|
||||
public void ToggleHiddenValue()
|
||||
{
|
||||
ShowHiddenValue = !ShowHiddenValue;
|
||||
}
|
||||
|
||||
public void TriggerFieldChanged()
|
||||
{
|
||||
TriggerPropertyChanged(nameof(Field), _additionalFieldProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
src/App/Resources/AppResources.Designer.cs
generated
18
src/App/Resources/AppResources.Designer.cs
generated
@ -2292,6 +2292,24 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Move Down.
|
||||
/// </summary>
|
||||
public static string MoveDown {
|
||||
get {
|
||||
return ResourceManager.GetString("MoveDown", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Move Up.
|
||||
/// </summary>
|
||||
public static string MoveUp {
|
||||
get {
|
||||
return ResourceManager.GetString("MoveUp", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Mr.
|
||||
/// </summary>
|
||||
|
@ -1411,4 +1411,10 @@
|
||||
<data name="Type" xml:space="preserve">
|
||||
<value>Type</value>
|
||||
</data>
|
||||
<data name="MoveDown" xml:space="preserve">
|
||||
<value>Move Down</value>
|
||||
</data>
|
||||
<data name="MoveUp" xml:space="preserve">
|
||||
<value>Move Up</value>
|
||||
</data>
|
||||
</root>
|
Loading…
Reference in New Issue
Block a user