Improve "new conversation" view.
* Add "search by phone number" to "no contacts" mode. * Coordinate "invite flow", "invite by SMS" and "new conversation with non-contact" to ensure only one (at most) is shown. * Show "new conversation with non-contact" IFF phone number is known to correspond to a signal account. // FREEBIE
This commit is contained in:
parent
3ae85ce2d8
commit
26b3be4ec5
|
@ -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.0f,
|
||||
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.nonContactPhoneNumber];
|
||||
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];
|
||||
|
||||
|
|
|
@ -244,9 +244,6 @@
|
|||
/* Generic server error */
|
||||
"ERROR_DESCRIPTION_SERVER_FAILURE" = "Server Error. Please try again later.";
|
||||
|
||||
/* Worst case generic error message */
|
||||
"ERROR_DESCRIPTION_UNKNOWN_ERROR" = "An unkown error occurred.";
|
||||
|
||||
/* Error message when attempting to send message */
|
||||
"ERROR_DESCRIPTION_UNREGISTERED_RECIPIENT" = "Contact is not a Signal user.";
|
||||
|
||||
|
@ -504,6 +501,9 @@
|
|||
/* No comment provided by engineer. */
|
||||
"NEW_GROUP_REQUEST_ADDPEOPLE" = "Add people";
|
||||
|
||||
/* Label for a button that lets users search for contacts by phone number */
|
||||
"NO_CONTACTS_SEARCH_BY_PHONE_NUMBER" = "Find Contacts by Phone Number";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"NOTIFICATION_SEND_FAILED" = "Your message failed to send to %@.";
|
||||
|
||||
|
|
Loading…
Reference in New Issue