Search controller for autofill setup

This commit is contained in:
kspearrin 2018-09-24 12:41:08 -04:00
parent e9ec02929d
commit 42fc868b68
8 changed files with 359 additions and 15 deletions

View file

@ -10,6 +10,7 @@ namespace Bit.iOS.Autofill
{ }
public LoginListViewController LoginListController { get; set; }
public LoginSearchViewController LoginSearchController { get; set; }
public override UINavigationItem BaseNavItem => NavItem;
public override UIBarButtonItem BaseCancelButton => CancelBarButton;
@ -19,6 +20,7 @@ namespace Bit.iOS.Autofill
{
_googleAnalyticsService.TrackAutofillExtensionEvent("CreatedLogin");
LoginListController?.DismissModal();
LoginSearchController?.DismissModal();
};
partial void CancelBarButton_Activated(UIBarButtonItem sender)

View file

@ -39,7 +39,7 @@ namespace Bit.iOS.Autofill
partial void CancelBarButton_Activated(UIBarButtonItem sender)
{
CPViewController.CompleteRequest(null, null, null);
CPViewController.CompleteRequest();
}
partial void AddBarButton_Activated(UIBarButtonItem sender)
@ -47,17 +47,28 @@ namespace Bit.iOS.Autofill
PerformSegue("loginAddSegue", this);
}
partial void SearchBarButton_Activated(UIBarButtonItem sender)
{
PerformSegue("loginSearchFromListSegue", this);
}
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
var navController = segue.DestinationViewController as UINavigationController;
if(navController != null)
{
var searchLoginController = navController.TopViewController as LoginSearchViewController;
var addLoginController = navController.TopViewController as LoginAddViewController;
if(addLoginController != null)
{
addLoginController.Context = Context;
addLoginController.LoginListController = this;
}
if(searchLoginController != null)
{
searchLoginController.Context = Context;
searchLoginController.CPViewController = CPViewController;
}
}
}

View file

@ -34,6 +34,10 @@ namespace Bit.iOS.Autofill
[GeneratedCode ("iOS Designer", "1.0")]
partial void CancelBarButton_Activated (UIKit.UIBarButtonItem sender);
[Action ("SearchBarButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void SearchBarButton_Activated (UIKit.UIBarButtonItem sender);
void ReleaseDesignerOutlets ()
{
if (AddBarButton != null) {

View file

@ -0,0 +1,161 @@
using System;
using System.Linq;
using Bit.iOS.Autofill.Models;
using Foundation;
using UIKit;
using Bit.iOS.Core.Utilities;
using Bit.iOS.Core.Controllers;
using Bit.App.Resources;
using Bit.iOS.Core.Views;
namespace Bit.iOS.Autofill
{
public partial class LoginSearchViewController : ExtendedUITableViewController
{
public LoginSearchViewController(IntPtr handle) : base(handle)
{ }
public Context Context { get; set; }
public CredentialProviderViewController CPViewController { get; set; }
public override void ViewWillAppear(bool animated)
{
UINavigationBar.Appearance.ShadowImage = new UIImage();
UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
base.ViewWillAppear(animated);
}
public async override void ViewDidLoad()
{
base.ViewDidLoad();
NavItem.Title = AppResources.SearchVault;
CancelBarButton.Title = AppResources.Cancel;
TableView.RowHeight = UITableView.AutomaticDimension;
TableView.EstimatedRowHeight = 44;
TableView.Source = new TableSource(this);
await ((TableSource)TableView.Source).LoadItemsAsync(false, SearchBar.Text);
}
partial void CancelBarButton_Activated(UIBarButtonItem sender)
{
CPViewController.CompleteRequest();
}
partial void AddBarButton_Activated(UIBarButtonItem sender)
{
PerformSegue("loginAddFromSearchSegue", this);
}
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
var navController = segue.DestinationViewController as UINavigationController;
if(navController != null)
{
var addLoginController = navController.TopViewController as LoginAddViewController;
if(addLoginController != null)
{
addLoginController.Context = Context;
addLoginController.LoginSearchController = this;
}
}
}
public void DismissModal()
{
DismissViewController(true, async () =>
{
await ((TableSource)TableView.Source).LoadItemsAsync(false, SearchBar.Text);
TableView.ReloadData();
});
}
public class TableSource : ExtensionTableSource
{
private Context _context;
private LoginSearchViewController _controller;
public TableSource(LoginSearchViewController controller)
:base(controller.Context, controller)
{
_context = controller.Context;
_controller = controller;
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
tableView.DeselectRow(indexPath, true);
tableView.EndEditing(true);
if(_tableItems == null || _tableItems.Count() == 0)
{
_controller.PerformSegue("loginAddFromSearchSegue", this);
return;
}
var item = _tableItems.ElementAt(indexPath.Row);
if(item == null)
{
_controller.CPViewController.CompleteRequest(null, null, null);
return;
}
if(!string.IsNullOrWhiteSpace(item.Password))
{
string totp = null;
if(!_settings.GetValueOrDefault(App.Constants.SettingDisableTotpCopy, false))
{
totp = GetTotp(item);
}
_controller.CPViewController.CompleteRequest(item.Username, item.Password, totp);
}
else if(!string.IsNullOrWhiteSpace(item.Username) || !string.IsNullOrWhiteSpace(item.Totp.Value))
{
var sheet = Dialogs.CreateActionSheet(item.Name, _controller);
if(!string.IsNullOrWhiteSpace(item.Username))
{
sheet.AddAction(UIAlertAction.Create(AppResources.CopyUsername, UIAlertActionStyle.Default, a =>
{
UIPasteboard clipboard = UIPasteboard.General;
clipboard.String = item.Username;
var alert = Dialogs.CreateMessageAlert(AppResources.CopyUsername);
_controller.PresentViewController(alert, true, () =>
{
_controller.DismissViewController(true, null);
});
}));
}
if(!string.IsNullOrWhiteSpace(item.Totp.Value))
{
sheet.AddAction(UIAlertAction.Create(AppResources.CopyTotp, UIAlertActionStyle.Default, a =>
{
var totp = GetTotp(item);
if(string.IsNullOrWhiteSpace(totp))
{
return;
}
UIPasteboard clipboard = UIPasteboard.General;
clipboard.String = totp;
var alert = Dialogs.CreateMessageAlert(AppResources.CopiedTotp);
_controller.PresentViewController(alert, true, () =>
{
_controller.DismissViewController(true, null);
});
}));
}
sheet.AddAction(UIAlertAction.Create(AppResources.Cancel, UIAlertActionStyle.Cancel, null));
_controller.PresentViewController(sheet, true, null);
}
else
{
var alert = Dialogs.CreateAlert(null, AppResources.NoUsernamePasswordConfigured, AppResources.Ok);
_controller.PresentViewController(alert, true, null);
}
}
}
}
}

View file

@ -0,0 +1,64 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace Bit.iOS.Autofill
{
[Register ("LoginSearchViewController")]
partial class LoginSearchViewController
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIBarButtonItem CancelBarButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UINavigationItem NavItem { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UISearchBar SearchBar { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UISearchDisplayController searchDisplayController { get; set; }
[Action ("AddBarButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void AddBarButton_Activated (UIKit.UIBarButtonItem sender);
[Action ("CancelBarButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void CancelBarButton_Activated (UIKit.UIBarButtonItem sender);
void ReleaseDesignerOutlets ()
{
if (CancelBarButton != null) {
CancelBarButton.Dispose ();
CancelBarButton = null;
}
if (NavItem != null) {
NavItem.Dispose ();
NavItem = null;
}
if (SearchBar != null) {
SearchBar.Dispose ();
SearchBar = null;
}
if (searchDisplayController != null) {
searchDisplayController.Dispose ();
searchDisplayController = null;
}
}
}
}

View file

@ -32,8 +32,8 @@
<segue destination="6512" kind="presentation" identifier="lockFingerprintSegue" id="8446"/>
<segue destination="6815" kind="presentation" identifier="lockPinSegue" id="8924"/>
<segue destination="6855" kind="presentation" identifier="lockPasswordSegue" id="9874"/>
<segue destination="1845" kind="presentation" identifier="newLoginSegue" modalPresentationStyle="fullScreen" modalTransitionStyle="coverVertical" id="10498"/>
<segue id="11089" destination="10580" kind="presentation" modalTransitionStyle="coverVertical" identifier="setupSegue"/>
<segue id="12959" destination="11552" kind="show" identifier="loginSearchSegue"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="45" userLabel="First Responder" sceneMemberID="firstResponder"/>
@ -171,12 +171,20 @@
<action selector="CancelBarButton_Activated:" destination="2304" id="3750"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" systemItem="add" id="3736">
<rightBarButtonItems>
<barButtonItem systemItem="add" id="3736">
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<action selector="AddBarButton_Activated:" destination="2304" id="3749"/>
</connections>
</barButtonItem>
<barButtonItem id="13195" systemItem="search">
<color key="tintColor" colorSpace="calibratedWhite" white="1" alpha="1"/>
<connections>
<action selector="SearchBarButton_Activated:" destination="2304" id="13400"/>
</connections>
</barButtonItem>
</rightBarButtonItems>
</navigationItem>
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
<connections>
@ -184,6 +192,7 @@
<outlet property="CancelBarButton" destination="3735" id="name-outlet-3735"/>
<outlet property="NavItem" destination="3734" id="name-outlet-3734"/>
<segue destination="1845" kind="presentation" identifier="loginAddSegue" modalPresentationStyle="fullScreen" modalTransitionStyle="coverVertical" id="3731"/>
<segue id="12574" destination="11552" kind="show" identifier="loginSearchFromListSegue"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="2310" userLabel="First Responder" sceneMemberID="firstResponder"/>
@ -548,7 +557,7 @@
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" id="11094" translatesAutoresizingMaskIntoConstraints="NO" image="check.png" misplaced="YES" ambiguous="YES">
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" id="11094" translatesAutoresizingMaskIntoConstraints="NO" image="check.png" misplaced="YES">
<rect key="frame" x="255" y="205.5" width="90" height="90"/>
</imageView>
</subviews>
@ -604,9 +613,90 @@
</objects>
<point key="canvasLocation" x="362" y="-267"/>
</scene>
<scene sceneID="11542">
<objects>
<tableViewController id="11543" sceneMemberID="viewController" customClass="LoginSearchViewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="11545">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="11548">
<rect key="frame" x="0.0" y="72" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="11548" id="11549">
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="11543" id="11546"/>
<outlet property="delegate" destination="11543" id="11547"/>
</connections>
<searchBar contentMode="redraw" id="13084" key="tableHeaderView">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="11543" id="13085"/>
</connections>
</searchBar>
</tableView>
<navigationItem key="navigationItem" title="Search Logins" id="11544">
<barButtonItem key="leftBarButtonItem" id="11950" title="Cancel">
<color key="tintColor" colorSpace="calibratedWhite" white="1" alpha="1"/>
<connections>
<action selector="CancelBarButton_Activated:" destination="11543" id="12044"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" id="11951" systemItem="add">
<color key="tintColor" colorSpace="calibratedWhite" white="1" alpha="1"/>
<connections>
<action selector="AddBarButton_Activated:" destination="11543" id="12045"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="CancelBarButton" destination="11950" id="name-outlet-11950"/>
<outlet property="NavItem" destination="11544" id="name-outlet-11544"/>
<segue id="12738" destination="1845" kind="show" identifier="loginAddFromSearchSegue"/>
<outlet property="searchDisplayController" destination="13087" id="13086"/>
<outlet property="SearchBar" destination="13084" id="name-outlet-13084"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="11550" userLabel="First Responder" sceneMemberID="firstResponder"/>
<searchDisplayController id="13087">
<connections>
<outlet property="delegate" destination="11543" id="13088"/>
<outlet property="searchBar" destination="13084" id="13089"/>
<outlet property="searchContentsController" destination="11543" id="13090"/>
<outlet property="searchResultsDataSource" destination="11543" id="13091"/>
<outlet property="searchResultsDelegate" destination="11543" id="13092"/>
</connections>
</searchDisplayController>
</objects>
<point key="canvasLocation" x="2513" y="907"/>
</scene>
<scene sceneID="11551">
<objects>
<navigationController id="11552" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" id="11554">
<rect key="frame" x="0.0" y="20" width="414" height="50"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="11543" kind="relationship" relationship="rootViewController" id="11553"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="11555" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1920" y="908"/>
</scene>
</scenes>
<resources>
<image name="fingerprint.png" width="91" height="92"/>
<image name="logo.png" width="282" height="44"/>
<image name="check.png" width="90" height="90"/>
</resources>
</document>

View file

@ -241,6 +241,10 @@
<Compile Include="PasswordGeneratorViewController.designer.cs">
<DependentUpon>PasswordGeneratorViewController.cs</DependentUpon>
</Compile>
<Compile Include="LoginSearchViewController.cs" />
<Compile Include="LoginSearchViewController.designer.cs">
<DependentUpon>LoginSearchViewController.cs</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />

View file

@ -35,19 +35,27 @@ namespace Bit.iOS.Core.Views
_controller = controller;
}
public async Task LoadItemsAsync()
public async Task LoadItemsAsync(bool urlFilter = true, string searchFilter = null)
{
var combinedLogins = new List<Cipher>();
if (urlFilter)
{
var logins = await _cipherService.GetAllAsync(_context.UrlString);
if(logins?.Item1 != null)
if (logins?.Item1 != null)
{
combinedLogins.AddRange(logins.Item1);
}
if(logins?.Item2 != null)
if (logins?.Item2 != null)
{
combinedLogins.AddRange(logins.Item2);
}
}
else
{
var logins = await _cipherService.GetAllAsync();
combinedLogins.AddRange(logins);
}
_tableItems = combinedLogins.Select(s => new CipherViewModel(s))
.OrderBy(s => s.Name)