Less confusing "#" avatar for unknown Contact instead of "+"
For consistency with the Android and Desktop client behavior. * Show a placeholder avatar when no image, initials (#1512) If all we know about the user is their phone number, their avatar image is rendered as a placeholder. Previously, it would render the first few characters of their phone number as if they were initials (eg. "+") * Rename, extend OWSContactsManager methods (#1512) Rename from: nameStringForPhoneIdentifier to: displayNameForPhoneIdentifier Also, add: - (BOOL)nameExistsForPhoneIdentifier:(NSString *)identifier; Which reports whether there's any "name" for a contact. * Remove unused typedefs These aren't used in the project anymore, and they were causing compiling warnings due to a lack of nullability indication. * Resolve some OWSContactsManager nullability warnings Did a pass through all of the existing nullability warnings in OWSContactsManager. Tried to pick descriptors that best reflected the behavior of the methods. // FREEBIE
This commit is contained in:
parent
fb508470d9
commit
e7126f8c60
|
@ -132,7 +132,7 @@ EXTERNAL SOURCES:
|
|||
|
||||
CHECKOUT OPTIONS:
|
||||
SignalServiceKit:
|
||||
:commit: 34ffce89f59356ab23f290866b1c3437f03312ce
|
||||
:commit: 71250281596cdd6a03072d1b4a23aea8ce490eeb
|
||||
:git: https://github.com/WhisperSystems/SignalServiceKit.git
|
||||
SocketRocket:
|
||||
:commit: 41b57bb2fc292a814f758441a05243eb38457027
|
||||
|
|
|
@ -57,8 +57,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
NSMutableString *initials = [NSMutableString string];
|
||||
|
||||
if (self.contactName.length > 0) {
|
||||
BOOL contactHasName = [self.contactsManager nameExistsForPhoneIdentifier:self.signalId];
|
||||
if (contactHasName) {
|
||||
// Make an image from the contact's initials
|
||||
NSArray *words =
|
||||
[self.contactName componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
for (NSString *word in words) {
|
||||
|
@ -67,19 +68,20 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[initials appendString:[firstLetter uppercaseString]];
|
||||
}
|
||||
}
|
||||
|
||||
NSRange stringRange = { 0, MIN([initials length], (NSUInteger)3) }; // Rendering max 3 letters.
|
||||
initials = [[initials substringWithRange:stringRange] mutableCopy];
|
||||
} else {
|
||||
// We don't have a name for this contact, so we can't make an "initials" image
|
||||
[initials appendString:@"#"];
|
||||
}
|
||||
|
||||
NSRange stringRange = { 0, MIN([initials length], (NSUInteger)3) }; // Rendering max 3 letters.
|
||||
initials = [[initials substringWithRange:stringRange] mutableCopy];
|
||||
|
||||
|
||||
UIColor *backgroundColor = [UIColor backgroundColorForContact:self.signalId];
|
||||
|
||||
UIImage *image = [[JSQMessagesAvatarImageFactory avatarImageWithUserInitials:initials
|
||||
backgroundColor:backgroundColor
|
||||
textColor:[UIColor whiteColor]
|
||||
font:[UIFont ows_boldFontWithSize:36.0]
|
||||
diameter:100] avatarImage];
|
||||
|
||||
[self.contactsManager.avatarCache setObject:image forKey:self.signalId];
|
||||
return image;
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
if ([interaction isKindOfClass:[TSIncomingMessage class]]) {
|
||||
NSString *contactId = ((TSContactThread *)thread).contactIdentifier;
|
||||
adapter.senderId = contactId;
|
||||
adapter.senderDisplayName = [contactsManager nameStringForPhoneIdentifier:contactId];
|
||||
adapter.senderDisplayName = [contactsManager displayNameForPhoneIdentifier:contactId];
|
||||
adapter.messageType = TSIncomingMessageAdapter;
|
||||
} else {
|
||||
adapter.senderId = ME_MESSAGE_IDENTIFIER;
|
||||
|
@ -111,7 +111,7 @@
|
|||
if ([interaction isKindOfClass:[TSIncomingMessage class]]) {
|
||||
TSIncomingMessage *message = (TSIncomingMessage *)interaction;
|
||||
adapter.senderId = message.authorId;
|
||||
adapter.senderDisplayName = [contactsManager nameStringForPhoneIdentifier:message.authorId];
|
||||
adapter.senderDisplayName = [contactsManager displayNameForPhoneIdentifier:message.authorId];
|
||||
adapter.messageType = TSIncomingMessageAdapter;
|
||||
} else {
|
||||
adapter.senderId = ME_MESSAGE_IDENTIFIER;
|
||||
|
|
|
@ -12,28 +12,26 @@
|
|||
|
||||
#define SIGNAL_LIST_UPDATED @"Signal_AB_UPDATED"
|
||||
|
||||
typedef void (^ABAccessRequestCompletionBlock)(BOOL hasAccess);
|
||||
typedef void (^ABReloadRequestCompletionBlock)(NSArray *contacts);
|
||||
|
||||
@interface OWSContactsManager : NSObject <ContactsManagerProtocol>
|
||||
|
||||
@property CNContactStore *contactStore;
|
||||
@property NSCache<NSString *, UIImage *> *avatarCache;
|
||||
@property CNContactStore * _Nullable contactStore;
|
||||
@property NSCache<NSString *, UIImage *> * _Nonnull avatarCache;
|
||||
|
||||
- (ObservableValue *)getObservableContacts;
|
||||
- (ObservableValue * _Nonnull)getObservableContacts;
|
||||
|
||||
- (NSArray *)getContactsFromAddressBook:(ABAddressBookRef)addressBook;
|
||||
- (Contact *)latestContactForPhoneNumber:(PhoneNumber *)phoneNumber;
|
||||
- (NSArray * _Nonnull)getContactsFromAddressBook:(ABAddressBookRef _Nonnull)addressBook;
|
||||
- (Contact * _Nullable)latestContactForPhoneNumber:(PhoneNumber * _Nullable)phoneNumber;
|
||||
|
||||
- (void)verifyABPermission;
|
||||
|
||||
- (NSArray<Contact *> *)allContacts;
|
||||
- (NSArray<Contact *> *)signalContacts;
|
||||
- (NSArray<Contact *> * _Nonnull)allContacts;
|
||||
- (NSArray<Contact *> * _Nonnull)signalContacts;
|
||||
|
||||
- (void)doAfterEnvironmentInitSetup;
|
||||
|
||||
- (NSString *)nameStringForPhoneIdentifier:(NSString *)identifier;
|
||||
- (UIImage *)imageForPhoneIdentifier:(NSString *)identifier;
|
||||
- (NSString * _Nonnull)displayNameForPhoneIdentifier:(NSString * _Nullable)identifier;
|
||||
- (BOOL)nameExistsForPhoneIdentifier:(NSString * _Nullable)identifier;
|
||||
- (UIImage * _Nullable)imageForPhoneIdentifier:(NSString * _Nullable)identifier;
|
||||
|
||||
+ (NSComparator)contactComparator;
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
|
|||
return futureAddressBookSource.future;
|
||||
}
|
||||
|
||||
- (NSArray *)getContactsFromAddressBook:(ABAddressBookRef)addressBook {
|
||||
- (NSArray *)getContactsFromAddressBook:(ABAddressBookRef _Nonnull)addressBook {
|
||||
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
|
||||
CFMutableArrayRef allPeopleMutable =
|
||||
CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(allPeople), allPeople);
|
||||
|
@ -290,7 +290,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
|
|||
andContactID:recordID];
|
||||
}
|
||||
|
||||
- (Contact *)latestContactForPhoneNumber:(PhoneNumber *)phoneNumber {
|
||||
- (Contact * _Nullable)latestContactForPhoneNumber:(PhoneNumber *)phoneNumber {
|
||||
NSArray *allContacts = [self allContacts];
|
||||
|
||||
ContactSearchBlock searchBlock = ^BOOL(Contact *contact, NSUInteger idx, BOOL *stop) {
|
||||
|
@ -361,7 +361,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
|
|||
}
|
||||
|
||||
|
||||
+ (BOOL)name:(NSString *)nameString matchesQuery:(NSString *)queryString {
|
||||
+ (BOOL)name:(NSString * _Nonnull)nameString matchesQuery:(NSString * _Nonnull)queryString {
|
||||
NSCharacterSet *whitespaceSet = NSCharacterSet.whitespaceCharacterSet;
|
||||
NSArray *queryStrings = [queryString componentsSeparatedByCharactersInSet:whitespaceSet];
|
||||
NSArray *nameStrings = [nameString componentsSeparatedByCharactersInSet:whitespaceSet];
|
||||
|
@ -395,36 +395,57 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
|
|||
return [Contact comparatorSortingNamesByFirstThenLast:firstNameOrdering];
|
||||
}
|
||||
|
||||
- (NSArray<Contact *> *)signalContacts {
|
||||
- (NSArray<Contact *> * _Nonnull)signalContacts {
|
||||
return [self getSignalUsersFromContactsArray:[self allContacts]];
|
||||
}
|
||||
|
||||
- (NSString *)nameStringForPhoneIdentifier:(NSString *)identifier {
|
||||
- (NSString * _Nonnull)displayNameForPhoneIdentifier:(NSString * _Nullable)identifier {
|
||||
if (!identifier) {
|
||||
return NSLocalizedString(@"UNKNOWN_CONTACT_NAME",
|
||||
@"Displayed if for some reason we can't determine a contacts phone number *or* name");
|
||||
}
|
||||
for (Contact *contact in self.allContacts) {
|
||||
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
|
||||
if ([phoneNumber.toE164 isEqualToString:identifier]) {
|
||||
return contact.fullName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return identifier;
|
||||
Contact *contact = [self contactForPhoneIdentifier:identifier];
|
||||
|
||||
NSString *displayName = (contact.fullName.length > 0) ? contact.fullName : identifier;
|
||||
|
||||
return displayName;
|
||||
}
|
||||
|
||||
- (UIImage *)imageForPhoneIdentifier:(NSString *)identifier {
|
||||
- (BOOL)nameExistsForPhoneIdentifier:(NSString * _Nullable)identifier {
|
||||
Contact *contact = [self contactForPhoneIdentifier:identifier];
|
||||
NSString *name = contact.fullName;
|
||||
|
||||
if (name.length <= 0) return NO;
|
||||
|
||||
// OWSContactsManager::contactForRecord will use the first phone number as a name
|
||||
// in absense of a name or business name during import. Make sure that's not happening here.
|
||||
if ((contact.userTextPhoneNumbers.count > 0) && ([contact.userTextPhoneNumbers[0] isEqualToString:name])) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (Contact * _Nullable)contactForPhoneIdentifier:(NSString * _Nullable)identifier {
|
||||
if (!identifier) {
|
||||
return nil;
|
||||
}
|
||||
for (Contact *contact in self.allContacts) {
|
||||
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
|
||||
if ([phoneNumber.toE164 isEqualToString:identifier]) {
|
||||
return contact.image;
|
||||
return contact;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (UIImage * _Nullable)imageForPhoneIdentifier:(NSString * _Nullable)identifier {
|
||||
Contact *contact = [self contactForPhoneIdentifier:identifier];
|
||||
|
||||
return contact.image;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
@{Signal_Thread_UserInfo_Key : thread.uniqueId, Signal_Message_UserInfo_Key : message.uniqueId};
|
||||
|
||||
if ([thread isGroupThread]) {
|
||||
NSString *sender = [self.contactsManager nameStringForPhoneIdentifier:message.authorId];
|
||||
NSString *sender = [self.contactsManager displayNameForPhoneIdentifier:message.authorId];
|
||||
NSString *threadName = [NSString stringWithFormat:@"\"%@\"", name];
|
||||
notification.alertBody =
|
||||
[NSString stringWithFormat:NSLocalizedString(@"APN_MESSAGE_IN_GROUP_DETAILED", nil),
|
||||
|
|
|
@ -111,7 +111,7 @@
|
|||
UILocalNotification *notification = [[UILocalNotification alloc] init];
|
||||
|
||||
NSString *callerId = call.initiatorNumber.toE164;
|
||||
NSString *displayName = [self.contactsManager nameStringForPhoneIdentifier:callerId];
|
||||
NSString *displayName = [self.contactsManager displayNameForPhoneIdentifier:callerId];
|
||||
PropertyListPreferences *prefs = [Environment preferences];
|
||||
|
||||
notification.alertBody = @"☎️ ";
|
||||
|
|
|
@ -1079,7 +1079,7 @@ typedef enum : NSUInteger {
|
|||
}
|
||||
} else if (message.messageType == TSIncomingMessageAdapter && [self.thread isKindOfClass:[TSGroupThread class]]) {
|
||||
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message.interaction;
|
||||
NSString *_Nonnull name = [self.contactsManager nameStringForPhoneIdentifier:incomingMessage.authorId];
|
||||
NSString *_Nonnull name = [self.contactsManager displayNameForPhoneIdentifier:incomingMessage.authorId];
|
||||
NSAttributedString *senderNameString = [[NSAttributedString alloc] initWithString:name];
|
||||
|
||||
return senderNameString;
|
||||
|
@ -1539,7 +1539,7 @@ typedef enum : NSUInteger {
|
|||
|
||||
- (void)tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)errorMessage
|
||||
{
|
||||
NSString *keyOwner = [self.contactsManager nameStringForPhoneIdentifier:errorMessage.theirSignalId];
|
||||
NSString *keyOwner = [self.contactsManager displayNameForPhoneIdentifier:errorMessage.theirSignalId];
|
||||
NSString *titleFormat = NSLocalizedString(@"SAFETY_NUMBERS_ACTIONSHEET_TITLE", @"Action sheet heading");
|
||||
NSString *titleText = [NSString stringWithFormat:titleFormat, keyOwner];
|
||||
|
||||
|
@ -1601,7 +1601,7 @@ typedef enum : NSUInteger {
|
|||
}
|
||||
OWSFingerprint *fingerprint = (OWSFingerprint *)sender;
|
||||
|
||||
NSString *contactName = [self.contactsManager nameStringForPhoneIdentifier:fingerprint.theirStableId];
|
||||
NSString *contactName = [self.contactsManager displayNameForPhoneIdentifier:fingerprint.theirStableId];
|
||||
[vc configureWithThread:self.thread fingerprint:fingerprint contactName:contactName];
|
||||
} else if ([segue.destinationViewController isKindOfClass:[OWSConversationSettingsTableViewController class]]) {
|
||||
OWSConversationSettingsTableViewController *controller
|
||||
|
|
Loading…
Reference in New Issue