diff --git a/src/App/Models/Page/VaultViewCipherPageModel.cs b/src/App/Models/Page/VaultViewCipherPageModel.cs index bd96b3afe..445ef31fe 100644 --- a/src/App/Models/Page/VaultViewCipherPageModel.cs +++ b/src/App/Models/Page/VaultViewCipherPageModel.cs @@ -3,7 +3,7 @@ using System.ComponentModel; using Xamarin.Forms; using System.Collections.Generic; using Bit.App.Enums; -using System.Linq; +using Bit.App.Resources; namespace Bit.App.Models.Page { @@ -12,6 +12,7 @@ namespace Bit.App.Models.Page private string _name, _notes; private List _attachments; private List _fields; + private List _loginUris; // Login private string _loginUsername, _loginPassword, _loginUri, _loginTotpCode; @@ -117,69 +118,17 @@ namespace Bit.App.Models.Page public ImageSource LoginShowHideImage => RevealLoginPassword ? ImageSource.FromFile("eye_slash.png") : ImageSource.FromFile("eye.png"); - public string LoginUri + public List LoginUris { - get => _loginUri; + get => _loginUris; set { - _loginUri = value; - PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginUri))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginUriHost))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowLoginUri))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowLoginLaunch))); - } - } - public bool ShowLoginUri => !string.IsNullOrWhiteSpace(LoginUri); - public bool ShowLoginLaunch - { - get - { - if(!ShowLoginUri) - { - return false; - } - - if(Device.RuntimePlatform == Device.Android && !LoginUri.StartsWith("http") && - !LoginUri.StartsWith("androidapp://")) - { - return false; - } - - if(Device.RuntimePlatform != Device.Android && !LoginUri.StartsWith("http")) - { - return false; - } - - if(!Uri.TryCreate(LoginUri, UriKind.Absolute, out Uri uri)) - { - return false; - } - - return true; - } - } - public string LoginUriHost - { - get - { - if(!ShowLoginUri) - { - return null; - } - - if(!Uri.TryCreate(LoginUri, UriKind.Absolute, out Uri uri)) - { - return LoginUri; - } - - if(DomainName.TryParseBaseDomain(uri.Host, out string domain)) - { - return domain; - } - - return uri.Host; + _loginUris = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginUris))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowLoginUris))); } } + public bool ShowLoginUris => (LoginUris?.Count ?? 0) > 0; public string LoginTotpCode { @@ -601,7 +550,23 @@ namespace Bit.App.Models.Page case CipherType.Login: LoginUsername = cipher.Login.Username?.Decrypt(cipher.OrganizationId); LoginPassword = cipher.Login.Password?.Decrypt(cipher.OrganizationId); - LoginUri = cipher.Login.Uris?.FirstOrDefault()?.Uri?.Decrypt(cipher.OrganizationId); + + if(cipher.Login.Uris != null) + { + var uris = new List(); + foreach(var uri in cipher.Login.Uris) + { + uris.Add(new LoginUri + { + Value = uri.Uri?.Decrypt(cipher.OrganizationId) + }); + } + LoginUris = uris; + } + else + { + LoginUris = null; + } break; case CipherType.Card: CardName = cipher.Card.CardholderName?.Decrypt(cipher.OrganizationId); @@ -666,5 +631,62 @@ namespace Bit.App.Models.Page public FieldType Type { get; set; } public bool Revealed { get; set; } } + + public class LoginUri + { + public string Value { get; set; } + public bool ShowLaunch + { + get + { + if(string.IsNullOrWhiteSpace(Value)) + { + return false; + } + + if(Device.RuntimePlatform == Device.Android && !IsWebsite && !IsApp) + { + return false; + } + + if(Device.RuntimePlatform != Device.Android && !IsWebsite) + { + return false; + } + + if(!Uri.TryCreate(Value, UriKind.Absolute, out Uri uri)) + { + return false; + } + + return true; + } + } + public string Host + { + get + { + if(string.IsNullOrWhiteSpace(Value)) + { + return null; + } + + if(!Uri.TryCreate(Value, UriKind.Absolute, out Uri uri)) + { + return Value; + } + + if(DomainName.TryParseBaseDomain(uri.Host, out string domain)) + { + return domain; + } + + return uri.Host; + } + } + public string Label => IsWebsite ? AppResources.Website : AppResources.URI; + public bool IsWebsite => Value.StartsWith("http://") || Value.StartsWith("https://"); + public bool IsApp => Value.StartsWith(Constants.AndroidAppProtocol); + } } } diff --git a/src/App/Pages/Vault/VaultAddCipherPage.cs b/src/App/Pages/Vault/VaultAddCipherPage.cs index 39e33af50..169a5feb9 100644 --- a/src/App/Pages/Vault/VaultAddCipherPage.cs +++ b/src/App/Pages/Vault/VaultAddCipherPage.cs @@ -85,6 +85,7 @@ namespace Bit.App.Pages public List Folders { get; set; } public TableRoot TableRoot { get; set; } public TableSection TopSection { get; set; } + public TableSection UrisSection { get; set; } public TableSection MiddleSection { get; set; } public TableSection FieldsSection { get; set; } public ExtendedTableView Table { get; set; } @@ -94,11 +95,11 @@ namespace Bit.App.Pages public FormPickerCell FolderCell { get; private set; } public ExtendedSwitchCell FavoriteCell { get; set; } public ExtendedTextCell AddFieldCell { get; private set; } + public ExtendedTextCell AddUriCell { get; private set; } // Login public FormEntryCell LoginPasswordCell { get; private set; } public FormEntryCell LoginUsernameCell { get; private set; } - public FormEntryCell LoginUriCell { get; private set; } public FormEntryCell LoginTotpCell { get; private set; } public ExtendedTextCell LoginGenerateCell { get; private set; } @@ -190,13 +191,16 @@ namespace Bit.App.Pages { AddFieldCell.Tapped += AddFieldCell_Tapped; } + if(AddUriCell != null) + { + AddUriCell.Tapped += AddUriCell_Tapped; + } switch(_type) { case CipherType.Login: LoginPasswordCell.InitEvents(); LoginUsernameCell.InitEvents(); - LoginUriCell.InitEvents(); LoginTotpCell.InitEvents(); LoginPasswordCell.Button.Clicked += PasswordButton_Clicked; LoginGenerateCell.Tapped += GenerateCell_Tapped; @@ -237,6 +241,9 @@ namespace Bit.App.Pages break; } + Helpers.InitSectionEvents(FieldsSection); + Helpers.InitSectionEvents(UrisSection); + if(_type == CipherType.Login && !_fromAutofill && !_settings.GetValueOrDefault(AddedLoginAlertKey, false)) { _settings.AddOrUpdateValue(AddedLoginAlertKey, true); @@ -267,6 +274,10 @@ namespace Bit.App.Pages { AddFieldCell.Tapped -= AddFieldCell_Tapped; } + if(AddUriCell != null) + { + AddUriCell.Tapped -= AddUriCell_Tapped; + } switch(_type) { @@ -274,7 +285,6 @@ namespace Bit.App.Pages LoginTotpCell.Dispose(); LoginPasswordCell.Dispose(); LoginUsernameCell.Dispose(); - LoginUriCell.Dispose(); LoginPasswordCell.Button.Clicked -= PasswordButton_Clicked; LoginGenerateCell.Tapped -= GenerateCell_Tapped; if(LoginTotpCell?.Button != null) @@ -314,16 +324,8 @@ namespace Bit.App.Pages break; } - if(FieldsSection != null && FieldsSection.Count > 0) - { - foreach(var cell in FieldsSection) - { - if(cell is FormEntryCell entrycell) - { - entrycell.Dispose(); - } - } - } + Helpers.DisposeSectionEvents(FieldsSection); + Helpers.DisposeSectionEvents(UrisSection); } protected override bool OnBackButtonPressed() @@ -398,8 +400,7 @@ namespace Bit.App.Pages if(_type == CipherType.Login) { - LoginTotpCell = new FormEntryCell(AppResources.AuthenticatorKey, nextElement: NotesCell.Editor, - useButton: _deviceInfo.HasCamera); + LoginTotpCell = new FormEntryCell(AppResources.AuthenticatorKey, useButton: _deviceInfo.HasCamera); if(_deviceInfo.HasCamera) { LoginTotpCell.Button.Image = "camera.png"; @@ -435,20 +436,23 @@ namespace Bit.App.Pages LoginUsernameCell.Entry.Text = _defaultUsername; } - LoginUriCell = new FormEntryCell(AppResources.URI, Keyboard.Url, nextElement: LoginUsernameCell.Entry); - if(!string.IsNullOrWhiteSpace(_defaultUri)) - { - LoginUriCell.Entry.Text = _defaultUri; - } - - NameCell.NextElement = LoginUriCell.Entry; + NameCell.NextElement = LoginUsernameCell.Entry; // Build sections - TopSection.Add(LoginUriCell); TopSection.Add(LoginUsernameCell); TopSection.Add(LoginPasswordCell); TopSection.Add(LoginGenerateCell); - MiddleSection.Insert(0, LoginTotpCell); + TopSection.Add(LoginTotpCell); + + // Uris + UrisSection = new TableSection(Helpers.GetEmptyTableSectionTitle()); + AddUriCell = new ExtendedTextCell + { + Text = AppResources.NewUri, + TextColor = Colors.Primary + }; + UrisSection.Add(AddUriCell); + UrisSection.Insert(0, Helpers.MakeUriCell(string.Empty, UrisSection)); } else if(_type == CipherType.Card) { @@ -583,6 +587,11 @@ namespace Bit.App.Pages FieldsSection }; + if(UrisSection != null) + { + TableRoot.Insert(1, UrisSection); + } + Table = new ExtendedTableView { Intent = TableIntent.Settings, @@ -638,8 +647,6 @@ namespace Bit.App.Pages case CipherType.Login: cipher.Login = new Login { - Uri = string.IsNullOrWhiteSpace(LoginUriCell.Entry.Text) ? null : - LoginUriCell.Entry.Text.Encrypt(), Username = string.IsNullOrWhiteSpace(LoginUsernameCell.Entry.Text) ? null : LoginUsernameCell.Entry.Text.Encrypt(), Password = string.IsNullOrWhiteSpace(LoginPasswordCell.Entry.Text) ? null : @@ -647,6 +654,8 @@ namespace Bit.App.Pages Totp = string.IsNullOrWhiteSpace(LoginTotpCell.Entry.Text) ? null : LoginTotpCell.Entry.Text.Encrypt(), }; + + Helpers.ProcessUrisSectionForSave(UrisSection, cipher); break; case CipherType.SecureNote: cipher.SecureNote = new SecureNote @@ -821,5 +830,15 @@ namespace Bit.App.Pages { await Helpers.AddField(this, FieldsSection); } + + private void AddUriCell_Tapped(object sender, EventArgs e) + { + var cell = Helpers.MakeUriCell(string.Empty, UrisSection); + if(cell != null) + { + UrisSection.Insert(UrisSection.Count - 1, cell); + cell.InitEvents(); + } + } } } diff --git a/src/App/Pages/Vault/VaultEditCipherPage.cs b/src/App/Pages/Vault/VaultEditCipherPage.cs index 1504bc0d2..203de6904 100644 --- a/src/App/Pages/Vault/VaultEditCipherPage.cs +++ b/src/App/Pages/Vault/VaultEditCipherPage.cs @@ -41,6 +41,7 @@ namespace Bit.App.Pages public List Folders { get; set; } public TableRoot TableRoot { get; set; } public TableSection TopSection { get; set; } + public TableSection UrisSection { get; set; } public TableSection MiddleSection { get; set; } public TableSection FieldsSection { get; set; } public ExtendedTableView Table { get; set; } @@ -52,11 +53,11 @@ namespace Bit.App.Pages public ExtendedTextCell AttachmentsCell { get; private set; } public ExtendedTextCell DeleteCell { get; private set; } public ExtendedTextCell AddFieldCell { get; private set; } + public ExtendedTextCell AddUriCell { get; private set; } // Login public FormEntryCell LoginPasswordCell { get; private set; } public FormEntryCell LoginUsernameCell { get; private set; } - public FormEntryCell LoginUriCell { get; private set; } public FormEntryCell LoginTotpCell { get; private set; } public ExtendedTextCell LoginGenerateCell { get; private set; } @@ -169,8 +170,7 @@ namespace Bit.App.Pages // Types if(Cipher.Type == CipherType.Login) { - LoginTotpCell = new FormEntryCell(AppResources.AuthenticatorKey, nextElement: NotesCell.Editor, - useButton: _deviceInfo.HasCamera); + LoginTotpCell = new FormEntryCell(AppResources.AuthenticatorKey, useButton: _deviceInfo.HasCamera); if(_deviceInfo.HasCamera) { LoginTotpCell.Button.Image = "camera.png"; @@ -201,18 +201,31 @@ namespace Bit.App.Pages LoginUsernameCell.Entry.DisableAutocapitalize = true; LoginUsernameCell.Entry.Autocorrect = false; - LoginUriCell = new FormEntryCell(AppResources.URI, Keyboard.Url, nextElement: LoginUsernameCell.Entry); - LoginUriCell.Entry.Text = Cipher.Login?.Uri?.Decrypt(Cipher.OrganizationId); - // Name - NameCell.NextElement = LoginUriCell.Entry; + NameCell.NextElement = LoginUsernameCell.Entry; // Build sections - TopSection.Add(LoginUriCell); TopSection.Add(LoginUsernameCell); TopSection.Add(LoginPasswordCell); TopSection.Add(LoginGenerateCell); - MiddleSection.Insert(0, LoginTotpCell); + TopSection.Add(LoginTotpCell); + + // Uris + UrisSection = new TableSection(Helpers.GetEmptyTableSectionTitle()); + AddUriCell = new ExtendedTextCell + { + Text = AppResources.NewUri, + TextColor = Colors.Primary + }; + UrisSection.Add(AddUriCell); + if(Cipher.Login?.Uris != null) + { + foreach(var uri in Cipher.Login.Uris) + { + var value = uri.Uri?.Decrypt(Cipher.OrganizationId); + UrisSection.Insert(UrisSection.Count - 1, Helpers.MakeUriCell(value, UrisSection)); + } + } } else if(Cipher.Type == CipherType.Card) { @@ -436,6 +449,11 @@ namespace Bit.App.Pages } }; + if(UrisSection != null) + { + TableRoot.Insert(1, UrisSection); + } + Table = new ExtendedTableView { Intent = TableIntent.Settings, @@ -488,8 +506,6 @@ namespace Bit.App.Pages case CipherType.Login: Cipher.Login = new Login { - Uri = string.IsNullOrWhiteSpace(LoginUriCell.Entry.Text) ? null : - LoginUriCell.Entry.Text.Encrypt(Cipher.OrganizationId), Username = string.IsNullOrWhiteSpace(LoginUsernameCell.Entry.Text) ? null : LoginUsernameCell.Entry.Text.Encrypt(Cipher.OrganizationId), Password = string.IsNullOrWhiteSpace(LoginPasswordCell.Entry.Text) ? null : @@ -497,6 +513,8 @@ namespace Bit.App.Pages Totp = string.IsNullOrWhiteSpace(LoginTotpCell.Entry.Text) ? null : LoginTotpCell.Entry.Text.Encrypt(Cipher.OrganizationId), }; + + Helpers.ProcessUrisSectionForSave(UrisSection, Cipher); break; case CipherType.SecureNote: Cipher.SecureNote = new SecureNote @@ -679,13 +697,16 @@ namespace Bit.App.Pages { AddFieldCell.Tapped += AddFieldCell_Tapped; } + if(AddUriCell != null) + { + AddUriCell.Tapped += AddUriCell_Tapped; + } switch(Cipher.Type) { case CipherType.Login: LoginPasswordCell?.InitEvents(); LoginUsernameCell?.InitEvents(); - LoginUriCell?.InitEvents(); LoginTotpCell?.InitEvents(); if(LoginPasswordCell?.Button != null) { @@ -732,16 +753,8 @@ namespace Bit.App.Pages break; } - if(FieldsSection != null && FieldsSection.Count > 0) - { - foreach(var cell in FieldsSection) - { - if(cell is FormEntryCell entrycell) - { - entrycell.InitEvents(); - } - } - } + Helpers.InitSectionEvents(FieldsSection); + Helpers.InitSectionEvents(UrisSection); } protected override void OnDisappearing() @@ -764,6 +777,10 @@ namespace Bit.App.Pages { AddFieldCell.Tapped -= AddFieldCell_Tapped; } + if(AddUriCell != null) + { + AddUriCell.Tapped -= AddUriCell_Tapped; + } switch(Cipher.Type) { @@ -771,7 +788,6 @@ namespace Bit.App.Pages LoginTotpCell?.Dispose(); LoginPasswordCell?.Dispose(); LoginUsernameCell?.Dispose(); - LoginUriCell?.Dispose(); if(LoginPasswordCell?.Button != null) { LoginPasswordCell.Button.Clicked -= PasswordButton_Clicked; @@ -816,17 +832,9 @@ namespace Bit.App.Pages default: break; } - - if(FieldsSection != null && FieldsSection.Count > 0) - { - foreach(var cell in FieldsSection) - { - if(cell is FormEntryCell entrycell) - { - entrycell.Dispose(); - } - } - } + + Helpers.DisposeSectionEvents(FieldsSection); + Helpers.DisposeSectionEvents(UrisSection); } private void PasswordButton_Clicked(object sender, EventArgs e) @@ -920,6 +928,16 @@ namespace Bit.App.Pages await Helpers.AddField(this, FieldsSection); } + private void AddUriCell_Tapped(object sender, EventArgs e) + { + var cell = Helpers.MakeUriCell(string.Empty, UrisSection); + if(cell != null) + { + UrisSection.Insert(UrisSection.Count - 1, cell); + cell.InitEvents(); + } + } + private void AlertNoConnection() { DisplayAlert(AppResources.InternetConnectionRequiredTitle, AppResources.InternetConnectionRequiredMessage, diff --git a/src/App/Pages/Vault/VaultViewCipherPage.cs b/src/App/Pages/Vault/VaultViewCipherPage.cs index 150d33d55..980bf9e04 100644 --- a/src/App/Pages/Vault/VaultViewCipherPage.cs +++ b/src/App/Pages/Vault/VaultViewCipherPage.cs @@ -39,6 +39,7 @@ namespace Bit.App.Pages private VaultViewCipherPageModel Model { get; set; } = new VaultViewCipherPageModel(); private ExtendedTableView Table { get; set; } private TableSection ItemInformationSection { get; set; } + public TableSection UrisSection { get; set; } private TableSection NotesSection { get; set; } private TableSection AttachmentsSection { get; set; } private TableSection FieldsSection { get; set; } @@ -50,7 +51,6 @@ namespace Bit.App.Pages // Login public LabeledValueCell LoginUsernameCell { get; set; } public LabeledValueCell LoginPasswordCell { get; set; } - public LabeledValueCell LoginUriCell { get; set; } public LabeledValueCell LoginTotpCodeCell { get; set; } // Card @@ -141,22 +141,6 @@ namespace Bit.App.Pages Helpers.OnPlatform(iOS: "Menlo-Regular", Android: "monospace", Windows: "Courier"); LoginPasswordCell.Value.LineBreakMode = LineBreakMode.WordWrap; - // URI - LoginUriCell = new LabeledValueCell(AppResources.Website, button1Image: "launch.png"); - LoginUriCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.LoginUriHost)); - LoginUriCell.Button1.SetBinding(IsVisibleProperty, nameof(VaultViewCipherPageModel.ShowLoginLaunch)); - LoginUriCell.Button1.Command = new Command(async () => - { - if(Device.RuntimePlatform == Device.Android && Model.LoginUri.StartsWith("androidapp://")) - { - await _deviceActionService.LaunchAppAsync(Model.LoginUri, this); - } - else if(Model.LoginUri.StartsWith("http://") || Model.LoginUri.StartsWith("https://")) - { - Device.OpenUri(new Uri(Model.LoginUri)); - } - }); - // Totp LoginTotpCodeCell = new LabeledValueCell( AppResources.VerificationCodeTotp, button1Image: "clipboard.png", subText: "--"); @@ -299,6 +283,22 @@ namespace Bit.App.Pages private void BuildTable(Cipher cipher) { + // URIs + if(UrisSection != null && Table.Root.Contains(UrisSection)) + { + Table.Root.Remove(UrisSection); + } + if(Model.ShowLoginUris) + { + UrisSection = new TableSection(Helpers.GetEmptyTableSectionTitle()); + foreach(var uri in Model.LoginUris) + { + UrisSection.Add(new UriViewCell(this, uri)); + } + Table.Root.Add(UrisSection); + } + + // Notes if(Table.Root.Contains(NotesSection)) { Table.Root.Remove(NotesSection); @@ -365,7 +365,6 @@ namespace Bit.App.Pages switch(cipher.Type) { case CipherType.Login: - AddSectionCell(LoginUriCell, Model.ShowLoginUri); AddSectionCell(LoginUsernameCell, Model.ShowLoginUsername); AddSectionCell(LoginPasswordCell, Model.ShowLoginPassword); @@ -575,8 +574,7 @@ namespace Bit.App.Pages public FieldViewCell(VaultViewCipherPage page, VaultViewCipherPageModel.Field field, bool? a, bool? b) : base(field.Name, field.MaskedValue, string.Empty, "clipboard.png") { - Value.FontFamily = Helpers.OnPlatform(iOS: "Menlo-Regular", - Android: "monospace", Windows: "Courier"); + Value.FontFamily = Helpers.OnPlatform(iOS: "Menlo-Regular", Android: "monospace", Windows: "Courier"); if(Device.RuntimePlatform == Device.iOS) { Button1.Margin = new Thickness(10, 0); @@ -610,5 +608,29 @@ namespace Bit.App.Pages } } } + + public class UriViewCell : LabeledValueCell + { + public UriViewCell(VaultViewCipherPage page, VaultViewCipherPageModel.LoginUri uri) + : base(uri.Label, uri.Host, uri.ShowLaunch ? "launch.png" : null, "clipboard.png") + { + Value.LineBreakMode = LineBreakMode.TailTruncation; + if(Button1 != null) + { + Button1.Command = new Command(async () => + { + if(Device.RuntimePlatform == Device.Android && uri.IsApp) + { + await page._deviceActionService.LaunchAppAsync(uri.Value, page); + } + else if(uri.IsWebsite) + { + Device.OpenUri(new Uri(uri.Value)); + } + }); + } + Button2.Command = new Command(() => page.Copy(uri.Value, AppResources.URI)); + } + } } } diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs index 46fc30bf2..76552a8f1 100644 --- a/src/App/Resources/AppResources.Designer.cs +++ b/src/App/Resources/AppResources.Designer.cs @@ -2130,6 +2130,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to New URI. + /// + public static string NewUri { + get { + return ResourceManager.GetString("NewUri", resourceCulture); + } + } + /// /// Looks up a localized string similar to No. /// @@ -2940,6 +2949,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to URI {0}. + /// + public static string URIPosition { + get { + return ResourceManager.GetString("URIPosition", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use another two-step login method. /// diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx index 369c7f829..480c38a30 100644 --- a/src/App/Resources/AppResources.resx +++ b/src/App/Resources/AppResources.resx @@ -1249,4 +1249,11 @@ Remove + + New URI + + + URI {0} + Label for a uri/url with position. i.e. URI 1, URI 2, etc + \ No newline at end of file diff --git a/src/App/Utilities/Helpers.cs b/src/App/Utilities/Helpers.cs index e37335fe4..985735335 100644 --- a/src/App/Utilities/Helpers.cs +++ b/src/App/Utilities/Helpers.cs @@ -373,5 +373,99 @@ namespace Bit.App.Utilities cipher.Fields = null; } } + + public static FormEntryCell MakeUriCell(string value, TableSection urisSection) + { + var label = string.Format(AppResources.URIPosition, urisSection.Count); + var cell = new FormEntryCell(label, entryKeyboard: Keyboard.Url); + cell.Entry.Text = value; + cell.Entry.DisableAutocapitalize = true; + cell.Entry.Autocorrect = false; + + var deleteAction = new MenuItem { Text = AppResources.Remove, IsDestructive = true }; + deleteAction.Clicked += (sender, e) => + { + if(urisSection.Contains(cell)) + { + urisSection.Remove(cell); + if(cell is FormEntryCell feCell) + { + feCell.Dispose(); + } + cell = null; + + for(int i = 0; i < urisSection.Count; i++) + { + if(urisSection[i] is FormEntryCell uriCell) + { + uriCell.Label.Text = string.Format(AppResources.URIPosition, i + 1); + } + } + } + }; + + var optionsAction = new MenuItem { Text = AppResources.Options }; + optionsAction.Clicked += async (sender, e) => + { + + }; + + cell.ContextActions.Add(optionsAction); + cell.ContextActions.Add(deleteAction); + return cell; + } + + public static void ProcessUrisSectionForSave(TableSection urisSection, Cipher cipher) + { + if(urisSection != null && urisSection.Count > 0) + { + var uris = new List(); + foreach(var cell in urisSection) + { + if(cell is FormEntryCell entryCell && !string.IsNullOrWhiteSpace(entryCell.Entry.Text)) + { + uris.Add(new LoginUri + { + Uri = entryCell.Entry.Text.Encrypt(cipher.OrganizationId), + Match = null + }); + } + } + cipher.Login.Uris = uris; + } + + if(!cipher.Login.Uris?.Any() ?? true) + { + cipher.Login.Uris = null; + } + } + + public static void InitSectionEvents(TableSection section) + { + if(section != null && section.Count > 0) + { + foreach(var cell in section) + { + if(cell is FormEntryCell entrycell) + { + entrycell.InitEvents(); + } + } + } + } + + public static void DisposeSectionEvents(TableSection section) + { + if(section != null && section.Count > 0) + { + foreach(var cell in section) + { + if(cell is FormEntryCell entrycell) + { + entrycell.Dispose(); + } + } + } + } } }