diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 42d32e493..5757108f4 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -130,7 +130,6 @@ static NSString* const kCallSegue = @"2.0_6.0_Call_Segue"; [TSPreKeyManager refreshPreKeys]; } [MIMETypeUtil initialize]; - return YES; } diff --git a/Signal/src/Storyboard/Storyboard.storyboard b/Signal/src/Storyboard/Storyboard.storyboard index 0b2671932..519aecafb 100755 --- a/Signal/src/Storyboard/Storyboard.storyboard +++ b/Signal/src/Storyboard/Storyboard.storyboard @@ -235,7 +235,7 @@ - + @@ -5526,7 +5526,7 @@ A0 09 9A FF A8 8A 09 99 - + diff --git a/Signal/src/environment/PreferencesUtil.h b/Signal/src/environment/PreferencesUtil.h index 5e398de71..1622a6360 100644 --- a/Signal/src/environment/PreferencesUtil.h +++ b/Signal/src/environment/PreferencesUtil.h @@ -43,4 +43,5 @@ typedef NS_ENUM(NSUInteger, TSImageQuality) { -(NSString*)lastRanVersion; -(NSString*)setAndGetCurrentVersion; + @end diff --git a/Signal/src/environment/PreferencesUtil.m b/Signal/src/environment/PreferencesUtil.m index 4c2785e30..378f62faf 100644 --- a/Signal/src/environment/PreferencesUtil.m +++ b/Signal/src/environment/PreferencesUtil.m @@ -26,7 +26,6 @@ #define NOTIFICATION_PREVIEW_TYPE_KEY @"Notification Preview Type Key" #define IMAGE_UPLOAD_QUALITY_KEY @"Image Upload Quality Key" #define IS_MIGRATING_FROM_1DOT0_TO_LARGER_KEY @"Migrating from 1.0 to Larger" - #define kSignalVersionKey @"SignalUpdateVersionKey" @implementation PropertyListPreferences (PropertyUtil) @@ -110,7 +109,6 @@ } else{ return NO; } - } - (BOOL)loggingIsEnabled{ @@ -131,6 +129,8 @@ } } + + -(NotificationType)notificationPreviewType { NSNumber *preference = [self tryGetValueForKey:NOTIFICATION_PREVIEW_TYPE_KEY]; @@ -192,8 +192,6 @@ [self setValueForKey:IS_MIGRATING_FROM_1DOT0_TO_LARGER_KEY toValue:@(enabled)]; } - - -(NSString*)setAndGetCurrentVersion{ NSString *lastVersion = self.lastRanVersion; [NSUserDefaults.standardUserDefaults setObject:[NSString stringWithFormat:@"%@", NSBundle.mainBundle.infoDictionary[@"CFBundleVersion"]] forKey:kSignalVersionKey]; diff --git a/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterManager.h b/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterManager.h index 06a146a36..337c4be4b 100644 --- a/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterManager.h +++ b/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterManager.h @@ -17,4 +17,6 @@ -(void) startUntilCancelled:(TOCCancelToken*)cancelToken; -(PhoneNumberDirectoryFilter*) getCurrentFilter; +@property BOOL isRefreshing; + @end diff --git a/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterManager.m b/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterManager.m index a317b47ab..e7c0a0e5e 100644 --- a/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterManager.m +++ b/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterManager.m @@ -26,6 +26,7 @@ - (id)init { if (self = [super init]) { phoneNumberDirectoryFilter = PhoneNumberDirectoryFilter.phoneNumberDirectoryFilterDefault; + _isRefreshing = NO; } return self; } @@ -70,7 +71,7 @@ - (void) updateRedPhone { - + _isRefreshing = YES; [[RPServerRequestsManager sharedInstance] performRequest:[RPAPICall fetchBloomFilter] success:^(NSURLSessionDataTask *task, id responseObject) { PhoneNumberDirectoryFilter *directory = [PhoneNumberDirectoryFilter phoneNumberDirectoryFilterFromURLResponse:(NSHTTPURLResponse*)task.response body:responseObject]; @@ -145,9 +146,12 @@ } }]; + _isRefreshing = NO; [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_DIRECTORY_WAS_UPDATED object:nil]; + [self scheduleUpdate]; } failure:^(NSURLSessionDataTask *task, NSError *error) { + _isRefreshing = NO; [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_DIRECTORY_FAILED object:nil]; }]; } diff --git a/Signal/src/view controllers/MessageComposeTableViewController.m b/Signal/src/view controllers/MessageComposeTableViewController.m index ceb34a5c7..ab64aa969 100644 --- a/Signal/src/view controllers/MessageComposeTableViewController.m +++ b/Signal/src/view controllers/MessageComposeTableViewController.m @@ -6,10 +6,11 @@ // // +#import "MessageComposeTableViewController.h" #import "Environment.h" #import "Contact.h" #import "PhoneNumberUtil.h" -#import "MessageComposeTableViewController.h" +#import "PreferencesUtil.h" #import "MessagesViewController.h" #import "SignalsViewController.h" #import "NotificationManifest.h" @@ -26,7 +27,6 @@ { UIButton* sendTextButton; NSString* currentSearchTerm; - NSArray* contacts; NSArray* searchResults; } @@ -34,6 +34,9 @@ @property (nonatomic, strong) UISearchController *searchController; @property (nonatomic, strong) UIActivityIndicatorView *activityIndicator; @property (nonatomic, strong) UIBarButtonItem *addGroup; +@property (nonatomic, strong) UIView *loadingBackgroundView; +@property (nonatomic, strong) UIView *emptyBackgroundView; + @end @implementation MessageComposeTableViewController @@ -47,25 +50,142 @@ [self initializeSearch]; self.searchController.searchBar.hidden = NO; + self.tableView.tableFooterView = [[UIView alloc]initWithFrame:CGRectZero]; + [self createLoadingAndBackgroundViews]; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; +} + + +-(void) viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + BOOL isRefreshing = [Environment getCurrent].phoneDirectoryManager.isRefreshing; + if([contacts count]==0) { + if([Environment getCurrent].phoneDirectoryManager.isRefreshing) { + [self showLoadingBackgroundView:YES]; + } + else { + [self showEmptyBackgroundView:YES]; + } + } + else if(isRefreshing) { + self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height); + [self.refreshControl beginRefreshing]; + } +} + +-(UILabel*) createLabelWithFirstLine:(NSString*) firstLine andSecondLine:(NSString*)secondLine { + UILabel *label = [[UILabel alloc] init]; + label.textColor = [UIColor grayColor]; + label.font = [UIFont ows_regularFontWithSize:18.f]; + label.textAlignment = NSTextAlignmentCenter; + label.numberOfLines = 4; + NSMutableAttributedString *fullLabelString = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@\n%@",firstLine,secondLine]]; + + [fullLabelString addAttribute:NSFontAttributeName value:[UIFont ows_boldFontWithSize:15.f] range:NSMakeRange(0,firstLine.length)]; + [fullLabelString addAttribute:NSFontAttributeName value:[UIFont ows_regularFontWithSize:14.f] range:NSMakeRange(firstLine.length + 1, secondLine.length)]; + [fullLabelString addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0,firstLine.length)]; + [fullLabelString addAttribute:NSForegroundColorAttributeName value:[UIColor ows_darkGrayColor] range:NSMakeRange(firstLine.length + 1, secondLine.length)]; + label.attributedText = fullLabelString; + //250, 66, 140 + [label setFrame:CGRectMake(self.tableView.frame.size.width/2.0f-250/2.0f, 100+140, 250, 66)]; + return label; +} - UIView* loadingView = [[UIView alloc] initWithFrame:self.tableView.frame]; +-(UIButton*) createButtonWithTitle:(NSString*)title { + NSDictionary* buttonTextAttributes = @{NSFontAttributeName:[UIFont ows_regularFontWithSize:15.0f], + NSForegroundColorAttributeName:[UIColor ows_materialBlueColor]}; + UIButton* button = [[UIButton alloc] initWithFrame:CGRectMake(0,0,65,24)]; + NSMutableAttributedString *attributedTitle = [[NSMutableAttributedString alloc] initWithString:title]; + [attributedTitle setAttributes:buttonTextAttributes range:NSMakeRange(0, [attributedTitle length])]; + [button setAttributedTitle:attributedTitle forState:UIControlStateNormal]; + [button.titleLabel setTextAlignment:NSTextAlignmentCenter]; + return button; +} + +-(void) createLoadingAndBackgroundViews { + // This will be further tweaked per design recs. It must currently be hardcoded (or we can place in separate .xib I suppose) as the controller must be a TableViewController to have access to the native pull to refresh capabilities. That means we can't do a UIView in the storyboard + _loadingBackgroundView = [[UIView alloc] initWithFrame:self.tableView.frame]; UIImageView *loadingImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"uiEmpty"]]; [loadingImageView setBackgroundColor:[UIColor whiteColor]]; [loadingImageView setContentMode:UIViewContentModeCenter]; - [loadingImageView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; - [loadingImageView setFrame:self.tableView.frame]; - [loadingView addSubview:loadingImageView]; + [loadingImageView setFrame:CGRectMake(self.tableView.frame.size.width/2.0f-115.0f/2.0f, 100, 115, 110)]; + loadingImageView.contentMode = UIViewContentModeCenter; + loadingImageView.contentMode = UIViewContentModeScaleAspectFit; - self.tableView.backgroundView = loadingView; - self.tableView.backgroundView.opaque = YES; + UIActivityIndicatorView *loadingProgressView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + [loadingProgressView setFrame:CGRectMake(self.tableView.frame.size.width/2.0f-loadingProgressView.frame.size.width/2.0f, 100+110/2.0f-loadingProgressView.frame.size.height/2.0f, loadingProgressView.frame.size.width, loadingProgressView.frame.size.height)]; + [loadingProgressView setHidesWhenStopped:NO]; + [loadingProgressView startAnimating]; + UILabel *loadingLabel = [self createLabelWithFirstLine:@"Loading your contacts." andSecondLine:@"Sit tight."]; + [_loadingBackgroundView addSubview:loadingImageView]; + [_loadingBackgroundView addSubview:loadingProgressView]; + [_loadingBackgroundView addSubview:loadingLabel]; - self.tableView.tableFooterView = [[UIView alloc]initWithFrame:CGRectZero]; + _emptyBackgroundView = [[UIView alloc] initWithFrame:self.tableView.frame]; + UIImageView *emptyImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"uiEmptyContact"]]; + [emptyImageView setBackgroundColor:[UIColor whiteColor]]; + [emptyImageView setContentMode:UIViewContentModeCenter]; + [emptyImageView setFrame:CGRectMake(self.tableView.frame.size.width/2.0f-115.0f/2.0f, 100, 115, 110)]; + emptyImageView.contentMode = UIViewContentModeCenter; + emptyImageView.contentMode = UIViewContentModeScaleAspectFit; + UILabel *emptyLabel = [self createLabelWithFirstLine:@"None of your contacts have Signal!" andSecondLine:@"Why don't you invite someone"]; + + UIButton *inviteContactButton = [self createButtonWithTitle:@"Invite contact"]; + + [inviteContactButton addTarget:self action:@selector(sendText) forControlEvents:UIControlEventTouchUpInside]; + [inviteContactButton setFrame:CGRectMake(self.tableView.frame.size.width/2.0f-inviteContactButton.frame.size.width/1.5f, self.tableView.frame.size.height - 200, 100, 66)]; + [inviteContactButton.titleLabel setTextAlignment:NSTextAlignmentCenter]; + + [_emptyBackgroundView addSubview:emptyImageView]; + [_emptyBackgroundView addSubview:emptyLabel]; + [_emptyBackgroundView addSubview:inviteContactButton]; + } -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; + +-(void) showLoadingBackgroundView:(BOOL)show { + if(show) { + _addGroup = self.navigationItem.rightBarButtonItem!=nil ? _addGroup : self.navigationItem.rightBarButtonItem; + self.navigationItem.rightBarButtonItem = nil; + self.searchController.searchBar.hidden = YES; + self.tableView.backgroundView = _loadingBackgroundView; + self.refreshControl = nil; + self.tableView.backgroundView.opaque = YES; + } + else { + [self initializeRefreshControl]; + self.navigationItem.rightBarButtonItem = self.navigationItem.rightBarButtonItem!=nil ? self.navigationItem.rightBarButtonItem : _addGroup; + self.searchController.searchBar.hidden = NO; + self.tableView.backgroundView = nil; + } +} + + +-(void) showEmptyBackgroundView:(BOOL)show { + + if(show) { + self.refreshControl = nil; + _addGroup = self.navigationItem.rightBarButtonItem!=nil ? _addGroup : self.navigationItem.rightBarButtonItem; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[[UIImage imageNamed:@"btnRefresh--white"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] style:UIBarButtonItemStylePlain target:self action:@selector(refreshContacts)]; + self.navigationItem.rightBarButtonItem.imageInsets = UIEdgeInsetsMake(8,8,8,8); + + + self.searchController.searchBar.hidden = YES; + self.tableView.backgroundView = _emptyBackgroundView; + self.tableView.backgroundView.opaque = YES; + } + else { + [self initializeRefreshControl]; + self.refreshControl.enabled = YES; + self.navigationItem.rightBarButtonItem = self.navigationItem.rightBarButtonItem!=nil ? self.navigationItem.rightBarButtonItem : _addGroup; + self.searchController.searchBar.hidden = NO; + self.tableView.backgroundView = nil; + } } #pragma mark - Initializers @@ -147,8 +267,9 @@ // search by contact name or number NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"(fullName contains[c] %@) OR (allPhoneNumbers contains[c] %@)", searchText, searchText]; searchResults = [contacts filteredArrayUsingPredicate:resultPredicate]; - if (!searchResults.count && _searchController.searchBar.text.length == 0) searchResults = contacts; - + if (!searchResults.count && _searchController.searchBar.text.length == 0) { + searchResults = contacts; + } NSString *formattedNumber = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:searchText].toE164; // text to a non-signal number if we have no results and a valid phone # @@ -168,9 +289,12 @@ #pragma mark - Send Normal Text to Unknown Contact - (void)sendText { - NSString *confirmMessage = @"Would you like to invite the following number to Signal: "; - confirmMessage = [confirmMessage stringByAppendingString:currentSearchTerm]; - confirmMessage = [confirmMessage stringByAppendingString:@"?"]; + NSString *confirmMessage = @"Invite a friend via insecure SMS?"; + if([currentSearchTerm length]>0) { + confirmMessage = @"Would you like to invite the following number to Signal: "; + confirmMessage = [confirmMessage stringByAppendingString:currentSearchTerm]; + confirmMessage = [confirmMessage stringByAppendingString:@"?"]; + } UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Confirm" message:confirmMessage @@ -195,13 +319,12 @@ MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init]; picker.messageComposeDelegate = self; - picker.recipients = [NSArray arrayWithObject:currentSearchTerm]; + picker.recipients = [currentSearchTerm length]> 0 ? [NSArray arrayWithObject:currentSearchTerm] : nil; picker.body = @"I'm inviting you to install Signal! Here is the link: https://itunes.apple.com/us/app/signal-private-messenger/id874139669?mt=8"; [self presentViewController:picker animated:YES completion:[UIUtil modalCompletionBlock]]; } else { // TODO: better backup for iPods (just don't support on) UIAlertView *notPermitted=[[UIAlertView alloc] initWithTitle:@"Alert" message:@"Your device doesn't support this feature." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; - [notPermitted show]; } }]; @@ -210,7 +333,7 @@ [alertController addAction:okAction]; sendTextButton.hidden = YES; self.searchController.searchBar.text = @""; - [self.navigationController dismissViewControllerAnimated:YES completion:nil]; + [self presentViewController:alertController animated:YES completion:[UIUtil modalCompletionBlock]]; } @@ -249,18 +372,7 @@ } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - if([contacts count] == 0) { - self.tableView.backgroundView.hidden = NO; - self.searchController.searchBar.hidden = YES; - _addGroup = self.navigationItem.rightBarButtonItem!=nil ? _addGroup : self.navigationItem.rightBarButtonItem; - self.navigationItem.rightBarButtonItem = nil; - } - else { - self.tableView.backgroundView.hidden = YES; - self.searchController.searchBar.hidden = NO; - self.navigationItem.rightBarButtonItem = self.navigationItem.rightBarButtonItem!=nil ? self.navigationItem.rightBarButtonItem : _addGroup; - } - + if (self.searchController.active) { return (NSInteger)[searchResults count]; } else { @@ -331,19 +443,34 @@ - (void)contactRefreshFailed { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:TIMEOUT message:TIMEOUT_CONTACTS_DETAIL delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", @"") otherButtonTitles:nil]; [alert show]; - [self.refreshControl endRefreshing]; + [self updateAfterRefreshTry]; } - (void)contactsDidRefresh { [self updateSearchResultsForSearchController:self.searchController]; [self.tableView reloadData]; + [self updateAfterRefreshTry]; +} + +- (void) updateAfterRefreshTry { [self.refreshControl endRefreshing]; + + [self showLoadingBackgroundView:NO]; + if([contacts count]==0) { + [self showEmptyBackgroundView:YES]; + } + else { + [self showEmptyBackgroundView:NO]; + } } - (void)refreshContacts { Environment *env = [Environment getCurrent]; PhoneNumberDirectoryFilterManager *manager = [env phoneDirectoryManager]; [manager forceUpdate]; + if([contacts count]==0) { + [self showLoadingBackgroundView:YES]; + } } #pragma mark - Navigation diff --git a/Signal/src/view controllers/UITests/SignalsViewController.m b/Signal/src/view controllers/UITests/SignalsViewController.m index 24e27a938..9e6db5e1e 100644 --- a/Signal/src/view controllers/UITests/SignalsViewController.m +++ b/Signal/src/view controllers/UITests/SignalsViewController.m @@ -378,7 +378,7 @@ static NSString* const kShowSignupFlowSegue = @"showSignupFlow"; - (void)checkIfEmptyView{ [_tableView setHidden:NO]; if (self.viewingThreadsIn == kInboxState && [self.threadMappings numberOfItemsInGroup:TSInboxGroup]==0) { - _emptyBoxImage.image = [UIImage imageNamed:@"uiEmptyContact"]; + _emptyBoxImage.image = [UIImage imageNamed:@"uiEmptyInbox"]; [self setEmptyBoxText]; [_tableView setHidden:YES]; }