From 16c646a9397e5517590d5e5e5353b07d5500b781 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 7 Aug 2017 15:46:18 -0400 Subject: [PATCH] Use profile name/image when available. Refactored the ContactAvatarBuilder to clarify intent of the two methods. One is only used for non-signal contacts in the Invite flow. The other should be used for any signal contacts so we have a single consistent way of generating the avatar initials. // FREEBIE --- Signal/src/Models/OWSAvatarBuilder.m | 3 +- Signal/src/Models/OWSContactAvatarBuilder.h | 23 +++++++---- Signal/src/Models/OWSContactAvatarBuilder.m | 25 +++++++++--- Signal/src/contact/OWSContactsManager.m | 45 ++++++++++++++++++--- Signal/src/views/ContactCell.swift | 9 +++-- Signal/src/views/ContactTableViewCell.m | 22 ++-------- 6 files changed, 84 insertions(+), 43 deletions(-) diff --git a/Signal/src/Models/OWSAvatarBuilder.m b/Signal/src/Models/OWSAvatarBuilder.m index 143ddcec9..90cc608d5 100644 --- a/Signal/src/Models/OWSAvatarBuilder.m +++ b/Signal/src/Models/OWSAvatarBuilder.m @@ -20,7 +20,8 @@ NS_ASSUME_NONNULL_BEGIN { OWSAvatarBuilder *avatarBuilder; if ([thread isKindOfClass:[TSContactThread class]]) { - avatarBuilder = [[OWSContactAvatarBuilder alloc] initWithThread:(TSContactThread *)thread contactsManager:contactsManager diameter:diameter]; + TSContactThread *contactThread = (TSContactThread *)thread; + avatarBuilder = [[OWSContactAvatarBuilder alloc] initWithSignalId:contactThread.contactIdentifier diameter:diameter contactsManager:contactsManager]; } else if ([thread isKindOfClass:[TSGroupThread class]]) { avatarBuilder = [[OWSGroupAvatarBuilder alloc] initWithThread:(TSGroupThread *)thread]; } else { diff --git a/Signal/src/Models/OWSContactAvatarBuilder.h b/Signal/src/Models/OWSContactAvatarBuilder.h index febc7966a..ad806da15 100644 --- a/Signal/src/Models/OWSContactAvatarBuilder.h +++ b/Signal/src/Models/OWSContactAvatarBuilder.h @@ -11,14 +11,21 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSContactAvatarBuilder : OWSAvatarBuilder -- (instancetype)initWithContactId:(NSString *)contactId - name:(NSString *)name - contactsManager:(OWSContactsManager *)contactsManager - diameter:(NSUInteger)diameter; - -- (instancetype)initWithThread:(TSContactThread *)thread - contactsManager:(OWSContactsManager *)contactsManager - diameter:(NSUInteger)diameter; +/** + * Build an avatar for a Signal recipient + */ +- (instancetype)initWithSignalId:(NSString *)signalId + diameter:(NSUInteger)diameter + contactsManager:(OWSContactsManager *)contactsManager; + +/** + * Build an avatar for a non-Signal recipient + */ +- (instancetype)initWithNonSignalName:(NSString *)nonSignalName + colorSeed:(NSString *)colorSeed + diameter:(NSUInteger)diameter + contactsManager:(OWSContactsManager *)contactsManager; + @end diff --git a/Signal/src/Models/OWSContactAvatarBuilder.m b/Signal/src/Models/OWSContactAvatarBuilder.m index 9031465b7..b0177ce0d 100644 --- a/Signal/src/Models/OWSContactAvatarBuilder.m +++ b/Signal/src/Models/OWSContactAvatarBuilder.m @@ -24,10 +24,12 @@ NS_ASSUME_NONNULL_BEGIN @implementation OWSContactAvatarBuilder +#pragma mark - Initializers + - (instancetype)initWithContactId:(NSString *)contactId name:(NSString *)name - contactsManager:(OWSContactsManager *)contactsManager diameter:(NSUInteger)diameter + contactsManager:(OWSContactsManager *)contactsManager { self = [super init]; if (!self) { @@ -36,19 +38,30 @@ NS_ASSUME_NONNULL_BEGIN _signalId = contactId; _contactName = name; - _contactsManager = contactsManager; _diameter = diameter; + _contactsManager = contactsManager; return self; } -- (instancetype)initWithThread:(TSContactThread *)thread - contactsManager:(OWSContactsManager *)contactsManager - diameter:(NSUInteger)diameter +- (instancetype)initWithSignalId:(NSString *)signalId + diameter:(NSUInteger)diameter + contactsManager:(OWSContactsManager *)contactsManager { - return [self initWithContactId:thread.contactIdentifier name:thread.name contactsManager:contactsManager diameter:diameter]; + NSString *name = [contactsManager displayNameForPhoneIdentifier:signalId]; + return [self initWithContactId:signalId name:name diameter:diameter contactsManager:contactsManager]; } +- (instancetype)initWithNonSignalName:(NSString *)nonSignalName + colorSeed:(NSString *)colorSeed + diameter:(NSUInteger)diameter + contactsManager:(OWSContactsManager *)contactsManager +{ + return [self initWithContactId:colorSeed name:nonSignalName diameter:diameter contactsManager:contactsManager]; +} + +#pragma mark - Instance methods + - (nullable UIImage *)buildSavedImage { return [self.contactsManager imageForPhoneIdentifier:self.signalId]; diff --git a/Signal/src/contact/OWSContactsManager.m b/Signal/src/contact/OWSContactsManager.m index ae7b641a1..5a6d6e3fe 100644 --- a/Signal/src/contact/OWSContactsManager.m +++ b/Signal/src/contact/OWSContactsManager.m @@ -206,13 +206,19 @@ NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_Account self.signalAccountMap = [signalAccountMap copy]; self.signalAccounts = [signalAccounts copy]; - [OWSProfileManager.sharedManager setContactRecipientIds:signalAccountMap.allKeys]; + [self.profileManager setContactRecipientIds:signalAccountMap.allKeys]; [self updateCachedDisplayNames]; }); }); } +// TODO dependency inject, avoid circular dependencies. +- (OWSProfileManager *)profileManager +{ + return [OWSProfileManager sharedManager]; +} + - (void)updateCachedDisplayNames { OWSAssert([NSThread isMainThread]); @@ -405,10 +411,19 @@ NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_Account return self.unknownContactName; } - NSString *displayName = [self cachedDisplayNameForRecipientId:recipientId]; + // 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; } + return displayName; } @@ -475,9 +490,19 @@ NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_Account [formattedName appendAttributedString:[[NSAttributedString alloc] initWithString:cachedLastName attributes:lastNameAttributes]]; } else { - return [[NSAttributedString alloc] - initWithString:[PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:recipientId] - attributes:normalFontAttributes]; + + // 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]]; } SignalAccount *signalAccount = [self signalAccountForRecipientId:recipientId]; @@ -520,7 +545,15 @@ NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_Account - (UIImage * _Nullable)imageForPhoneIdentifier:(NSString * _Nullable)identifier { Contact *contact = self.allContactsMap[identifier]; - return contact.image; + // Prefer the contact image from the local address book if available + UIImage *_Nullable image = contact.image; + + // Else try to use the image from their profile + if (image == nil) { + image = [self.profileManager profileAvatarForRecipientId:identifier]; + } + + return image; } #pragma mark - Logging diff --git a/Signal/src/views/ContactCell.swift b/Signal/src/views/ContactCell.swift index eb467936b..e424657e2 100644 --- a/Signal/src/views/ContactCell.swift +++ b/Signal/src/views/ContactCell.swift @@ -61,10 +61,11 @@ class ContactCell: UITableViewCell { } let kAvatarWidth: UInt = 40 - let avatarBuilder = OWSContactAvatarBuilder(contactId:contactIdForDeterminingBackgroundColor, - name:contact.fullName, - contactsManager:contactsManager, - diameter: kAvatarWidth) + let avatarBuilder = OWSContactAvatarBuilder(nonSignalName: contact.fullName, + colorSeed: contactIdForDeterminingBackgroundColor, + diameter: kAvatarWidth, + contactsManager:contactsManager) + self.contactImageView?.image = avatarBuilder.buildDefaultImage() } else { self.contactImageView?.image = contact.image diff --git a/Signal/src/views/ContactTableViewCell.m b/Signal/src/views/ContactTableViewCell.m index 16e92da3e..6a44848f1 100644 --- a/Signal/src/views/ContactTableViewCell.m +++ b/Signal/src/views/ContactTableViewCell.m @@ -81,26 +81,13 @@ const NSUInteger kContactTableViewCellAvatarSize = 40; - (void)configureWithSignalAccount:(SignalAccount *)signalAccount contactsManager:(OWSContactsManager *)contactsManager { [self configureWithRecipientId:signalAccount.recipientId - avatarName:signalAccount.contact.fullName - displayName:[contactsManager formattedDisplayNameForSignalAccount:signalAccount - font:self.nameLabel.font] contactsManager:contactsManager]; } -- (void)configureWithRecipientId:(NSString *)recipientId contactsManager:(OWSContactsManager *)contactsManager -{ - [self - configureWithRecipientId:recipientId - avatarName:@"" - displayName:[contactsManager formattedFullNameForRecipientId:recipientId font:self.nameLabel.font] - contactsManager:contactsManager]; -} - - (void)configureWithRecipientId:(NSString *)recipientId - avatarName:(NSString *)avatarName - displayName:(NSAttributedString *)displayName contactsManager:(OWSContactsManager *)contactsManager { + NSAttributedString *displayName = [contactsManager formattedFullNameForRecipientId:recipientId font:self.nameLabel.font]; NSMutableAttributedString *attributedText = [displayName mutableCopy]; if (self.accessoryMessage) { UILabel *blockedLabel = [[UILabel alloc] init]; @@ -113,10 +100,9 @@ const NSUInteger kContactTableViewCellAvatarSize = 40; self.accessoryView = blockedLabel; } self.nameLabel.attributedText = attributedText; - self.avatarView.image = [[[OWSContactAvatarBuilder alloc] initWithContactId:recipientId - name:avatarName - contactsManager:contactsManager - diameter:kContactTableViewCellAvatarSize] build]; + self.avatarView.image = [[[OWSContactAvatarBuilder alloc] initWithSignalId:recipientId + diameter:kContactTableViewCellAvatarSize + contactsManager:contactsManager] build]; // Force layout, since imageView isn't being initally rendered on App Store optimized build. [self layoutSubviews];