diff --git a/src/App/Models/Page/VaultViewCipherPageModel.cs b/src/App/Models/Page/VaultViewCipherPageModel.cs index c5831ab48..0fec0ad78 100644 --- a/src/App/Models/Page/VaultViewCipherPageModel.cs +++ b/src/App/Models/Page/VaultViewCipherPageModel.cs @@ -23,6 +23,7 @@ namespace Bit.App.Models.Page // Card private string _cardName, _cardNumber, _cardBrand, _cardExpMonth, _cardExpYear, _cardCode; + private bool _cardRevealCardCode; // Identity private string _idFirstName, _idLastName, _idMiddleName, _idCompany, _idEmail, _idPhone, _idUsername, @@ -250,6 +251,22 @@ namespace Bit.App.Models.Page } } public bool ShowCardCode => !string.IsNullOrWhiteSpace(CardCode); + public bool RevealCardCode + { + get => _cardRevealCardCode; + set + { + _cardRevealCardCode = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(RevealCardCode))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(MaskedCardCode))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(CardCodeShowHideImage))); + } + } + public string MaskedCardCode => RevealCardCode ? + CardCode : CardCode == null ? null : new String('•', CardCode.Length); + + public ImageSource CardCodeShowHideImage => RevealCardCode ? + ImageSource.FromFile("eye_slash.png") : ImageSource.FromFile("eye.png"); // Identity diff --git a/src/App/Pages/Vault/VaultAddCipherPage.cs b/src/App/Pages/Vault/VaultAddCipherPage.cs index 28b06b128..83a096f20 100644 --- a/src/App/Pages/Vault/VaultAddCipherPage.cs +++ b/src/App/Pages/Vault/VaultAddCipherPage.cs @@ -215,6 +215,7 @@ namespace Bit.App.Pages CardExpYearCell.InitEvents(); CardNameCell.InitEvents(); CardNumberCell.InitEvents(); + CardCodeCell.Button1.Clicked += CardCodeButton_Clicked; break; case CipherType.Identity: IdTitleCell.InitEvents(); @@ -305,6 +306,7 @@ namespace Bit.App.Pages CardExpYearCell.Dispose(); CardNameCell.Dispose(); CardNumberCell.Dispose(); + CardCodeCell.Button1.Clicked -= CardCodeButton_Clicked; break; case CipherType.Identity: IdTitleCell.Dispose(); @@ -384,6 +386,13 @@ namespace Bit.App.Pages await Navigation.PushModalAsync(new ExtendedNavigationPage(scanPage)); } + private void CardCodeButton_Clicked(object sender, EventArgs e) + { + CardCodeCell.Entry.InvokeToggleIsPassword(); + CardCodeCell.Button1.Image = + "eye" + (!CardCodeCell.Entry.IsPasswordFromToggled ? "_slash" : string.Empty) + ".png"; + } + private void AlertNoConnection() { DisplayAlert(AppResources.InternetConnectionRequiredTitle, AppResources.InternetConnectionRequiredMessage, @@ -451,8 +460,7 @@ namespace Bit.App.Pages } else if(_type == CipherType.Card) { - CardCodeCell = new FormEntryCell(AppResources.SecurityCode, Keyboard.Numeric, - nextElement: NotesCell.Editor); + CardCodeCell = new FormEntryCell(AppResources.SecurityCode, Keyboard.Numeric, isPassword: true, nextElement: NotesCell.Editor, button1: "eye.png"); if(!string.IsNullOrWhiteSpace(_defaultCardCode)) { CardCodeCell.Entry.Text = _defaultCardCode; diff --git a/src/App/Pages/Vault/VaultEditCipherPage.cs b/src/App/Pages/Vault/VaultEditCipherPage.cs index 40f2a52fd..a138a9722 100644 --- a/src/App/Pages/Vault/VaultEditCipherPage.cs +++ b/src/App/Pages/Vault/VaultEditCipherPage.cs @@ -219,7 +219,7 @@ namespace Bit.App.Pages else if(Cipher.Type == CipherType.Card) { CardCodeCell = new FormEntryCell(AppResources.SecurityCode, Keyboard.Numeric, - nextElement: NotesCell.Editor); + isPassword: true, nextElement: NotesCell.Editor, button1: "eye.png"); CardCodeCell.Entry.Text = Cipher.Card.Code?.Decrypt(Cipher.OrganizationId); CardExpYearCell = new FormEntryCell(AppResources.ExpirationYear, Keyboard.Numeric, @@ -717,6 +717,10 @@ namespace Bit.App.Pages CardExpYearCell?.InitEvents(); CardNameCell?.InitEvents(); CardNumberCell?.InitEvents(); + if (CardCodeCell?.Button1 != null) + { + CardCodeCell.Button1.Clicked += CardCodeButton_Clicked; + } break; case CipherType.Identity: IdTitleCell?.InitEvents(); @@ -797,6 +801,10 @@ namespace Bit.App.Pages CardExpYearCell?.Dispose(); CardNameCell?.Dispose(); CardNumberCell?.Dispose(); + if (CardCodeCell?.Button1 != null) + { + CardCodeCell.Button1.Clicked -= CardCodeButton_Clicked; + } break; case CipherType.Identity: IdTitleCell?.Dispose(); @@ -871,6 +879,13 @@ namespace Bit.App.Pages await Navigation.PushModalAsync(new ExtendedNavigationPage(scanPage)); } + private void CardCodeButton_Clicked(object sender, EventArgs e) + { + CardCodeCell.Entry.InvokeToggleIsPassword(); + CardCodeCell.Button1.Image = + "eye" + (!CardCodeCell.Entry.IsPasswordFromToggled ? "_slash" : string.Empty) + ".png"; + } + private async void AttachmentsCell_Tapped(object sender, EventArgs e) { var page = new ExtendedNavigationPage(new VaultAttachmentsPage(_cipherId)); diff --git a/src/App/Pages/Vault/VaultViewCipherPage.cs b/src/App/Pages/Vault/VaultViewCipherPage.cs index f377121c3..05c00f109 100644 --- a/src/App/Pages/Vault/VaultViewCipherPage.cs +++ b/src/App/Pages/Vault/VaultViewCipherPage.cs @@ -168,9 +168,14 @@ namespace Bit.App.Pages CardExpCell = new LabeledValueCell(AppResources.Expiration); CardExpCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.CardExp)); - CardCodeCell = new LabeledValueCell(AppResources.SecurityCode, button1Image: "clipboard.png"); - CardCodeCell.Button1.Command = new Command(() => Copy(Model.CardCode, AppResources.SecurityCode)); - CardCodeCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.CardCode)); + CardCodeCell = new LabeledValueCell(AppResources.SecurityCode, button1Image: string.Empty, button2Image: "clipboard.png"); + CardCodeCell.Value.SetBinding(Label.TextProperty, + nameof(VaultViewCipherPageModel.MaskedCardCode)); + CardCodeCell.Button1.SetBinding(Button.ImageProperty, + nameof(VaultViewCipherPageModel.CardCodeShowHideImage)); + CardCodeCell.Button1.Command = + new Command(() => Model.RevealCardCode = !Model.RevealCardCode); + CardCodeCell.Button2.Command = new Command(() => Copy(Model.CardCode, AppResources.SecurityCode)); break; case CipherType.Identity: IdNameCell = new LabeledValueCell(AppResources.Name);