mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
PM-5154 [Passkeys iOS] Updated UI for passkey creation
This commit is contained in:
59
src/iOS.Autofill/ListItems/HeaderItemView.cs
Normal file
59
src/iOS.Autofill/ListItems/HeaderItemView.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using Bit.Core.Services;
|
||||||
|
using Foundation;
|
||||||
|
using ObjCRuntime;
|
||||||
|
using UIKit;
|
||||||
|
|
||||||
|
namespace Bit.iOS.Autofill.ListItems
|
||||||
|
{
|
||||||
|
public class HeaderItemView : UITableViewHeaderFooterView
|
||||||
|
{
|
||||||
|
private readonly UILabel _header = new UILabel();
|
||||||
|
private readonly UIView _separator = new UIView();
|
||||||
|
|
||||||
|
public HeaderItemView(NSString reuseIdentifier)
|
||||||
|
: base(reuseIdentifier)
|
||||||
|
{
|
||||||
|
Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal HeaderItemView(NativeHandle handle) : base(handle)
|
||||||
|
{
|
||||||
|
Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetHeaderText(string text) => _header.Text = text;
|
||||||
|
|
||||||
|
private void Setup()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_header.TextColor = UIColor.FromName(ColorConstants.LIGHT_TEXT_MUTED);
|
||||||
|
_header.Font = UIFont.SystemFontOfSize(15);
|
||||||
|
_separator.BackgroundColor = UIColor.FromName(ColorConstants.LIGHT_SECONDARY_300);
|
||||||
|
|
||||||
|
_header.TranslatesAutoresizingMaskIntoConstraints = false;
|
||||||
|
_separator.TranslatesAutoresizingMaskIntoConstraints = false;
|
||||||
|
|
||||||
|
ContentView.AddSubview(_header);
|
||||||
|
ContentView.AddSubview(_separator);
|
||||||
|
|
||||||
|
NSLayoutConstraint.ActivateConstraints(new NSLayoutConstraint[]
|
||||||
|
{
|
||||||
|
_header.LeadingAnchor.ConstraintEqualTo(ContentView.LayoutMarginsGuide.LeadingAnchor, 9),
|
||||||
|
_header.TrailingAnchor.ConstraintEqualTo(ContentView.LayoutMarginsGuide.TrailingAnchor, 9),
|
||||||
|
_header.TopAnchor.ConstraintEqualTo(ContentView.LayoutMarginsGuide.TopAnchor, 3),
|
||||||
|
|
||||||
|
_separator.HeightAnchor.ConstraintEqualTo(2),
|
||||||
|
_separator.TopAnchor.ConstraintEqualTo(_header.BottomAnchor, 8),
|
||||||
|
_separator.LeadingAnchor.ConstraintEqualTo(ContentView.LayoutMarginsGuide.LeadingAnchor, 5),
|
||||||
|
_separator.TrailingAnchor.ConstraintEqualTo(ContentView.LayoutMarginsGuide.TrailingAnchor, 5),
|
||||||
|
_separator.BottomAnchor.ConstraintEqualTo(ContentView.LayoutMarginsGuide.BottomAnchor, 2)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ using Bit.Core.Abstractions;
|
|||||||
using Bit.Core.Resources.Localization;
|
using Bit.Core.Resources.Localization;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
using Bit.iOS.Autofill.ListItems;
|
||||||
using Bit.iOS.Autofill.Models;
|
using Bit.iOS.Autofill.Models;
|
||||||
using Bit.iOS.Autofill.Utilities;
|
using Bit.iOS.Autofill.Utilities;
|
||||||
using Bit.iOS.Core.Controllers;
|
using Bit.iOS.Core.Controllers;
|
||||||
@@ -20,7 +21,7 @@ namespace Bit.iOS.Autofill
|
|||||||
{
|
{
|
||||||
public partial class LoginListViewController : ExtendedUIViewController
|
public partial class LoginListViewController : ExtendedUIViewController
|
||||||
{
|
{
|
||||||
//internal const string HEADER_SECTION_IDENTIFIER = "headerSectionId";
|
internal const string HEADER_SECTION_IDENTIFIER = "headerSectionId";
|
||||||
|
|
||||||
UIBarButtonItem _cancelButton;
|
UIBarButtonItem _cancelButton;
|
||||||
UIControl _accountSwitchButton;
|
UIControl _accountSwitchButton;
|
||||||
@@ -48,59 +49,71 @@ namespace Bit.iOS.Autofill
|
|||||||
|
|
||||||
public async override void ViewDidLoad()
|
public async override void ViewDidLoad()
|
||||||
{
|
{
|
||||||
_cancelButton = new UIBarButtonItem(UIBarButtonSystemItem.Cancel, CancelButton_TouchUpInside);
|
try
|
||||||
|
|
||||||
base.ViewDidLoad();
|
|
||||||
|
|
||||||
SubscribeSyncCompleted();
|
|
||||||
|
|
||||||
NavItem.Title = Context.IsCreatingPasskey ? AppResources.SavePasskey : AppResources.Items;
|
|
||||||
_cancelButton.Title = AppResources.Cancel;
|
|
||||||
|
|
||||||
TableView.RowHeight = UITableView.AutomaticDimension;
|
|
||||||
TableView.EstimatedRowHeight = 44;
|
|
||||||
TableView.BackgroundColor = ThemeHelpers.BackgroundColor;
|
|
||||||
TableView.Source = new TableSource(this);
|
|
||||||
//TableView.RegisterClassForHeaderFooterViewReuse(typeof(AccountViewCell), HEADER_SECTION_IDENTIFIER);
|
|
||||||
|
|
||||||
await ((TableSource)TableView.Source).LoadAsync();
|
|
||||||
|
|
||||||
if (Context.IsCreatingPasskey)
|
|
||||||
{
|
{
|
||||||
_headerLabel.Text = AppResources.ChooseALoginToSaveThisPasskeyTo;
|
_cancelButton = new UIBarButtonItem(UIBarButtonSystemItem.Cancel, CancelButton_TouchUpInside);
|
||||||
_emptyViewLabel.Text = string.Format(AppResources.NoItemsForUri, Context.UrlString);
|
|
||||||
|
|
||||||
_emptyViewButton.SetTitle(AppResources.SavePasskeyAsNewLogin, UIControlState.Normal);
|
base.ViewDidLoad();
|
||||||
_emptyViewButton.Layer.BorderWidth = 2;
|
|
||||||
_emptyViewButton.Layer.BorderColor = UIColor.FromName(ColorConstants.LIGHT_TEXT_MUTED).CGColor;
|
|
||||||
_emptyViewButton.Layer.CornerRadius = 10;
|
|
||||||
_emptyViewButton.ClipsToBounds = true;
|
|
||||||
|
|
||||||
_headerView.Hidden = false;
|
SubscribeSyncCompleted();
|
||||||
|
|
||||||
|
NavItem.Title = Context.IsCreatingPasskey ? AppResources.SavePasskey : AppResources.Items;
|
||||||
|
_cancelButton.Title = AppResources.Cancel;
|
||||||
|
|
||||||
|
TableView.RowHeight = UITableView.AutomaticDimension;
|
||||||
|
TableView.EstimatedRowHeight = 44;
|
||||||
|
TableView.BackgroundColor = ThemeHelpers.BackgroundColor;
|
||||||
|
TableView.Source = new TableSource(this);
|
||||||
|
TableView.SectionHeaderHeight = 55;
|
||||||
|
TableView.RegisterClassForHeaderFooterViewReuse(typeof(HeaderItemView), HEADER_SECTION_IDENTIFIER);
|
||||||
|
if (UIDevice.CurrentDevice.CheckSystemVersion(15, 0))
|
||||||
|
{
|
||||||
|
TableView.SectionHeaderTopPadding = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ((TableSource)TableView.Source).LoadAsync();
|
||||||
|
|
||||||
|
if (Context.IsCreatingPasskey)
|
||||||
|
{
|
||||||
|
_headerLabel.Text = AppResources.ChooseALoginToSaveThisPasskeyTo;
|
||||||
|
_emptyViewLabel.Text = string.Format(AppResources.NoItemsForUri, Context.UrlString);
|
||||||
|
|
||||||
|
_emptyViewButton.SetTitle(AppResources.SavePasskeyAsNewLogin, UIControlState.Normal);
|
||||||
|
_emptyViewButton.Layer.BorderWidth = 2;
|
||||||
|
_emptyViewButton.Layer.BorderColor = UIColor.FromName(ColorConstants.LIGHT_TEXT_MUTED).CGColor;
|
||||||
|
_emptyViewButton.Layer.CornerRadius = 10;
|
||||||
|
_emptyViewButton.ClipsToBounds = true;
|
||||||
|
|
||||||
|
_headerView.Hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_alreadyLoadItemsOnce = true;
|
||||||
|
|
||||||
|
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
|
var needsAutofillReplacement = await storageService.GetAsync<bool?>(
|
||||||
|
Core.Constants.AutofillNeedsIdentityReplacementKey);
|
||||||
|
if (needsAutofillReplacement.GetValueOrDefault())
|
||||||
|
{
|
||||||
|
await ASHelpers.ReplaceAllIdentitiesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
_accountSwitchingOverlayHelper = new AccountSwitchingOverlayHelper();
|
||||||
|
|
||||||
|
_accountSwitchButton = await _accountSwitchingOverlayHelper.CreateAccountSwitchToolbarButtonItemCustomViewAsync();
|
||||||
|
_accountSwitchButton.TouchUpInside += AccountSwitchedButton_TouchUpInside;
|
||||||
|
|
||||||
|
NavItem.SetLeftBarButtonItems(new UIBarButtonItem[]
|
||||||
|
{
|
||||||
|
_cancelButton,
|
||||||
|
new UIBarButtonItem(_accountSwitchButton)
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
_accountSwitchingOverlayView = _accountSwitchingOverlayHelper.CreateAccountSwitchingOverlayView(OverlayView);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
_alreadyLoadItemsOnce = true;
|
|
||||||
|
|
||||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
var needsAutofillReplacement = await storageService.GetAsync<bool?>(
|
|
||||||
Core.Constants.AutofillNeedsIdentityReplacementKey);
|
|
||||||
if (needsAutofillReplacement.GetValueOrDefault())
|
|
||||||
{
|
{
|
||||||
await ASHelpers.ReplaceAllIdentitiesAsync();
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
_accountSwitchingOverlayHelper = new AccountSwitchingOverlayHelper();
|
|
||||||
|
|
||||||
_accountSwitchButton = await _accountSwitchingOverlayHelper.CreateAccountSwitchToolbarButtonItemCustomViewAsync();
|
|
||||||
_accountSwitchButton.TouchUpInside += AccountSwitchedButton_TouchUpInside;
|
|
||||||
|
|
||||||
NavItem.SetLeftBarButtonItems(new UIBarButtonItem[]
|
|
||||||
{
|
|
||||||
_cancelButton,
|
|
||||||
new UIBarButtonItem(_accountSwitchButton)
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
_accountSwitchingOverlayView = _accountSwitchingOverlayHelper.CreateAccountSwitchingOverlayView(OverlayView);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelButton_TouchUpInside(object sender, EventArgs e)
|
private void CancelButton_TouchUpInside(object sender, EventArgs e)
|
||||||
@@ -130,7 +143,6 @@ namespace Bit.iOS.Autofill
|
|||||||
|
|
||||||
partial void EmptyButton_Activated(UIButton sender)
|
partial void EmptyButton_Activated(UIButton sender)
|
||||||
{
|
{
|
||||||
ClipLogger.Log($"EmptyButton_Activated");
|
|
||||||
SavePasskeyAsNewLoginAsync().FireAndForget(ex =>
|
SavePasskeyAsNewLoginAsync().FireAndForget(ex =>
|
||||||
{
|
{
|
||||||
_platformUtilsService.Value.ShowDialogAsync(AppResources.GenericErrorMessage, AppResources.AnErrorHasOccurred).FireAndForget();
|
_platformUtilsService.Value.ShowDialogAsync(AppResources.GenericErrorMessage, AppResources.AnErrorHasOccurred).FireAndForget();
|
||||||
@@ -145,11 +157,7 @@ namespace Bit.iOS.Autofill
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClipLogger.Log($"SavePasskeyAsNewLoginAsync ");
|
|
||||||
|
|
||||||
var cipherId = await _cipherService.Value.CreateNewLoginForPasskeyAsync(Context.PasskeyCredentialIdentity.RelyingPartyIdentifier);
|
var cipherId = await _cipherService.Value.CreateNewLoginForPasskeyAsync(Context.PasskeyCredentialIdentity.RelyingPartyIdentifier);
|
||||||
|
|
||||||
ClipLogger.Log($"SavePasskeyAsNewLoginAsync -> setting result {cipherId}");
|
|
||||||
Context.ConfirmNewCredentialTcs.TrySetResult(new Fido2ConfirmNewCredentialResult
|
Context.ConfirmNewCredentialTcs.TrySetResult(new Fido2ConfirmNewCredentialResult
|
||||||
{
|
{
|
||||||
CipherId = cipherId,
|
CipherId = cipherId,
|
||||||
@@ -203,7 +211,6 @@ namespace Bit.iOS.Autofill
|
|||||||
|
|
||||||
public void OnEmptyList()
|
public void OnEmptyList()
|
||||||
{
|
{
|
||||||
ClipLogger.Log($"OnEmptyList");
|
|
||||||
_emptyView.Hidden = false;
|
_emptyView.Hidden = false;
|
||||||
_headerView.Hidden = false;
|
_headerView.Hidden = false;
|
||||||
TableView.Hidden = true;
|
TableView.Hidden = true;
|
||||||
@@ -262,42 +269,43 @@ namespace Bit.iOS.Autofill
|
|||||||
|
|
||||||
private Context Context => (Context)_context;
|
private Context Context => (Context)_context;
|
||||||
|
|
||||||
//protected override async Task<IEnumerable<CipherViewModel>> LoadItemsAsync(bool urlFilter = true, string searchFilter = null)
|
|
||||||
//{
|
|
||||||
// if (!Context.IsCreatingPasskey)
|
|
||||||
// {
|
|
||||||
// return await base.LoadItemsAsync(urlFilter, searchFilter);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
//}
|
|
||||||
|
|
||||||
public override async Task LoadAsync(bool urlFilter = true, string searchFilter = null)
|
public override async Task LoadAsync(bool urlFilter = true, string searchFilter = null)
|
||||||
{
|
{
|
||||||
await base.LoadAsync(urlFilter, searchFilter);
|
try
|
||||||
|
|
||||||
if (Context.IsCreatingPasskey && !Items.Any())
|
|
||||||
{
|
{
|
||||||
_controller?.OnEmptyList();
|
await base.LoadAsync(urlFilter, searchFilter);
|
||||||
|
|
||||||
|
if (Context.IsCreatingPasskey && !Items.Any())
|
||||||
|
{
|
||||||
|
_controller?.OnEmptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//public override nint NumberOfSections(UITableView tableView)
|
public override UIView GetViewForHeader(UITableView tableView, nint section)
|
||||||
//{
|
{
|
||||||
// return Context.IsCreatingPasskey ? 1 : 0;
|
try
|
||||||
//}
|
{
|
||||||
|
if (Context.IsCreatingPasskey
|
||||||
|
&&
|
||||||
|
tableView.DequeueReusableHeaderFooterView(LoginListViewController.HEADER_SECTION_IDENTIFIER) is HeaderItemView headerItemView)
|
||||||
|
{
|
||||||
|
headerItemView.SetHeaderText(AppResources.ChooseALoginToSaveThisPasskeyTo);
|
||||||
|
return headerItemView;
|
||||||
|
}
|
||||||
|
|
||||||
//public override UIView GetViewForHeader(UITableView tableView, nint section)
|
return base.GetViewForHeader(tableView, section);
|
||||||
//{
|
}
|
||||||
// if (Context.IsCreatingPasskey)
|
catch (Exception ex)
|
||||||
// {
|
{
|
||||||
// var view = tableView.DequeueReusableHeaderFooterView(LoginListViewController.HEADER_SECTION_IDENTIFIER);
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
return new UIView();
|
||||||
// return view;
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// return base.GetViewForHeader(tableView, section);
|
|
||||||
//}
|
|
||||||
|
|
||||||
public override nint RowsInSection(UITableView tableview, nint section)
|
public override nint RowsInSection(UITableView tableview, nint section)
|
||||||
{
|
{
|
||||||
@@ -311,27 +319,31 @@ namespace Bit.iOS.Autofill
|
|||||||
|
|
||||||
public async override void RowSelected(UITableView tableView, NSIndexPath indexPath)
|
public async override void RowSelected(UITableView tableView, NSIndexPath indexPath)
|
||||||
{
|
{
|
||||||
if (Context.IsCreatingPasskey)
|
try
|
||||||
{
|
{
|
||||||
await SelectRowForPasskeyCreationAsync(tableView, indexPath);
|
if (Context.IsCreatingPasskey)
|
||||||
return;
|
{
|
||||||
}
|
await SelectRowForPasskeyCreationAsync(tableView, indexPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await AutofillHelpers.TableRowSelectedAsync(tableView, indexPath, this,
|
await AutofillHelpers.TableRowSelectedAsync(tableView, indexPath, this,
|
||||||
_controller.CPViewController, _controller, _controller.PasswordRepromptService, "loginAddSegue");
|
_controller.CPViewController, _controller, _controller.PasswordRepromptService, "loginAddSegue");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SelectRowForPasskeyCreationAsync(UITableView tableView, NSIndexPath indexPath)
|
private async Task SelectRowForPasskeyCreationAsync(UITableView tableView, NSIndexPath indexPath)
|
||||||
{
|
{
|
||||||
ClipLogger.Log($"SelectRowForPasskeyCreationAsync");
|
|
||||||
|
|
||||||
tableView.DeselectRow(indexPath, true);
|
tableView.DeselectRow(indexPath, true);
|
||||||
tableView.EndEditing(true);
|
tableView.EndEditing(true);
|
||||||
|
|
||||||
var item = Items.ElementAt(indexPath.Row);
|
var item = Items.ElementAt(indexPath.Row);
|
||||||
if (item is null)
|
if (item is null)
|
||||||
{
|
{
|
||||||
ClipLogger.Log($"SelectRowForPasskeyCreationAsync -> item is null");
|
|
||||||
await _platformUtilsService.Value.ShowDialogAsync(AppResources.GenericErrorMessage, AppResources.AnErrorHasOccurred);
|
await _platformUtilsService.Value.ShowDialogAsync(AppResources.GenericErrorMessage, AppResources.AnErrorHasOccurred);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -344,19 +356,16 @@ namespace Bit.iOS.Autofill
|
|||||||
AppResources.Yes,
|
AppResources.Yes,
|
||||||
AppResources.No))
|
AppResources.No))
|
||||||
{
|
{
|
||||||
ClipLogger.Log($"SelectRowForPasskeyCreationAsync -> don't want to overwrite");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await _passwordRepromptService.Value.PromptAndCheckPasswordIfNeededAsync(item.Reprompt))
|
if (!await _passwordRepromptService.Value.PromptAndCheckPasswordIfNeededAsync(item.Reprompt))
|
||||||
{
|
{
|
||||||
ClipLogger.Log($"SelectRowForPasskeyCreationAsync -> PromptAndCheckPasswordIfNeededAsync -> false");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check user verification
|
// TODO: Check user verification
|
||||||
|
|
||||||
ClipLogger.Log($"SelectRowForPasskeyCreationAsync -> Setting result {item.Id}");
|
|
||||||
Context.ConfirmNewCredentialTcs.SetResult(new Fido2ConfirmNewCredentialResult
|
Context.ConfirmNewCredentialTcs.SetResult(new Fido2ConfirmNewCredentialResult
|
||||||
{
|
{
|
||||||
CipherId = item.Id,
|
CipherId = item.Id,
|
||||||
|
|||||||
@@ -200,7 +200,7 @@
|
|||||||
<constraint firstItem="FDN-Dp-jl3" firstAttribute="top" secondItem="wNm-Sy-bJv" secondAttribute="top" id="kKX-UE-JzG"/>
|
<constraint firstItem="FDN-Dp-jl3" firstAttribute="top" secondItem="wNm-Sy-bJv" secondAttribute="top" id="kKX-UE-JzG"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<tableView opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="2305">
|
<tableView opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="2305">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="781"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="781"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
|
|||||||
@@ -44,6 +44,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<TrimmerRootAssembly Include="System.Security.Cryptography" />
|
<TrimmerRootAssembly Include="System.Security.Cryptography" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="ListItems\" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="CredentialProviderViewController.cs" />
|
<Compile Include="CredentialProviderViewController.cs" />
|
||||||
<Compile Include="CredentialProviderViewController.designer.cs">
|
<Compile Include="CredentialProviderViewController.designer.cs">
|
||||||
@@ -84,6 +87,7 @@
|
|||||||
<Compile Include="CredentialProviderViewController.Passkeys.cs" />
|
<Compile Include="CredentialProviderViewController.Passkeys.cs" />
|
||||||
<Compile Include="SegueConstants.cs" />
|
<Compile Include="SegueConstants.cs" />
|
||||||
<Compile Include="ColorConstants.cs" />
|
<Compile Include="ColorConstants.cs" />
|
||||||
|
<Compile Include="ListItems\HeaderItemView.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<BundleResource Include="Resources\check.png" />
|
<BundleResource Include="Resources\check.png" />
|
||||||
@@ -182,4 +186,7 @@
|
|||||||
<ProjectReference Include="..\Core\Core.csproj" />
|
<ProjectReference Include="..\Core\Core.csproj" />
|
||||||
<ProjectReference Include="..\iOS.Core\iOS.Core.csproj" />
|
<ProjectReference Include="..\iOS.Core\iOS.Core.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="ListItems\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user