@ -22,9 +22,9 @@ NS_ASSUME_NONNULL_BEGIN
@ property ( nonatomic ) IBOutlet UITableViewCell * inviteCell ;
@ property ( nonatomic ) UITableViewCell * conversationForNonContactCell ;
@ property ( nonatomic ) UITableViewCell * inviteViaSMSCell ;
@ property ( nonatomic ) IBOutlet OWSNoSignalContactsView * noSignalContactsView ;
@ property ( nonatomic ) UIButton * sendTextButton ;
@ property ( nonatomic ) UISearchController * searchController ;
@ property ( nonatomic ) UIActivityIndicatorView * activityIndicator ;
@ property ( nonatomic ) UIBarButtonItem * addGroup ;
@ -35,15 +35,29 @@ NS_ASSUME_NONNULL_BEGIN
@ property ( copy ) NSArray < Contact * > * searchResults ;
@ property ( nonatomic , readonly ) OWSContactsManager * contactsManager ;
@ property ( nonatomic ) BOOL showNewConversationForNonContactButton ;
/ / This property should be set IFF showNewConversationForNonContactButton is YES .
@ property ( nonatomic ) NSString * nonContactPhoneNumber ;
/ / This property should be set IFF the current search text can
/ / be parsed as a phone number . If set , it contains a E164 value .
@ property ( nonatomic ) NSString * searchPhoneNumber ;
/ / This dictionary is used to cache the set of phone numbers
/ / which are known to correspond to Signal accounts .
@ property ( nonatomic ) NSMutableSet * phoneNumberAccountSet ;
@ property ( nonatomic ) BOOL isBackgroundViewHidden ;
@ end
NSInteger const MessageComposeTableViewControllerSectionInvite = 0 ;
NSInteger const MessageComposeTableViewControllerSectionContacts = 1 ;
NSInteger const MessageComposeTableViewControllerSectionNewConversationForNonContact = 2 ;
/ / The "special " sections are used to display ( at most ) one of three cells :
/ /
/ / * "New conversation for non - contact " if user has entered a phone
/ / number which corresponds to a signal account , or :
/ / * "Send invite via SMS " if user has entered a phone number
/ / which is not known to correspond to a signal account , or :
/ / * "Invite contacts " if the invite flow is available , or :
/ / * Nothing , otherwise .
NSInteger const MessageComposeTableViewControllerSectionInviteNonContactConversation = 0 ;
NSInteger const MessageComposeTableViewControllerSectionInviteViaSMS = 1 ;
NSInteger const MessageComposeTableViewControllerSectionInviteFlow = 2 ;
NSInteger const MessageComposeTableViewControllerSectionContacts = 3 ;
NSString * const MessageComposeTableViewControllerCellInvite = @ "ContactTableInviteCell ";
NSString * const MessageComposeTableViewControllerCellContact = @ "ContactTableViewCell ";
@ -91,6 +105,7 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
@ "INVITE_FRIENDS_CONTACT_TABLE_BUTTON ", @ "Text for button at the top of the contact picker ") ;
self . conversationForNonContactCell = [ UITableViewCell new ] ;
self . inviteViaSMSCell = [ UITableViewCell new ] ;
self . tableView . tableFooterView = [ [ UIView alloc ] initWithFrame : CGRectZero ] ;
[ self createLoadingAndBackgroundViews ] ;
@ -160,9 +175,36 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
[ _loadingBackgroundView addSubview : loadingProgressView ] ;
[ _loadingBackgroundView addSubview : loadingLabel ] ;
[ self . noSignalContactsView . inviteButton addTarget : self
action : @ selector ( presentInviteFlow )
forControlEvents : UIControlEventTouchUpInside ] ;
UIButton * inviteButton = self . noSignalContactsView . inviteButton ;
[ inviteButton addTarget : self
action : @ selector ( presentInviteFlow )
forControlEvents : UIControlEventTouchUpInside ] ;
[ inviteButton setTitleColor : [ UIColor ows_materialBlueColor ]
forState : UIControlStateNormal ] ;
[ inviteButton . titleLabel setFont : [ UIFont ows_regularFontWithSize : 17. f ] ] ;
UIButton * searchByPhoneNumberButton = [ UIButton buttonWithType : UIButtonTypeCustom ] ;
[ searchByPhoneNumberButton setTitle : NSLocalizedString ( @ "NO_CONTACTS_SEARCH_BY_PHONE_NUMBER ",
@ "Label for a button that lets users search for contacts by phone number ")
forState : UIControlStateNormal ] ;
[ searchByPhoneNumberButton setTitleColor : [ UIColor ows_materialBlueColor ]
forState : UIControlStateNormal ] ;
[ searchByPhoneNumberButton . titleLabel setFont : [ UIFont ows_regularFontWithSize : 17. f ] ] ;
[ inviteButton . superview addSubview : searchByPhoneNumberButton ] ;
[ searchByPhoneNumberButton autoHCenterInSuperview ] ;
[ searchByPhoneNumberButton autoPinEdge : ALEdgeTop
toEdge : ALEdgeBottom
ofView : inviteButton
withOffset : 20 ] ;
[ searchByPhoneNumberButton addTarget : self
action : @ selector ( hideBackgroundView )
forControlEvents : UIControlEventTouchUpInside ] ;
}
- ( void ) hideBackgroundView {
self . isBackgroundViewHidden = YES ;
[ self showEmptyBackgroundView : NO ] ;
}
- ( void ) presentInviteFlow
@ -172,9 +214,8 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
[ self presentViewController : inviteFlow . actionSheetController animated : YES completion : nil ] ;
}
- ( void ) showLoadingBackgroundView : ( BOOL ) show {
if ( show ) {
if ( show && !self . isBackgroundViewHidden ) {
_addGroup = self . navigationItem . rightBarButtonItem != nil ? _addGroup : self . navigationItem . rightBarButtonItem ;
self . navigationItem . rightBarButtonItem = nil ;
self . searchController . searchBar . hidden = YES ;
@ -206,6 +247,7 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
self . inviteCell . hidden = YES ;
self . conversationForNonContactCell . hidden = YES ;
self . inviteViaSMSCell . hidden = YES ;
self . searchController . searchBar . hidden = YES ;
self . tableView . backgroundView = self . noSignalContactsView ;
self . tableView . backgroundView . opaque = YES ;
@ -218,6 +260,7 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
self . tableView . backgroundView = nil ;
self . inviteCell . hidden = NO ;
self . conversationForNonContactCell . hidden = NO ;
self . inviteViaSMSCell . hidden = NO ;
}
}
@ -244,17 +287,6 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
self . searchController . searchBar . delegate = self ;
self . searchController . searchBar . placeholder = NSLocalizedString ( @ "SEARCH_BYNAMEORNUMBER_PLACEHOLDER_TEXT ", @ "") ;
self . sendTextButton = [ UIButton buttonWithType : UIButtonTypeRoundedRect ] ;
[ self . sendTextButton setBackgroundColor : [ UIColor ows_materialBlueColor ] ] ;
[ self . sendTextButton setTitleColor : [ UIColor whiteColor ] forState : UIControlStateNormal ] ;
self . sendTextButton . frame = CGRectMake ( self . searchController . searchBar . frame . origin . x ,
self . searchController . searchBar . frame . origin . y + 44.0 f ,
self . searchController . searchBar . frame . size . width ,
44.0 ) ;
[ self . view addSubview : self . sendTextButton ] ;
self . sendTextButton . hidden = YES ;
[ self . sendTextButton addTarget : self action : @ selector ( sendText ) forControlEvents : UIControlEventTouchUpInside ] ;
[ self initializeRefreshControl ] ;
}
@ -283,10 +315,8 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
}
- ( void ) searchBarCancelButtonClicked : ( UISearchBar * ) searchBar {
self . sendTextButton . hidden = YES ;
}
#pragma mark - Filter
- ( void ) filterContentForSearchText : ( NSString * ) searchText
@ -297,26 +327,58 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
NSString * formattedNumber = [ PhoneNumber tryParsePhoneNumberFromUserSpecifiedText : searchText ] . toE164 ;
/ / text to a non - signal number if we have no results and a valid phone #
if ( self . searchResults . count == 0 && searchText . length > 8 && formattedNumber ) {
NSString * sendTextTo = NSLocalizedString ( @ "SEND_SMS_BUTTON ", @ "") ;
sendTextTo = [ sendTextTo stringByAppendingString : formattedNumber ] ;
[ self . sendTextButton setTitle : sendTextTo forState : UIControlStateNormal ] ;
self . sendTextButton . hidden = NO ;
self . currentSearchTerm = formattedNumber ;
self . showNewConversationForNonContactButton = YES ;
self . nonContactPhoneNumber = formattedNumber ;
self . searchPhoneNumber = formattedNumber ;
/ / Kick off account lookup if necessary .
[ self checkIsNonContactPhoneNumberSignalUser : formattedNumber ] ;
} else {
self . sendTextButton . hidden = YES ;
self . showNewConversationForNonContactButton = NO ;
_nonContactPhoneNumber = nil ;
_searchPhoneNumber = nil ;
}
}
- ( void ) setShowNewConversationForNonContactButton : ( BOOL ) showNewConversationForNonContactButton {
if ( _showNewConversationForNonContactButton == showNewConversationForNonContactButton ) {
return ;
- ( BOOL ) checkIsNonContactPhoneNumberSignalUser : ( NSString * ) phoneNumber
{
DDLogWarn ( @ "isNonContactPhoneNumberSignalUser : %@", phoneNumber);
if ( [ self . phoneNumberAccountSet containsObject : phoneNumber ] ) {
return YES ;
}
_showNewConversationForNonContactButton = showNewConversationForNonContactButton ;
__weak MessageComposeTableViewController * weakSelf = self ;
[ [ ContactsUpdater sharedUpdater ] lookupIdentifier : phoneNumber
success : ^( SignalRecipient * recipient ) {
DDLogWarn ( @ "Lookup contact with recipient : %@ %@", recipient, phoneNumber);
MessageComposeTableViewController * strongSelf = weakSelf ;
if ( !strongSelf ) {
return ;
}
if ( !strongSelf . phoneNumberAccountSet ) {
strongSelf . phoneNumberAccountSet = [ NSMutableSet set ] ;
}
if ( ![ strongSelf . phoneNumberAccountSet containsObject : phoneNumber ] ) {
[ strongSelf . phoneNumberAccountSet addObject : phoneNumber ] ;
[ strongSelf . tableView reloadData ] ;
}
}
failure : ^( NSError * error ) {
DDLogWarn ( @ "Failed to lookup contact with error : %@ %@", error, phoneNumber);
} ] ;
return NO ;
/ / / / This dictionary is used to cache the set of known Signal
/ / / / accounts that correspond to "non - contact phone numbers . "
/ / @ property ( nonatomic ) NSMutableSet * phoneNumberAccountSet ;
}
- ( void ) setSearchPhoneNumber : ( NSString * ) searchPhoneNumber {
if ( [ _searchPhoneNumber isEqualToString : searchPhoneNumber ] ) {
return ;
}
_searchPhoneNumber = searchPhoneNumber ;
[ self . tableView reloadData ] ;
}
@ -371,7 +433,6 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
[ alertController addAction : cancelAction ] ;
[ alertController addAction : okAction ] ;
self . sendTextButton . hidden = YES ;
self . searchController . searchBar . text = @ "";
/ / must dismiss search controller before presenting alert .
@ -430,15 +491,30 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
}
- ( NSInteger ) tableView : ( UITableView * ) tableView numberOfRowsInSection : ( NSInteger ) section {
if ( section == MessageComposeTableViewControllerSectionInvite ) {
if ( floor ( NSFoundationVersionNumber ) < NSFoundationVersionNumber_iOS_9_0 ) {
/ / Invite flow not supported on iOS8
return 0 ;
}
return 1 ;
} else if ( section == MessageComposeTableViewControllerSectionNewConversationForNonContact ) {
return _showNewConversationForNonContactButton ? 1 : 0 ;
BOOL showNonContactConversation = NO ;
BOOL showInviteViaSMS = NO ;
BOOL showInviteFlow = NO ;
BOOL hasPhoneNumber = self . searchPhoneNumber . length > 0 ;
BOOL isKnownSignalUser = self . searchPhoneNumber && [ self . phoneNumberAccountSet containsObject : self . searchPhoneNumber ] ;
BOOL isInviteFlowSupported = floor ( NSFoundationVersionNumber ) > = NSFoundationVersionNumber_iOS_9_0 ;
if ( hasPhoneNumber && isKnownSignalUser ) {
showNonContactConversation = YES ;
} else if ( hasPhoneNumber ) {
showInviteViaSMS = YES ;
} else if ( isInviteFlowSupported ) {
showInviteFlow = YES ;
}
if ( section == MessageComposeTableViewControllerSectionInviteNonContactConversation ) {
return showNonContactConversation ? 1 : 0 ;
} else if ( section == MessageComposeTableViewControllerSectionInviteViaSMS ) {
return showInviteViaSMS ? 1 : 0 ;
} else if ( section == MessageComposeTableViewControllerSectionInviteFlow ) {
return showInviteFlow ? 1 : 0 ;
} else {
OWSAssert ( section == MessageComposeTableViewControllerSectionContacts )
if ( self . searchController . active ) {
return ( NSInteger ) [ self . searchResults count ] ;
} else {
@ -449,16 +525,23 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
- ( UITableViewCell * ) tableView : ( UITableView * ) tableView cellForRowAtIndexPath : ( NSIndexPath * ) indexPath
{
if ( indexPath . section == MessageComposeTableViewControllerSectionInvite ) {
return self . inviteCell ;
} else if ( indexPath . section == MessageComposeTableViewControllerSectionNewConversationForNonContact ) {
OWSAssert ( self . nonContactPhoneNumber . length > 0 ) ;
if ( indexPath . section == MessageComposeTableViewControllerSectionInviteNonContactConversation ) {
self . conversationForNonContactCell . textLabel . text = [ NSString stringWithFormat : NSLocalizedString ( @ "NEW_CONVERSATION_FOR_NON_CONTACT_FORMAT ",
@ "Text for button to start a new conversation with a non - contact ") ,
self . nonContact PhoneNumber] ;
self . searchPhoneNumber ] ;
return self . conversationForNonContactCell ;
} else if ( indexPath . section == MessageComposeTableViewControllerSectionInviteViaSMS ) {
/ / TODO : We should rework this string to be a format , to account for languages where the
/ / phone number should not appear at the end of the copy .
self . inviteViaSMSCell . textLabel . text = [ NSLocalizedString ( @ "SEND_SMS_BUTTON ",
@ "Text for button to send a Signal invite via SMS ")
stringByAppendingString : self . searchPhoneNumber ] ;
return self . inviteViaSMSCell ;
} else if ( indexPath . section == MessageComposeTableViewControllerSectionInviteFlow ) {
return self . inviteCell ;
} else {
OWSAssert ( indexPath . section == MessageComposeTableViewControllerSectionContacts )
ContactTableViewCell * cell = ( ContactTableViewCell * ) [ tableView
dequeueReusableCellWithIdentifier : MessageComposeTableViewControllerCellContact ] ;
@ -472,7 +555,18 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
- ( void ) tableView : ( UITableView * ) tableView didSelectRowAtIndexPath : ( NSIndexPath * ) indexPath {
if ( indexPath . section == MessageComposeTableViewControllerSectionInvite ) {
if ( indexPath . section == MessageComposeTableViewControllerSectionInviteNonContactConversation ) {
OWSAssert ( self . searchPhoneNumber . length > 0 ) ;
if ( self . searchPhoneNumber . length > 0 ) {
[ self dismissViewControllerAnimated : YES
completion : ^( ) {
[ Environment messageIdentifier : self . searchPhoneNumber withCompose : YES ] ;
} ] ;
}
} else if ( indexPath . section == MessageComposeTableViewControllerSectionInviteViaSMS ) {
[ self sendText ] ;
} else if ( indexPath . section == MessageComposeTableViewControllerSectionInviteFlow ) {
void ( ^showInvite ) ( ) = ^{
OWSInviteFlow * inviteFlow =
[ [ OWSInviteFlow alloc ] initWithPresentingViewController : self contactsManager : self . contactsManager ] ;
@ -489,15 +583,6 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
} else {
showInvite ( ) ;
}
} else if ( indexPath . section == MessageComposeTableViewControllerSectionNewConversationForNonContact ) {
OWSAssert ( self . nonContactPhoneNumber . length > 0 ) ;
if ( self . nonContactPhoneNumber . length > 0 ) {
[ self dismissViewControllerAnimated : YES
completion : ^( ) {
[ Environment messageIdentifier : self . nonContactPhoneNumber withCompose : YES ] ;
} ] ;
}
} else {
NSString * identifier = [ [ [ self contactForIndexPath : indexPath ] textSecureIdentifiers ] firstObject ] ;