mirror of
https://github.com/bitwarden/mobile
synced 2026-01-04 09:33:16 +00:00
add/edit/delete custom fields. remove field page.
This commit is contained in:
@@ -71,9 +71,12 @@ namespace Bit.App.Pages
|
||||
|
||||
SliderCell = new SliderViewCell(this, _passwordGenerationService, _settings);
|
||||
|
||||
var buttonColor = Color.FromHex("3c8dbc");
|
||||
RegenerateCell = new ExtendedTextCell { Text = AppResources.RegeneratePassword, TextColor = buttonColor };
|
||||
CopyCell = new ExtendedTextCell { Text = AppResources.CopyPassword, TextColor = buttonColor };
|
||||
RegenerateCell = new ExtendedTextCell
|
||||
{
|
||||
Text = AppResources.RegeneratePassword,
|
||||
TextColor = Colors.Primary
|
||||
};
|
||||
CopyCell = new ExtendedTextCell { Text = AppResources.CopyPassword, TextColor = Colors.Primary };
|
||||
|
||||
UppercaseCell = new ExtendedSwitchCell
|
||||
{
|
||||
|
||||
@@ -86,12 +86,14 @@ namespace Bit.App.Pages
|
||||
public TableRoot TableRoot { get; set; }
|
||||
public TableSection TopSection { get; set; }
|
||||
public TableSection MiddleSection { get; set; }
|
||||
public TableSection FieldsSection { get; set; }
|
||||
public ExtendedTableView Table { get; set; }
|
||||
|
||||
public FormEntryCell NameCell { get; private set; }
|
||||
public FormEditorCell NotesCell { get; private set; }
|
||||
public FormPickerCell FolderCell { get; private set; }
|
||||
public ExtendedSwitchCell FavoriteCell { get; set; }
|
||||
public ExtendedTextCell AddFieldCell { get; private set; }
|
||||
|
||||
// Login
|
||||
public FormEntryCell LoginPasswordCell { get; private set; }
|
||||
@@ -184,6 +186,11 @@ namespace Bit.App.Pages
|
||||
NotesCell.InitEvents();
|
||||
FolderCell.InitEvents();
|
||||
|
||||
if(AddFieldCell != null)
|
||||
{
|
||||
AddFieldCell.Tapped += AddFieldCell_Tapped;
|
||||
}
|
||||
|
||||
switch(_type)
|
||||
{
|
||||
case CipherType.Login:
|
||||
@@ -256,6 +263,11 @@ namespace Bit.App.Pages
|
||||
NotesCell.Dispose();
|
||||
FolderCell.Dispose();
|
||||
|
||||
if(AddFieldCell != null)
|
||||
{
|
||||
AddFieldCell.Tapped -= AddFieldCell_Tapped;
|
||||
}
|
||||
|
||||
switch(_type)
|
||||
{
|
||||
case CipherType.Login:
|
||||
@@ -301,6 +313,17 @@ namespace Bit.App.Pages
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(FieldsSection != null && FieldsSection.Count > 0)
|
||||
{
|
||||
foreach(var cell in FieldsSection)
|
||||
{
|
||||
if(cell is FormEntryCell entrycell)
|
||||
{
|
||||
entrycell.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnBackButtonPressed()
|
||||
@@ -540,6 +563,14 @@ namespace Bit.App.Pages
|
||||
NameCell.NextElement = NotesCell.Editor;
|
||||
}
|
||||
|
||||
FieldsSection = new TableSection(AppResources.CustomFields);
|
||||
AddFieldCell = new ExtendedTextCell
|
||||
{
|
||||
Text = AppResources.NewCustomField,
|
||||
TextColor = Colors.Primary
|
||||
};
|
||||
FieldsSection.Add(AddFieldCell);
|
||||
|
||||
// Make table
|
||||
TableRoot = new TableRoot
|
||||
{
|
||||
@@ -548,7 +579,8 @@ namespace Bit.App.Pages
|
||||
new TableSection(AppResources.Notes)
|
||||
{
|
||||
NotesCell
|
||||
}
|
||||
},
|
||||
FieldsSection
|
||||
};
|
||||
|
||||
Table = new ExtendedTableView
|
||||
@@ -744,6 +776,8 @@ namespace Bit.App.Pages
|
||||
cipher.FolderId = Folders.ElementAt(FolderCell.Picker.SelectedIndex - 1).Id;
|
||||
}
|
||||
|
||||
Helpers.ProcessFieldsSectionForSave(FieldsSection, cipher);
|
||||
|
||||
_deviceActionService.ShowLoading(AppResources.Saving);
|
||||
var saveTask = await _cipherService.SaveAsync(cipher);
|
||||
_deviceActionService.HideLoading();
|
||||
@@ -782,6 +816,10 @@ namespace Bit.App.Pages
|
||||
|
||||
ToolbarItems.Add(saveToolBarItem);
|
||||
}
|
||||
|
||||
private async void AddFieldCell_Tapped(object sender, EventArgs e)
|
||||
{
|
||||
await Helpers.AddField(this, FieldsSection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Resources;
|
||||
using Xamarin.Forms;
|
||||
using XLabs.Ioc;
|
||||
using Bit.App.Utilities;
|
||||
using Plugin.Connectivity.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Enums;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class VaultCustomFieldsPage : ExtendedContentPage
|
||||
{
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IConnectivity _connectivity;
|
||||
private readonly IGoogleAnalyticsService _googleAnalyticsService;
|
||||
private readonly string _cipherId;
|
||||
private Cipher _cipher;
|
||||
private DateTime? _lastAction;
|
||||
|
||||
public VaultCustomFieldsPage(string cipherId)
|
||||
: base(true)
|
||||
{
|
||||
_cipherId = cipherId;
|
||||
_cipherService = Resolver.Resolve<ICipherService>();
|
||||
_connectivity = Resolver.Resolve<IConnectivity>();
|
||||
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
|
||||
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
public ToolbarItem SaveToolbarItem { get; set; }
|
||||
public ToolbarItem CloseToolbarItem { get; set; }
|
||||
public Label NoDataLabel { get; set; }
|
||||
public TableSection FieldsSection { get; set; }
|
||||
public ExtendedTableView Table { get; set; }
|
||||
|
||||
private void Init()
|
||||
{
|
||||
FieldsSection = new TableSection(Helpers.GetEmptyTableSectionTitle());
|
||||
|
||||
Table = new ExtendedTableView
|
||||
{
|
||||
Intent = TableIntent.Settings,
|
||||
EnableScrolling = true,
|
||||
HasUnevenRows = true,
|
||||
Root = new TableRoot
|
||||
{
|
||||
FieldsSection
|
||||
}
|
||||
};
|
||||
|
||||
NoDataLabel = new Label
|
||||
{
|
||||
Text = AppResources.NoCustomFields,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)),
|
||||
Margin = new Thickness(10, 40, 10, 0)
|
||||
};
|
||||
|
||||
SaveToolbarItem = new ToolbarItem(AppResources.Save, Helpers.ToolbarImage("envelope.png"), async () =>
|
||||
{
|
||||
if(_lastAction.LastActionWasRecent() || _cipher == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_lastAction = DateTime.UtcNow;
|
||||
|
||||
if(!_connectivity.IsConnected)
|
||||
{
|
||||
AlertNoConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
if(FieldsSection.Count > 0)
|
||||
{
|
||||
var fields = new List<Field>();
|
||||
foreach(var cell in FieldsSection)
|
||||
{
|
||||
if(cell is FormEntryCell entryCell)
|
||||
{
|
||||
fields.Add(new Field
|
||||
{
|
||||
Name = string.IsNullOrWhiteSpace(entryCell.Label.Text) ? null :
|
||||
entryCell.Label.Text.Encrypt(_cipher.OrganizationId),
|
||||
Value = string.IsNullOrWhiteSpace(entryCell.Entry.Text) ? null :
|
||||
entryCell.Entry.Text.Encrypt(_cipher.OrganizationId),
|
||||
Type = entryCell.Entry.IsPassword ? FieldType.Hidden : FieldType.Text
|
||||
});
|
||||
}
|
||||
else if(cell is ExtendedSwitchCell switchCell)
|
||||
{
|
||||
var value = switchCell.On ? "true" : "false";
|
||||
fields.Add(new Field
|
||||
{
|
||||
Name = string.IsNullOrWhiteSpace(switchCell.Text) ? null :
|
||||
switchCell.Text.Encrypt(_cipher.OrganizationId),
|
||||
Value = value.Encrypt(_cipher.OrganizationId),
|
||||
Type = FieldType.Boolean
|
||||
});
|
||||
}
|
||||
}
|
||||
_cipher.Fields = fields;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cipher.Fields = null;
|
||||
}
|
||||
|
||||
_deviceActionService.ShowLoading(AppResources.Saving);
|
||||
var saveTask = await _cipherService.SaveAsync(_cipher);
|
||||
_deviceActionService.HideLoading();
|
||||
|
||||
if(saveTask.Succeeded)
|
||||
{
|
||||
_deviceActionService.Toast(AppResources.CustomFieldsUpdated);
|
||||
_googleAnalyticsService.TrackAppEvent("UpdatedCustomFields");
|
||||
await Navigation.PopForDeviceAsync();
|
||||
}
|
||||
else if(saveTask.Errors.Count() > 0)
|
||||
{
|
||||
await DisplayAlert(AppResources.AnErrorHasOccurred, saveTask.Errors.First().Message, AppResources.Ok);
|
||||
}
|
||||
else
|
||||
{
|
||||
await DisplayAlert(null, AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||
}
|
||||
}, ToolbarItemOrder.Default, 0);
|
||||
ToolbarItems.Add(SaveToolbarItem);
|
||||
|
||||
Title = AppResources.CustomFields;
|
||||
Content = Table;
|
||||
|
||||
if(Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
CloseToolbarItem = new DismissModalToolBarItem(this, AppResources.Close);
|
||||
ToolbarItems.Add(CloseToolbarItem);
|
||||
|
||||
Table.RowHeight = -1;
|
||||
Table.EstimatedRowHeight = 44;
|
||||
}
|
||||
else if(Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
Table.BottomPadding = 50;
|
||||
}
|
||||
}
|
||||
|
||||
protected async override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
|
||||
_cipher = await _cipherService.GetByIdAsync(_cipherId);
|
||||
if(_cipher == null)
|
||||
{
|
||||
await Navigation.PopForDeviceAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
var hasFields = _cipher.Fields?.Any() ?? false;
|
||||
|
||||
if(hasFields)
|
||||
{
|
||||
Content = Table;
|
||||
if(CloseToolbarItem != null)
|
||||
{
|
||||
CloseToolbarItem.Text = AppResources.Cancel;
|
||||
}
|
||||
|
||||
foreach(var field in _cipher.Fields)
|
||||
{
|
||||
var label = field.Name?.Decrypt(_cipher.OrganizationId) ?? string.Empty;
|
||||
var value = field.Value?.Decrypt(_cipher.OrganizationId);
|
||||
switch(field.Type)
|
||||
{
|
||||
case FieldType.Text:
|
||||
case FieldType.Hidden:
|
||||
var hidden = field.Type == FieldType.Hidden;
|
||||
|
||||
var textFieldCell = new FormEntryCell(label, isPassword: hidden, useButton: hidden);
|
||||
textFieldCell.Entry.Text = value;
|
||||
textFieldCell.Entry.DisableAutocapitalize = true;
|
||||
textFieldCell.Entry.Autocorrect = false;
|
||||
|
||||
if(hidden)
|
||||
{
|
||||
textFieldCell.Entry.FontFamily = Helpers.OnPlatform(
|
||||
iOS: "Menlo-Regular", Android: "monospace", Windows: "Courier");
|
||||
textFieldCell.Button.Image = "eye.png";
|
||||
textFieldCell.Button.Command = new Command(() =>
|
||||
{
|
||||
textFieldCell.Entry.InvokeToggleIsPassword();
|
||||
textFieldCell.Button.Image =
|
||||
"eye" + (!textFieldCell.Entry.IsPasswordFromToggled ? "_slash" : string.Empty) + ".png";
|
||||
});
|
||||
}
|
||||
|
||||
textFieldCell.InitEvents();
|
||||
FieldsSection.Add(textFieldCell);
|
||||
break;
|
||||
case FieldType.Boolean:
|
||||
var switchFieldCell = new ExtendedSwitchCell
|
||||
{
|
||||
Text = label,
|
||||
On = value == "true"
|
||||
};
|
||||
FieldsSection.Add(switchFieldCell);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Content = NoDataLabel;
|
||||
if(ToolbarItems.Count > 0)
|
||||
{
|
||||
ToolbarItems.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDisappearing()
|
||||
{
|
||||
base.OnDisappearing();
|
||||
|
||||
if(FieldsSection != null && FieldsSection.Count > 0)
|
||||
{
|
||||
foreach(var cell in FieldsSection)
|
||||
{
|
||||
if(cell is FormEntryCell entrycell)
|
||||
{
|
||||
entrycell.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AlertNoConnection()
|
||||
{
|
||||
DisplayAlert(AppResources.InternetConnectionRequiredTitle, AppResources.InternetConnectionRequiredMessage,
|
||||
AppResources.Ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ namespace Bit.App.Pages
|
||||
public TableRoot TableRoot { get; set; }
|
||||
public TableSection TopSection { get; set; }
|
||||
public TableSection MiddleSection { get; set; }
|
||||
public TableSection FieldsSection { get; set; }
|
||||
public ExtendedTableView Table { get; set; }
|
||||
|
||||
public FormEntryCell NameCell { get; private set; }
|
||||
@@ -49,8 +50,8 @@ namespace Bit.App.Pages
|
||||
public FormPickerCell FolderCell { get; private set; }
|
||||
public ExtendedSwitchCell FavoriteCell { get; set; }
|
||||
public ExtendedTextCell AttachmentsCell { get; private set; }
|
||||
public ExtendedTextCell CustomFieldsCell { get; private set; }
|
||||
public ExtendedTextCell DeleteCell { get; private set; }
|
||||
public ExtendedTextCell AddFieldCell { get; private set; }
|
||||
|
||||
// Login
|
||||
public FormEntryCell LoginPasswordCell { get; private set; }
|
||||
@@ -152,12 +153,6 @@ namespace Bit.App.Pages
|
||||
ShowDisclousure = true
|
||||
};
|
||||
|
||||
CustomFieldsCell = new ExtendedTextCell
|
||||
{
|
||||
Text = AppResources.CustomFields,
|
||||
ShowDisclousure = true
|
||||
};
|
||||
|
||||
// Sections
|
||||
TopSection = new TableSection(AppResources.ItemInformation)
|
||||
{
|
||||
@@ -168,8 +163,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
FolderCell,
|
||||
FavoriteCell,
|
||||
AttachmentsCell,
|
||||
CustomFieldsCell
|
||||
AttachmentsCell
|
||||
};
|
||||
|
||||
// Types
|
||||
@@ -405,6 +399,27 @@ namespace Bit.App.Pages
|
||||
NameCell.NextElement = NotesCell.Editor;
|
||||
}
|
||||
|
||||
FieldsSection = new TableSection(AppResources.CustomFields);
|
||||
if(Cipher.Fields != null)
|
||||
{
|
||||
foreach(var field in Cipher.Fields)
|
||||
{
|
||||
var label = field.Name?.Decrypt(Cipher.OrganizationId) ?? string.Empty;
|
||||
var value = field.Value?.Decrypt(Cipher.OrganizationId);
|
||||
var cell = Helpers.MakeFieldCell(field.Type, label, value, FieldsSection);
|
||||
if(cell != null)
|
||||
{
|
||||
FieldsSection.Add(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
AddFieldCell = new ExtendedTextCell
|
||||
{
|
||||
Text = AppResources.NewCustomField,
|
||||
TextColor = Colors.Primary
|
||||
};
|
||||
FieldsSection.Add(AddFieldCell);
|
||||
|
||||
// Make table
|
||||
TableRoot = new TableRoot
|
||||
{
|
||||
@@ -414,6 +429,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
NotesCell
|
||||
},
|
||||
FieldsSection,
|
||||
new TableSection(Helpers.GetEmptyTableSectionTitle())
|
||||
{
|
||||
DeleteCell
|
||||
@@ -614,6 +630,8 @@ namespace Bit.App.Pages
|
||||
Cipher.FolderId = null;
|
||||
}
|
||||
|
||||
Helpers.ProcessFieldsSectionForSave(FieldsSection, Cipher);
|
||||
|
||||
_deviceActionService.ShowLoading(AppResources.Saving);
|
||||
var saveTask = await _cipherService.SaveAsync(Cipher);
|
||||
_deviceActionService.HideLoading();
|
||||
@@ -653,14 +671,14 @@ namespace Bit.App.Pages
|
||||
{
|
||||
AttachmentsCell.Tapped += AttachmentsCell_Tapped;
|
||||
}
|
||||
if(CustomFieldsCell != null)
|
||||
{
|
||||
CustomFieldsCell.Tapped += CustomFieldsCell_Tapped;
|
||||
}
|
||||
if(DeleteCell != null)
|
||||
{
|
||||
DeleteCell.Tapped += DeleteCell_Tapped;
|
||||
}
|
||||
if(AddFieldCell != null)
|
||||
{
|
||||
AddFieldCell.Tapped += AddFieldCell_Tapped;
|
||||
}
|
||||
|
||||
switch(Cipher.Type)
|
||||
{
|
||||
@@ -713,6 +731,17 @@ namespace Bit.App.Pages
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(FieldsSection != null && FieldsSection.Count > 0)
|
||||
{
|
||||
foreach(var cell in FieldsSection)
|
||||
{
|
||||
if(cell is FormEntryCell entrycell)
|
||||
{
|
||||
entrycell.InitEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDisappearing()
|
||||
@@ -727,14 +756,14 @@ namespace Bit.App.Pages
|
||||
{
|
||||
AttachmentsCell.Tapped -= AttachmentsCell_Tapped;
|
||||
}
|
||||
if(CustomFieldsCell != null)
|
||||
{
|
||||
CustomFieldsCell.Tapped -= CustomFieldsCell_Tapped;
|
||||
}
|
||||
if(DeleteCell != null)
|
||||
{
|
||||
DeleteCell.Tapped -= DeleteCell_Tapped;
|
||||
}
|
||||
if(AddFieldCell != null)
|
||||
{
|
||||
AddFieldCell.Tapped -= AddFieldCell_Tapped;
|
||||
}
|
||||
|
||||
switch(Cipher.Type)
|
||||
{
|
||||
@@ -787,6 +816,17 @@ namespace Bit.App.Pages
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(FieldsSection != null && FieldsSection.Count > 0)
|
||||
{
|
||||
foreach(var cell in FieldsSection)
|
||||
{
|
||||
if(cell is FormEntryCell entrycell)
|
||||
{
|
||||
entrycell.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PasswordButton_Clicked(object sender, EventArgs e)
|
||||
@@ -840,12 +880,6 @@ namespace Bit.App.Pages
|
||||
await Navigation.PushModalAsync(page);
|
||||
}
|
||||
|
||||
private async void CustomFieldsCell_Tapped(object sender, EventArgs e)
|
||||
{
|
||||
var page = new ExtendedNavigationPage(new VaultCustomFieldsPage(_cipherId));
|
||||
await Navigation.PushModalAsync(page);
|
||||
}
|
||||
|
||||
private async void DeleteCell_Tapped(object sender, EventArgs e)
|
||||
{
|
||||
if(!_connectivity.IsConnected)
|
||||
@@ -881,6 +915,11 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private async void AddFieldCell_Tapped(object sender, EventArgs e)
|
||||
{
|
||||
await Helpers.AddField(this, FieldsSection);
|
||||
}
|
||||
|
||||
private void AlertNoConnection()
|
||||
{
|
||||
DisplayAlert(AppResources.InternetConnectionRequiredTitle, AppResources.InternetConnectionRequiredMessage,
|
||||
|
||||
Reference in New Issue
Block a user