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] )];
|
||||
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ];
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
|
||||
queryPattern, queryPattern, [[MPMacAppDelegate get] activeUserInContext:context]];
|
||||
queryPattern, queryPattern, [MPMacAppDelegate get].activeUserOID];
|
||||
|
||||
NSError *error = nil;
|
||||
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
|
||||
|
@ -27,6 +27,8 @@ typedef NS_ENUM ( NSUInteger, MPPasswordCellMode ) {
|
||||
|
||||
@interface MPPasswordCell : MPCell <UIScrollViewDelegate, UITextFieldDelegate>
|
||||
|
||||
@property (nonatomic) NSArray *fuzzyGroups;
|
||||
|
||||
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated;
|
||||
- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;
|
||||
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated;
|
||||
|
@ -133,6 +133,7 @@
|
||||
[super prepareForReuse];
|
||||
|
||||
_siteOID = nil;
|
||||
_fuzzyGroups = nil;
|
||||
self.transientSite = nil;
|
||||
self.mode = MPPasswordCellModePassword;
|
||||
[self updateAnimated:NO];
|
||||
@ -147,6 +148,15 @@
|
||||
|
||||
#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 {
|
||||
|
||||
if (mode == _mode)
|
||||
@ -490,8 +500,7 @@
|
||||
[self.loginNameButton setTitle:@"Tap the pencil to save a username" forState:UIControlStateNormal];
|
||||
|
||||
// Site Name
|
||||
self.siteNameLabel.text = strl( @"%@ - %@", self.transientSite?: mainSite.name,
|
||||
self.transientSite? @"Tap to create": [mainSite.algorithm shortNameOfType:mainSite.type] );
|
||||
[self updateSiteName:mainSite];
|
||||
|
||||
// Site Password
|
||||
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 {
|
||||
|
||||
inf( @"Copying password for: %@", site.name );
|
||||
|
@ -44,6 +44,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
BOOL _showTransientItem;
|
||||
NSUInteger _transientItem;
|
||||
NSCharacterSet *_siteNameAcceptableCharactersSet;
|
||||
NSArray *_fuzzyGroups;
|
||||
}
|
||||
|
||||
#pragma mark - Life
|
||||
@ -147,6 +148,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath];
|
||||
[cell setFuzzyGroups:_fuzzyGroups];
|
||||
if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects)
|
||||
[cell setSite:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO];
|
||||
else
|
||||
@ -253,7 +255,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
|
||||
|
||||
if (searchBar == self.passwordsSearchBar) {
|
||||
if ([self.query length] && [[self.query stringByTrimmingCharactersInSet:_siteNameAcceptableCharactersSet] length])
|
||||
if ([[self.query stringByTrimmingCharactersInSet:_siteNameAcceptableCharactersSet] length])
|
||||
[self showTips:MPPasswordsBadNameTip];
|
||||
|
||||
[self updatePasswords];
|
||||
@ -364,7 +366,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
- (void)updatePasswords {
|
||||
|
||||
NSString *query = self.query;
|
||||
NSManagedObjectID *activeUserOID = [MPiOSAppDelegate get].activeUserOID;
|
||||
if (!activeUserOID) {
|
||||
PearlMainQueue( ^{
|
||||
@ -375,6 +376,20 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
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:^{
|
||||
NSArray *oldSectionInfos = [self.fetchedResultsController sections];
|
||||
NSMutableArray *oldSections = [[NSMutableArray alloc] initWithCapacity:[oldSectionInfos count]];
|
||||
@ -383,9 +398,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
NSError *error = nil;
|
||||
self.fetchedResultsController.fetchRequest.predicate =
|
||||
[query length]?
|
||||
[NSPredicate predicateWithFormat:@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, query]:
|
||||
[NSPredicate predicateWithFormat:@"user == %@", activeUserOID];
|
||||
[NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
|
||||
queryPattern, queryPattern, activeUserOID];
|
||||
if (![self.fetchedResultsController performFetch:&error])
|
||||
err( @"Couldn't fetch sites: %@", [error fullDescription] );
|
||||
|
||||
@ -411,6 +425,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
if (finished)
|
||||
[self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top )
|
||||
animated:YES];
|
||||
for (MPPasswordCell *cell in self.passwordCollectionView.visibleCells)
|
||||
[cell setFuzzyGroups:_fuzzyGroups];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
|
Loading…
Reference in New Issue
Block a user