diff --git a/Signal/src/ViewControllers/ConversationView/MessagesViewController.m b/Signal/src/ViewControllers/ConversationView/MessagesViewController.m index 1930f91f1..41322b8d9 100644 --- a/Signal/src/ViewControllers/ConversationView/MessagesViewController.m +++ b/Signal/src/ViewControllers/ConversationView/MessagesViewController.m @@ -1932,10 +1932,7 @@ typedef enum : NSUInteger { } } else if (message.messageType == TSIncomingMessageAdapter && [self.thread isKindOfClass:[TSGroupThread class]]) { TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message.interaction; - NSString *_Nonnull name = [self.contactsManager displayNameForPhoneIdentifier:incomingMessage.authorId]; - NSAttributedString *senderNameString = [[NSAttributedString alloc] initWithString:name]; - - return senderNameString; + return [self.contactsManager attributedStringForMessageFooterWithPhoneIdentifier:incomingMessage.authorId]; } return nil; diff --git a/Signal/src/contact/OWSContactsManager.h b/Signal/src/contact/OWSContactsManager.h index ffcbc2df3..4d5e4848e 100644 --- a/Signal/src/contact/OWSContactsManager.h +++ b/Signal/src/contact/OWSContactsManager.h @@ -51,11 +51,14 @@ extern NSString *const OWSContactsManagerSignalAccountsDidChangeNotification; #pragma mark - Util +- (BOOL)hasNameInSystemContactsForRecipientId:(NSString *)recipientId; - (NSString *)displayNameForPhoneIdentifier:(nullable NSString *)identifier; - (NSString *)displayNameForSignalAccount:(SignalAccount *)signalAccount; +- (nullable NSString *)formattedProfileNameForRecipientId:(NSString *)recipientId; - (nullable UIImage *)imageForPhoneIdentifier:(nullable NSString *)identifier; - (NSAttributedString *)formattedDisplayNameForSignalAccount:(SignalAccount *)signalAccount font:(UIFont *_Nonnull)font; - (NSAttributedString *)formattedFullNameForRecipientId:(NSString *)recipientId font:(UIFont *)font; +- (NSAttributedString *)attributedStringForMessageFooterWithPhoneIdentifier:(NSString *)recipientId; @end diff --git a/Signal/src/contact/OWSContactsManager.m b/Signal/src/contact/OWSContactsManager.m index 5a6d6e3fe..1e88e1ae0 100644 --- a/Signal/src/contact/OWSContactsManager.m +++ b/Signal/src/contact/OWSContactsManager.m @@ -399,12 +399,26 @@ NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_Account #pragma mark - Whisper User Management +- (BOOL)hasNameInSystemContactsForRecipientId:(NSString *)recipientId +{ + return [self cachedDisplayNameForRecipientId:recipientId] != nil; +} + - (NSString *)unknownContactName { return NSLocalizedString(@"UNKNOWN_CONTACT_NAME", @"Displayed if for some reason we can't determine a contacts phone number *or* name"); } +- (nullable NSString *)formattedProfileNameForRecipientId:(NSString *)recipientId +{ + NSString *_Nullable profileName = [self.profileManager profileNameForRecipientId:recipientId]; + if (profileName == nil) { + return nil; + } + return [@"~" stringByAppendingString:profileName]; +} + - (NSString *_Nonnull)displayNameForPhoneIdentifier:(NSString *_Nullable)recipientId { if (!recipientId) { @@ -414,11 +428,6 @@ NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_Account // Prefer a saved name from system contacts, if available NSString *_Nullable displayName = [self cachedDisplayNameForRecipientId:recipientId]; - // Else try to use their profile name - if (displayName.length < 1) { - displayName = [self.profileManager profileNameForRecipientId:recipientId]; - } - // Else fall back to just using their recipientId if (displayName.length < 1) { displayName = recipientId; @@ -490,21 +499,13 @@ NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_Account [formattedName appendAttributedString:[[NSAttributedString alloc] initWithString:cachedLastName attributes:lastNameAttributes]]; } else { - - // If there's no name saved in our contacts, try their profile. - // TODO we might want to format this specially. - NSString *_Nullable profileName = [self.profileManager profileNameForRecipientId:recipientId]; - if (profileName.length < 1) { - // Else, fall back to using just their recipientId - NSString *phoneString = [PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:recipientId]; - return [[NSAttributedString alloc] initWithString:phoneString - attributes:normalFontAttributes]; - } - - [formattedName appendAttributedString:[[NSAttributedString alloc] initWithString:profileName - attributes:lastNameAttributes]]; + // Else, fall back to using just their recipientId + NSString *phoneString = + [PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:recipientId]; + return [[NSAttributedString alloc] initWithString:phoneString attributes:normalFontAttributes]; } + // Append unique label for contacts with multiple Signal accounts SignalAccount *signalAccount = [self signalAccountForRecipientId:recipientId]; if (signalAccount && signalAccount.multipleAccountLabelText) { OWSAssert(signalAccount.multipleAccountLabelText.length > 0); @@ -521,6 +522,24 @@ NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_Account return formattedName; } +- (NSAttributedString *)attributedStringForMessageFooterWithPhoneIdentifier:(NSString *)recipientId +{ + // Prefer a saved name from system contacts, if available + NSString *_Nullable savedContactName = [self cachedDisplayNameForRecipientId:recipientId]; + if (savedContactName.length > 0) { + return [[NSAttributedString alloc] initWithString:savedContactName]; + } + + NSString *_Nullable formattedProfileName = [self formattedProfileNameForRecipientId:recipientId]; + if (formattedProfileName.length > 0) { + NSString *numberAndProfileName = [NSString stringWithFormat:@"%@ %@", recipientId, formattedProfileName]; + return [[NSAttributedString alloc] initWithString:numberAndProfileName]; + } + + // else fall back to recipient id + return [[NSAttributedString alloc] initWithString:recipientId]; +} + - (nullable SignalAccount *)signalAccountForRecipientId:(NSString *)recipientId { OWSAssert(recipientId.length > 0); diff --git a/Signal/src/views/ContactTableViewCell.m b/Signal/src/views/ContactTableViewCell.m index 6a44848f1..49ac81372 100644 --- a/Signal/src/views/ContactTableViewCell.m +++ b/Signal/src/views/ContactTableViewCell.m @@ -22,6 +22,7 @@ const NSUInteger kContactTableViewCellAvatarSize = 40; @interface ContactTableViewCell () @property (nonatomic) IBOutlet UILabel *nameLabel; +@property (nonatomic) IBOutlet UILabel *profileNameLabel; @property (nonatomic) IBOutlet UIImageView *avatarView; @property (nonatomic, nullable) UILabel *subtitle; @@ -60,19 +61,37 @@ const NSUInteger kContactTableViewCellAvatarSize = 40; _avatarView = [AvatarImageView new]; [self.contentView addSubview:_avatarView]; + UIView *nameContainerView = [UIView containerView]; + [self.contentView addSubview:nameContainerView]; + _nameLabel = [UILabel new]; _nameLabel.lineBreakMode = NSLineBreakByTruncatingTail; _nameLabel.font = [UIFont ows_dynamicTypeBodyFont]; - [self.contentView addSubview:_nameLabel]; + [nameContainerView addSubview:_nameLabel]; + + _profileNameLabel = [UILabel new]; + _profileNameLabel.lineBreakMode = NSLineBreakByTruncatingTail; + _profileNameLabel.font = [UIFont ows_footnoteFont]; + _profileNameLabel.textColor = [UIColor grayColor]; + [nameContainerView addSubview:_profileNameLabel]; [_avatarView autoVCenterInSuperview]; [_avatarView autoPinLeadingToSuperView]; [_avatarView autoSetDimension:ALDimensionWidth toSize:kContactTableViewCellAvatarSize]; [_avatarView autoSetDimension:ALDimensionHeight toSize:kContactTableViewCellAvatarSize]; - [_nameLabel autoVCenterInSuperview]; - [_nameLabel autoPinLeadingToTrailingOfView:_avatarView margin:12.f]; - [_nameLabel autoPinTrailingToSuperView]; + [_nameLabel autoPinEdgeToSuperviewEdge:ALEdgeTop]; + [_nameLabel autoPinWidthToSuperview]; + + // profileNameLabel can be zero sized, in which case nameLabel essentially occupies the totality of + // nameContainerView's frame. + [_profileNameLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:_nameLabel]; + [_profileNameLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom]; + [_profileNameLabel autoPinWidthToSuperview]; + + [nameContainerView autoVCenterInSuperview]; + [nameContainerView autoPinLeadingToTrailingOfView:_avatarView margin:12.f]; + [nameContainerView autoPinTrailingToSuperView]; // Force layout, since imageView isn't being initally rendered on App Store optimized build. [self layoutSubviews]; @@ -87,8 +106,17 @@ const NSUInteger kContactTableViewCellAvatarSize = 40; - (void)configureWithRecipientId:(NSString *)recipientId contactsManager:(OWSContactsManager *)contactsManager { - NSAttributedString *displayName = [contactsManager formattedFullNameForRecipientId:recipientId font:self.nameLabel.font]; - NSMutableAttributedString *attributedText = [displayName mutableCopy]; + self.nameLabel.attributedText = + [contactsManager formattedFullNameForRecipientId:recipientId font:self.nameLabel.font]; + + if ([contactsManager hasNameInSystemContactsForRecipientId:recipientId]) { + // Don't display profile name when we have a veritas name in system Contacts + self.profileNameLabel.text = nil; + } else { + // Use profile name, if any is available + self.profileNameLabel.text = [contactsManager formattedProfileNameForRecipientId:recipientId]; + } + if (self.accessoryMessage) { UILabel *blockedLabel = [[UILabel alloc] init]; blockedLabel.textAlignment = NSTextAlignmentRight; @@ -99,7 +127,7 @@ const NSUInteger kContactTableViewCellAvatarSize = 40; self.accessoryView = blockedLabel; } - self.nameLabel.attributedText = attributedText; + self.avatarView.image = [[[OWSContactAvatarBuilder alloc] initWithSignalId:recipientId diameter:kContactTableViewCellAvatarSize contactsManager:contactsManager] build];