Support for fuzzy searching on iOS.
This commit is contained in:
parent
1c72643aaa
commit
4b2251d4fa
@ -519,7 +519,7 @@
|
|||||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||||
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ];
|
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ];
|
||||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
|
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
|
||||||
queryPattern, queryPattern, [[MPMacAppDelegate get] activeUserInContext:context]];
|
queryPattern, queryPattern, [MPMacAppDelegate get].activeUserOID];
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
|
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
|
||||||
|
@ -27,6 +27,8 @@ typedef NS_ENUM ( NSUInteger, MPPasswordCellMode ) {
|
|||||||
|
|
||||||
@interface MPPasswordCell : MPCell <UIScrollViewDelegate, UITextFieldDelegate>
|
@interface MPPasswordCell : MPCell <UIScrollViewDelegate, UITextFieldDelegate>
|
||||||
|
|
||||||
|
@property (nonatomic) NSArray *fuzzyGroups;
|
||||||
|
|
||||||
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated;
|
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated;
|
||||||
- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;
|
- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;
|
||||||
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated;
|
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated;
|
||||||
|
@ -133,6 +133,7 @@
|
|||||||
[super prepareForReuse];
|
[super prepareForReuse];
|
||||||
|
|
||||||
_siteOID = nil;
|
_siteOID = nil;
|
||||||
|
_fuzzyGroups = nil;
|
||||||
self.transientSite = nil;
|
self.transientSite = nil;
|
||||||
self.mode = MPPasswordCellModePassword;
|
self.mode = MPPasswordCellModePassword;
|
||||||
[self updateAnimated:NO];
|
[self updateAnimated:NO];
|
||||||
@ -147,6 +148,15 @@
|
|||||||
|
|
||||||
#pragma mark - State
|
#pragma mark - State
|
||||||
|
|
||||||
|
- (void)setFuzzyGroups:(NSArray *)fuzzyGroups {
|
||||||
|
|
||||||
|
if (_fuzzyGroups == fuzzyGroups)
|
||||||
|
return;
|
||||||
|
_fuzzyGroups = fuzzyGroups;
|
||||||
|
|
||||||
|
[self updateSiteName:[self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated {
|
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated {
|
||||||
|
|
||||||
if (mode == _mode)
|
if (mode == _mode)
|
||||||
@ -490,8 +500,7 @@
|
|||||||
[self.loginNameButton setTitle:@"Tap the pencil to save a username" forState:UIControlStateNormal];
|
[self.loginNameButton setTitle:@"Tap the pencil to save a username" forState:UIControlStateNormal];
|
||||||
|
|
||||||
// Site Name
|
// Site Name
|
||||||
self.siteNameLabel.text = strl( @"%@ - %@", self.transientSite?: mainSite.name,
|
[self updateSiteName:mainSite];
|
||||||
self.transientSite? @"Tap to create": [mainSite.algorithm shortNameOfType:mainSite.type] );
|
|
||||||
|
|
||||||
// Site Password
|
// Site Password
|
||||||
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
|
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
|
||||||
@ -557,6 +566,26 @@
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)updateSiteName:(MPSiteEntity *)site {
|
||||||
|
|
||||||
|
NSString *siteName = self.transientSite?: site.name;
|
||||||
|
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName?: @""];
|
||||||
|
if ([attributedSiteName length])
|
||||||
|
for (NSUInteger f = 0, s = (NSUInteger)-1; f < [self.fuzzyGroups count]; ++f) {
|
||||||
|
s = [siteName rangeOfString:self.fuzzyGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
|
||||||
|
range:NSMakeRange( s + 1, [siteName length] - (s + 1) )].location;
|
||||||
|
if (s == NSNotFound)
|
||||||
|
break;
|
||||||
|
|
||||||
|
[attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[UIColor redColor]
|
||||||
|
range:NSMakeRange( s, [self.fuzzyGroups[f] length] )];
|
||||||
|
}
|
||||||
|
|
||||||
|
[attributedSiteName appendAttributedString:stra(
|
||||||
|
strf( @" - %@", self.transientSite? @"Tap to create": [site.algorithm shortNameOfType:site.type] ), @{ } )];
|
||||||
|
self.siteNameLabel.attributedText = attributedSiteName;
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)copyContentOfSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context {
|
- (BOOL)copyContentOfSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context {
|
||||||
|
|
||||||
inf( @"Copying password for: %@", site.name );
|
inf( @"Copying password for: %@", site.name );
|
||||||
|
@ -44,6 +44,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|||||||
BOOL _showTransientItem;
|
BOOL _showTransientItem;
|
||||||
NSUInteger _transientItem;
|
NSUInteger _transientItem;
|
||||||
NSCharacterSet *_siteNameAcceptableCharactersSet;
|
NSCharacterSet *_siteNameAcceptableCharactersSet;
|
||||||
|
NSArray *_fuzzyGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Life
|
#pragma mark - Life
|
||||||
@ -147,6 +148,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|||||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
|
||||||
MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath];
|
MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath];
|
||||||
|
[cell setFuzzyGroups:_fuzzyGroups];
|
||||||
if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects)
|
if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects)
|
||||||
[cell setSite:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO];
|
[cell setSite:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO];
|
||||||
else
|
else
|
||||||
@ -253,7 +255,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|||||||
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
|
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
|
||||||
|
|
||||||
if (searchBar == self.passwordsSearchBar) {
|
if (searchBar == self.passwordsSearchBar) {
|
||||||
if ([self.query length] && [[self.query stringByTrimmingCharactersInSet:_siteNameAcceptableCharactersSet] length])
|
if ([[self.query stringByTrimmingCharactersInSet:_siteNameAcceptableCharactersSet] length])
|
||||||
[self showTips:MPPasswordsBadNameTip];
|
[self showTips:MPPasswordsBadNameTip];
|
||||||
|
|
||||||
[self updatePasswords];
|
[self updatePasswords];
|
||||||
@ -364,7 +366,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|||||||
|
|
||||||
- (void)updatePasswords {
|
- (void)updatePasswords {
|
||||||
|
|
||||||
NSString *query = self.query;
|
|
||||||
NSManagedObjectID *activeUserOID = [MPiOSAppDelegate get].activeUserOID;
|
NSManagedObjectID *activeUserOID = [MPiOSAppDelegate get].activeUserOID;
|
||||||
if (!activeUserOID) {
|
if (!activeUserOID) {
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
@ -375,6 +376,20 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NSRegularExpression *fuzzyRE;
|
||||||
|
static dispatch_once_t once = 0;
|
||||||
|
dispatch_once( &once, ^{
|
||||||
|
fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
|
||||||
|
} );
|
||||||
|
|
||||||
|
NSString *queryString = self.query;
|
||||||
|
NSString *queryPattern = [queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1*"];
|
||||||
|
NSMutableArray *fuzzyGroups = [NSMutableArray arrayWithCapacity:[queryString length]];
|
||||||
|
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
|
||||||
|
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||||
|
[fuzzyGroups addObject:[queryString substringWithRange:result.range]];
|
||||||
|
}];
|
||||||
|
_fuzzyGroups = fuzzyGroups;
|
||||||
[self.fetchedResultsController.managedObjectContext performBlock:^{
|
[self.fetchedResultsController.managedObjectContext performBlock:^{
|
||||||
NSArray *oldSectionInfos = [self.fetchedResultsController sections];
|
NSArray *oldSectionInfos = [self.fetchedResultsController sections];
|
||||||
NSMutableArray *oldSections = [[NSMutableArray alloc] initWithCapacity:[oldSectionInfos count]];
|
NSMutableArray *oldSections = [[NSMutableArray alloc] initWithCapacity:[oldSectionInfos count]];
|
||||||
@ -383,9 +398,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
self.fetchedResultsController.fetchRequest.predicate =
|
self.fetchedResultsController.fetchRequest.predicate =
|
||||||
[query length]?
|
[NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
|
||||||
[NSPredicate predicateWithFormat:@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, query]:
|
queryPattern, queryPattern, activeUserOID];
|
||||||
[NSPredicate predicateWithFormat:@"user == %@", activeUserOID];
|
|
||||||
if (![self.fetchedResultsController performFetch:&error])
|
if (![self.fetchedResultsController performFetch:&error])
|
||||||
err( @"Couldn't fetch sites: %@", [error fullDescription] );
|
err( @"Couldn't fetch sites: %@", [error fullDescription] );
|
||||||
|
|
||||||
@ -411,6 +425,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|||||||
if (finished)
|
if (finished)
|
||||||
[self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top )
|
[self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top )
|
||||||
animated:YES];
|
animated:YES];
|
||||||
|
for (MPPasswordCell *cell in self.passwordCollectionView.visibleCells)
|
||||||
|
[cell setFuzzyGroups:_fuzzyGroups];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@catch (NSException *exception) {
|
@catch (NSException *exception) {
|
||||||
|
Loading…
Reference in New Issue
Block a user