From dd3394be1bd86d074cb5e6a12f21f0b06424d442 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 19 May 2017 16:30:43 -0400 Subject: [PATCH 1/5] Cache display names for accounts. // FREEBIE --- Podfile | 4 +- Podfile.lock | 9 ++-- Signal/src/contact/OWSContactsManager.m | 51 +++++++++++++++++-- Signal/src/environment/NotificationsManager.m | 24 +++++---- .../translations/en.lproj/Localizable.strings | 3 ++ 5 files changed, 69 insertions(+), 22 deletions(-) diff --git a/Podfile b/Podfile index 55d7dcd08..624665393 100644 --- a/Podfile +++ b/Podfile @@ -5,8 +5,8 @@ target 'Signal' do pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git' pod 'AxolotlKit', git: 'https://github.com/WhisperSystems/SignalProtocolKit.git' #pod 'AxolotlKit', path: '../SignalProtocolKit' - pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git' - #pod 'SignalServiceKit', path: '../SignalServiceKit' + #pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git' + pod 'SignalServiceKit', path: '../SignalServiceKit' pod 'OpenSSL' pod 'JSQMessagesViewController', git: 'https://github.com/WhisperSystems/JSQMessagesViewController.git', branch: 'mkirk/position-edit-menu' #pod 'JSQMessagesViewController' path: '../JSQMessagesViewController' diff --git a/Podfile.lock b/Podfile.lock index a8256922a..4b8ae2071 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -114,7 +114,7 @@ DEPENDENCIES: - OpenSSL - PureLayout - Reachability - - SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`) + - SignalServiceKit (from `../SignalServiceKit`) - SocketRocket (from `https://github.com/facebook/SocketRocket.git`) EXTERNAL SOURCES: @@ -124,7 +124,7 @@ EXTERNAL SOURCES: :branch: mkirk/position-edit-menu :git: https://github.com/WhisperSystems/JSQMessagesViewController.git SignalServiceKit: - :git: https://github.com/WhisperSystems/SignalServiceKit.git + :path: ../SignalServiceKit SocketRocket: :git: https://github.com/facebook/SocketRocket.git @@ -135,9 +135,6 @@ CHECKOUT OPTIONS: JSQMessagesViewController: :commit: 7054e4b13ee5bcd6d524adb6dc9a726e8c466308 :git: https://github.com/WhisperSystems/JSQMessagesViewController.git - SignalServiceKit: - :commit: 33a2b05dcaa0ccfe94db12f0e7f0cdf6e861bddf - :git: https://github.com/WhisperSystems/SignalServiceKit.git SocketRocket: :commit: 877ac7438be3ad0b45ef5ca3969574e4b97112bf :git: https://github.com/facebook/SocketRocket.git @@ -164,6 +161,6 @@ SPEC CHECKSUMS: UnionFind: c33be5adb12983981d6e827ea94fc7f9e370f52d YapDatabase: cd911121580ff16675f65ad742a9eb0ab4d9e266 -PODFILE CHECKSUM: 48e80d7f1e049bbf544a689fdfdf33e8196c640a +PODFILE CHECKSUM: 6f9ef5d9fa17469569e127a9f5719dafa11631b9 COCOAPODS: 1.2.1 diff --git a/Signal/src/contact/OWSContactsManager.m b/Signal/src/contact/OWSContactsManager.m index 7636db0c3..0659a4672 100644 --- a/Signal/src/contact/OWSContactsManager.m +++ b/Signal/src/contact/OWSContactsManager.m @@ -9,12 +9,15 @@ #import #import #import +#import @import Contacts; NSString *const OWSContactsManagerSignalAccountsDidChangeNotification = @"OWSContactsManagerSignalAccountsDidChangeNotification"; +NSString *const kTSStorageManager_ContactNames = @"kTSStorageManager_ContactNames"; + @interface OWSContactsManager () @property (atomic) id addressBookReference; @@ -191,11 +194,44 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification = [[NSNotificationCenter defaultCenter] postNotificationName:OWSContactsManagerSignalAccountsDidChangeNotification object:nil]; + + [self updateCachedDisplayNames]; }); }); } +- (void)updateCachedDisplayNames +{ + OWSAssert([NSThread isMainThread]); + + NSMutableDictionary *accountNameMap = [NSMutableDictionary new]; + for (SignalAccount *signalAccount in self.signalAccounts) { + NSString *displayName = [self displayNameForSignalAccount:signalAccount]; + if (![displayName isEqualToString:signalAccount.recipientId]) { + accountNameMap[signalAccount.recipientId] = displayName; + } + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [TSStorageManager.sharedManager.newDatabaseConnection + readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { + for (NSString *recipientId in accountNameMap) { + NSString *displayName = accountNameMap[recipientId]; + [transaction setObject:displayName forKey:recipientId inCollection:kTSStorageManager_ContactNames]; + } + }]; + }); +} + +- (NSString *_Nullable)cachedDisplayNameForRecipientId:(NSString *)recipientId +{ + OWSAssert(recipientId.lastPathComponent > 0); + + return [[TSStorageManager sharedManager] objectForKey:recipientId inCollection:kTSStorageManager_ContactNames]; +} + #pragma mark - View Helpers + // TODO move into Contact class. + (NSString *)accountLabelForContact:(Contact *)contact recipientId:(NSString *)recipientId { @@ -245,16 +281,23 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification = @"Displayed if for some reason we can't determine a contacts phone number *or* name"); } -- (NSString * _Nonnull)displayNameForPhoneIdentifier:(NSString * _Nullable)identifier { - if (!identifier) { +- (NSString *_Nonnull)displayNameForPhoneIdentifier:(NSString *_Nullable)recipientId +{ + if (!recipientId) { return self.unknownContactName; } // When viewing an old thread with someone who is no longer a Signal user, they won't have a SignalAccount // so we get the name from `allContactsMap` as opposed to `signalAccountForRecipientId`. - Contact *contact = self.allContactsMap[identifier]; + Contact *contact = self.allContactsMap[recipientId]; - NSString *displayName = (contact.fullName.length > 0) ? contact.fullName : identifier; + NSString *displayName = contact.fullName; + if (displayName.length < 1) { + displayName = [self cachedDisplayNameForRecipientId:recipientId]; + } + if (displayName.length < 1) { + displayName = recipientId; + } return displayName; } diff --git a/Signal/src/environment/NotificationsManager.m b/Signal/src/environment/NotificationsManager.m index f7de7f453..08133db67 100644 --- a/Signal/src/environment/NotificationsManager.m +++ b/Signal/src/environment/NotificationsManager.m @@ -146,7 +146,6 @@ } - (void)notifyUserForIncomingMessage:(TSIncomingMessage *)message - from:(NSString *)name inThread:(TSThread *)thread contactsManager:(id)contactsManager { @@ -155,6 +154,11 @@ } NSString *messageDescription = message.description; + NSString *senderName = [contactsManager displayNameForPhoneIdentifier:message.authorId]; + NSString *groupName = [thread.name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + if (groupName.length < 1) { + groupName = NSLocalizedString(@"UNNAMED_GROUP_NAME", @"Named used for groups without a name."); + } if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive && messageDescription) { UILocalNotification *notification = [[UILocalNotification alloc] init]; @@ -167,25 +171,25 @@ @{Signal_Thread_UserInfo_Key : thread.uniqueId, Signal_Message_UserInfo_Key : message.uniqueId}; if ([thread isGroupThread]) { - NSString *sender = [contactsManager displayNameForPhoneIdentifier:message.authorId]; - NSString *threadName = [NSString stringWithFormat:@"\"%@\"", name]; + NSString *threadName = [NSString stringWithFormat:@"\"%@\"", groupName]; + // TODO: Format parameters might change order in l10n. We should use named parameters. notification.alertBody = [NSString stringWithFormat:NSLocalizedString(@"APN_MESSAGE_IN_GROUP_DETAILED", nil), - sender, - threadName, - messageDescription]; + senderName, + threadName, + messageDescription]; } else { - notification.alertBody = [NSString stringWithFormat:@"%@: %@", name, messageDescription]; + notification.alertBody = [NSString stringWithFormat:@"%@: %@", senderName, messageDescription]; } break; case NotificationNameNoPreview: { notification.userInfo = @{Signal_Thread_UserInfo_Key : thread.uniqueId}; if ([thread isGroupThread]) { - notification.alertBody = - [NSString stringWithFormat:@"%@ \"%@\"", NSLocalizedString(@"APN_MESSAGE_IN_GROUP", nil), name]; + notification.alertBody = [NSString + stringWithFormat:@"%@ \"%@\"", NSLocalizedString(@"APN_MESSAGE_IN_GROUP", nil), groupName]; } else { notification.alertBody = - [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"APN_MESSAGE_FROM", nil), name]; + [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"APN_MESSAGE_FROM", nil), senderName]; } break; } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 1d9df0ede..a4a3c10b6 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1297,6 +1297,9 @@ /* Label text in device manager for a device with no name */ "UNNAMED_DEVICE" = "Unnamed Device"; +/* Named used for groups without a name. */ +"UNNAMED_GROUP_NAME" = "Unnamed Group"; + /* No comment provided by engineer. */ "UNREGISTER_SIGNAL_FAIL" = "Failed to unregister from Signal."; From 86fb08307992957cb6342769e752fdf0d614745d Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 24 May 2017 11:06:46 -0400 Subject: [PATCH 2/5] Rationalize the attributed and unattributed display name formatting and caching. // FREEBIE --- Signal/src/contact/OWSContactsManager.m | 270 ++++++++++++++---------- 1 file changed, 159 insertions(+), 111 deletions(-) diff --git a/Signal/src/contact/OWSContactsManager.m b/Signal/src/contact/OWSContactsManager.m index 0659a4672..fd177a5c1 100644 --- a/Signal/src/contact/OWSContactsManager.m +++ b/Signal/src/contact/OWSContactsManager.m @@ -16,7 +16,9 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification = @"OWSContactsManagerSignalAccountsDidChangeNotification"; -NSString *const kTSStorageManager_ContactNames = @"kTSStorageManager_ContactNames"; +NSString *const kTSStorageManager_AccountDisplayNames = @"kTSStorageManager_AccountDisplayNames"; +NSString *const kTSStorageManager_AccountFirstNames = @"kTSStorageManager_AccountFirstNames"; +NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_AccountLastNames"; @interface OWSContactsManager () @@ -30,6 +32,11 @@ NSString *const kTSStorageManager_ContactNames = @"kTSStorageManager_ContactName @property (atomic) NSArray *signalAccounts; @property (atomic) NSDictionary *signalAccountMap; @property (nonatomic, readonly) SystemContactsFetcher *systemContactsFetcher; + +@property (atomic) NSDictionary *cachedAccountNameMap; +@property (atomic) NSDictionary *cachedFirstNameMap; +@property (atomic) NSDictionary *cachedLastNameMap; + @end @implementation OWSContactsManager @@ -49,6 +56,8 @@ NSString *const kTSStorageManager_ContactNames = @"kTSStorageManager_ContactName OWSSingletonAssert(); + [self loadCachedDisplayNames]; + return self; } @@ -191,11 +200,12 @@ NSString *const kTSStorageManager_ContactNames = @"kTSStorageManager_ContactName dispatch_async(dispatch_get_main_queue(), ^{ self.signalAccountMap = [signalAccountMap copy]; self.signalAccounts = [signalAccounts copy]; + + [self updateCachedDisplayNames]; + [[NSNotificationCenter defaultCenter] postNotificationName:OWSContactsManagerSignalAccountsDidChangeNotification object:nil]; - - [self updateCachedDisplayNames]; }); }); } @@ -204,22 +214,87 @@ NSString *const kTSStorageManager_ContactNames = @"kTSStorageManager_ContactName { OWSAssert([NSThread isMainThread]); - NSMutableDictionary *accountNameMap = [NSMutableDictionary new]; + NSMutableDictionary *cachedAccountNameMap = [NSMutableDictionary new]; + NSMutableDictionary *cachedFirstNameMap = [NSMutableDictionary new]; + NSMutableDictionary *cachedLastNameMap = [NSMutableDictionary new]; for (SignalAccount *signalAccount in self.signalAccounts) { - NSString *displayName = [self displayNameForSignalAccount:signalAccount]; + NSString *baseName + = (signalAccount.contact.fullName.length > 0 ? signalAccount.contact.fullName : signalAccount.recipientId); + OWSAssert(signalAccount.hasMultipleAccountContact == (signalAccount.multipleAccountLabelText != nil)); + NSString *displayName = (signalAccount.multipleAccountLabelText + ? [NSString stringWithFormat:@"%@ (%@)", baseName, signalAccount.multipleAccountLabelText] + : baseName); if (![displayName isEqualToString:signalAccount.recipientId]) { - accountNameMap[signalAccount.recipientId] = displayName; + cachedAccountNameMap[signalAccount.recipientId] = displayName; + } + + if (signalAccount.contact.firstName.length > 0) { + cachedFirstNameMap[signalAccount.recipientId] = signalAccount.contact.firstName; + } + if (signalAccount.contact.lastName.length > 0) { + cachedLastNameMap[signalAccount.recipientId] = signalAccount.contact.lastName; } } - dispatch_async(dispatch_get_main_queue(), ^{ - [TSStorageManager.sharedManager.newDatabaseConnection - readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - for (NSString *recipientId in accountNameMap) { - NSString *displayName = accountNameMap[recipientId]; - [transaction setObject:displayName forKey:recipientId inCollection:kTSStorageManager_ContactNames]; - } - }]; + self.cachedAccountNameMap = [cachedAccountNameMap copy]; + self.cachedFirstNameMap = [cachedFirstNameMap copy]; + self.cachedLastNameMap = [cachedLastNameMap copy]; + + // Write to database off the main thread. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [TSStorageManager.sharedManager.newDatabaseConnection readWriteWithBlock:^( + YapDatabaseReadWriteTransaction *_Nonnull transaction) { + for (NSString *recipientId in cachedAccountNameMap) { + NSString *displayName = cachedAccountNameMap[recipientId]; + [transaction setObject:displayName + forKey:recipientId + inCollection:kTSStorageManager_AccountDisplayNames]; + } + for (NSString *recipientId in cachedFirstNameMap) { + NSString *firstName = cachedFirstNameMap[recipientId]; + [transaction setObject:firstName forKey:recipientId inCollection:kTSStorageManager_AccountFirstNames]; + } + for (NSString *recipientId in cachedLastNameMap) { + NSString *lastName = cachedLastNameMap[recipientId]; + [transaction setObject:lastName forKey:recipientId inCollection:kTSStorageManager_AccountLastNames]; + } + }]; + }); +} + +- (void)loadCachedDisplayNames +{ + // Read from database off the main thread. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSMutableDictionary *cachedAccountNameMap = [NSMutableDictionary new]; + NSMutableDictionary *cachedFirstNameMap = [NSMutableDictionary new]; + NSMutableDictionary *cachedLastNameMap = [NSMutableDictionary new]; + + [TSStorageManager.sharedManager.newDatabaseConnection readWithBlock:^( + YapDatabaseReadTransaction *_Nonnull transaction) { + [transaction + enumerateKeysAndObjectsInCollection:kTSStorageManager_AccountDisplayNames + usingBlock:^( + NSString *_Nonnull key, NSString *_Nonnull object, BOOL *_Nonnull stop) { + cachedAccountNameMap[key] = object; + }]; + [transaction + enumerateKeysAndObjectsInCollection:kTSStorageManager_AccountFirstNames + usingBlock:^( + NSString *_Nonnull key, NSString *_Nonnull object, BOOL *_Nonnull stop) { + cachedFirstNameMap[key] = object; + }]; + [transaction + enumerateKeysAndObjectsInCollection:kTSStorageManager_AccountLastNames + usingBlock:^( + NSString *_Nonnull key, NSString *_Nonnull object, BOOL *_Nonnull stop) { + cachedLastNameMap[key] = object; + }]; + }]; + + self.cachedAccountNameMap = [cachedAccountNameMap copy]; + self.cachedFirstNameMap = [cachedFirstNameMap copy]; + self.cachedLastNameMap = [cachedLastNameMap copy]; }); } @@ -227,7 +302,21 @@ NSString *const kTSStorageManager_ContactNames = @"kTSStorageManager_ContactName { OWSAssert(recipientId.lastPathComponent > 0); - return [[TSStorageManager sharedManager] objectForKey:recipientId inCollection:kTSStorageManager_ContactNames]; + return self.cachedAccountNameMap[recipientId]; +} + +- (NSString *_Nullable)cachedFirstNameForRecipientId:(NSString *)recipientId +{ + OWSAssert(recipientId.lastPathComponent > 0); + + return self.cachedFirstNameMap[recipientId]; +} + +- (NSString *_Nullable)cachedLastNameForRecipientId:(NSString *)recipientId +{ + OWSAssert(recipientId.lastPathComponent > 0); + + return self.cachedLastNameMap[recipientId]; } #pragma mark - View Helpers @@ -287,28 +376,10 @@ NSString *const kTSStorageManager_ContactNames = @"kTSStorageManager_ContactName return self.unknownContactName; } - // When viewing an old thread with someone who is no longer a Signal user, they won't have a SignalAccount - // so we get the name from `allContactsMap` as opposed to `signalAccountForRecipientId`. - Contact *contact = self.allContactsMap[recipientId]; - - NSString *displayName = contact.fullName; - if (displayName.length < 1) { - displayName = [self cachedDisplayNameForRecipientId:recipientId]; - } + NSString *displayName = [self cachedDisplayNameForRecipientId:recipientId]; if (displayName.length < 1) { displayName = recipientId; } - - return displayName; -} - -// TODO move into Contact class. -- (NSString *_Nonnull)displayNameForContact:(Contact *)contact -{ - OWSAssert(contact); - - NSString *displayName = (contact.fullName.length > 0) ? contact.fullName : self.unknownContactName; - return displayName; } @@ -316,14 +387,7 @@ NSString *const kTSStorageManager_ContactNames = @"kTSStorageManager_ContactName { OWSAssert(signalAccount); - NSString *baseName = (signalAccount.contact ? [self displayNameForContact:signalAccount.contact] - : [self displayNameForPhoneIdentifier:signalAccount.recipientId]); - OWSAssert(signalAccount.hasMultipleAccountContact == (signalAccount.multipleAccountLabelText != nil)); - if (signalAccount.multipleAccountLabelText) { - return [NSString stringWithFormat:@"%@ (%@)", baseName, signalAccount.multipleAccountLabelText]; - } else { - return baseName; - } + return [self displayNameForPhoneIdentifier:signalAccount.recipientId]; } - (NSAttributedString *_Nonnull)formattedDisplayNameForSignalAccount:(SignalAccount *)signalAccount @@ -332,91 +396,75 @@ NSString *const kTSStorageManager_ContactNames = @"kTSStorageManager_ContactName OWSAssert(signalAccount); OWSAssert(font); - NSAttributedString *baseName = [self formattedFullNameForContact:signalAccount.contact font:font]; - - if (baseName.length == 0) { - baseName = [self formattedFullNameForRecipientId:signalAccount.recipientId font:font]; - } - - OWSAssert(signalAccount.hasMultipleAccountContact == (signalAccount.multipleAccountLabelText != nil)); - if (signalAccount.multipleAccountLabelText) { - NSMutableAttributedString *result = [NSMutableAttributedString new]; - [result appendAttributedString:baseName]; - [result appendAttributedString:[[NSAttributedString alloc] initWithString:@" (" - attributes:@{ - NSFontAttributeName : font, - }]]; - [result - appendAttributedString:[[NSAttributedString alloc] initWithString:signalAccount.multipleAccountLabelText]]; - [result appendAttributedString:[[NSAttributedString alloc] initWithString:@")" - attributes:@{ - NSFontAttributeName : font, - }]]; - return result; - } else { - return baseName; - } + return [self formattedFullNameForRecipientId:signalAccount.recipientId font:font]; } -// TODO move into Contact class. -- (NSAttributedString *_Nonnull)formattedFullNameForContact:(Contact *)contact font:(UIFont *_Nonnull)font +- (NSAttributedString *)formattedFullNameForRecipientId:(NSString *)recipientId font:(UIFont *)font { + OWSAssert(recipientId.length > 0); + OWSAssert(font); + UIFont *boldFont = [UIFont ows_mediumFontWithSize:font.pointSize]; NSDictionary *boldFontAttributes = @{ NSFontAttributeName : boldFont, NSForegroundColorAttributeName : [UIColor blackColor] }; - NSDictionary *normalFontAttributes = @{ NSFontAttributeName : font, NSForegroundColorAttributeName : [UIColor ows_darkGrayColor] }; - - NSAttributedString *_Nullable firstName, *_Nullable lastName; - if (ABPersonGetSortOrdering() == kABPersonSortByFirstName) { - if (contact.firstName) { - firstName = [[NSAttributedString alloc] initWithString:contact.firstName attributes:boldFontAttributes]; - } - if (contact.lastName) { - lastName = [[NSAttributedString alloc] initWithString:contact.lastName attributes:normalFontAttributes]; + NSDictionary *firstNameAttributes + = (ABPersonGetSortOrdering() == kABPersonSortByFirstName ? boldFontAttributes : normalFontAttributes); + NSDictionary *lastNameAttributes + = (ABPersonGetSortOrdering() == kABPersonSortByFirstName ? normalFontAttributes : boldFontAttributes); + + NSString *cachedFirstName = [self cachedFirstNameForRecipientId:recipientId]; + NSString *cachedLastName = [self cachedLastNameForRecipientId:recipientId]; + + NSMutableAttributedString *formattedName = [NSMutableAttributedString new]; + + if (cachedFirstName.length > 0 && cachedLastName.length > 0) { + NSAttributedString *firstName = + [[NSAttributedString alloc] initWithString:cachedFirstName attributes:firstNameAttributes]; + NSAttributedString *lastName = + [[NSAttributedString alloc] initWithString:cachedLastName attributes:lastNameAttributes]; + + NSAttributedString *_Nullable leftName, *_Nullable rightName; + if (ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatFirstNameFirst) { + leftName = firstName; + rightName = lastName; + } else { + leftName = lastName; + rightName = firstName; } - } else { - if (contact.firstName) { - firstName = [[NSAttributedString alloc] initWithString:contact.firstName attributes:normalFontAttributes]; - } - if (contact.lastName) { - lastName = [[NSAttributedString alloc] initWithString:contact.lastName attributes:boldFontAttributes]; - } - } - NSAttributedString *_Nullable leftName, *_Nullable rightName; - if (ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatFirstNameFirst) { - leftName = firstName; - rightName = lastName; + [formattedName appendAttributedString:leftName]; + [formattedName + appendAttributedString:[[NSAttributedString alloc] initWithString:@" " attributes:normalFontAttributes]]; + [formattedName appendAttributedString:rightName]; + } else if (cachedFirstName.length > 0) { + [formattedName appendAttributedString:[[NSAttributedString alloc] initWithString:cachedFirstName + attributes:firstNameAttributes]]; + } else if (cachedLastName.length > 0) { + [formattedName appendAttributedString:[[NSAttributedString alloc] initWithString:cachedLastName + attributes:lastNameAttributes]]; } else { - leftName = lastName; - rightName = firstName; + return [[NSAttributedString alloc] + initWithString:[PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:recipientId] + attributes:normalFontAttributes]; } - NSMutableAttributedString *fullNameString = [NSMutableAttributedString new]; - if (leftName.length > 0) { - [fullNameString appendAttributedString:leftName]; + SignalAccount *signalAccount = [self signalAccountForRecipientId:recipientId]; + if (signalAccount && signalAccount.multipleAccountLabelText) { + OWSAssert(signalAccount.multipleAccountLabelText.length > 0); + + [formattedName + appendAttributedString:[[NSAttributedString alloc] initWithString:@" (" attributes:normalFontAttributes]]; + [formattedName + appendAttributedString:[[NSAttributedString alloc] initWithString:signalAccount.multipleAccountLabelText + attributes:normalFontAttributes]]; + [formattedName + appendAttributedString:[[NSAttributedString alloc] initWithString:@")" attributes:normalFontAttributes]]; } - if (leftName.length > 0 && rightName.length > 0) { - [fullNameString appendAttributedString:[[NSAttributedString alloc] initWithString:@" "]]; - } - if (rightName.length > 0) { - [fullNameString appendAttributedString:rightName]; - } - - return fullNameString; -} - -- (NSAttributedString *)formattedFullNameForRecipientId:(NSString *)recipientId font:(UIFont *)font -{ - NSDictionary *normalFontAttributes = - @{ NSFontAttributeName : font, NSForegroundColorAttributeName : [UIColor ows_darkGrayColor] }; - return [[NSAttributedString alloc] - initWithString:[PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:recipientId] - attributes:normalFontAttributes]; + return formattedName; } - (nullable SignalAccount *)signalAccountForRecipientId:(NSString *)recipientId From 616041c0fbf86ac3eb2b496a159fc6e6b3964205 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 24 May 2017 11:13:09 -0400 Subject: [PATCH 3/5] Respond to CR. // FREEBIE --- Signal/src/environment/NotificationsManager.m | 2 +- Signal/translations/en.lproj/Localizable.strings | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Signal/src/environment/NotificationsManager.m b/Signal/src/environment/NotificationsManager.m index 08133db67..7fb70dd3e 100644 --- a/Signal/src/environment/NotificationsManager.m +++ b/Signal/src/environment/NotificationsManager.m @@ -157,7 +157,7 @@ NSString *senderName = [contactsManager displayNameForPhoneIdentifier:message.authorId]; NSString *groupName = [thread.name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; if (groupName.length < 1) { - groupName = NSLocalizedString(@"UNNAMED_GROUP_NAME", @"Named used for groups without a name."); + groupName = NSLocalizedString(@"NEW_GROUP_DEFAULT_TITLE", @""); } if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive && messageDescription) { diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index a4a3c10b6..1d9df0ede 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1297,9 +1297,6 @@ /* Label text in device manager for a device with no name */ "UNNAMED_DEVICE" = "Unnamed Device"; -/* Named used for groups without a name. */ -"UNNAMED_GROUP_NAME" = "Unnamed Group"; - /* No comment provided by engineer. */ "UNREGISTER_SIGNAL_FAIL" = "Failed to unregister from Signal."; From c871e2de3e0bc23786c5e04e02f19caddf5aa7bf Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 30 May 2017 09:50:17 -0400 Subject: [PATCH 4/5] Respond to CR. // FREEBIE --- Signal/src/contact/OWSContactsManager.m | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/Signal/src/contact/OWSContactsManager.m b/Signal/src/contact/OWSContactsManager.m index fd177a5c1..b2569c746 100644 --- a/Signal/src/contact/OWSContactsManager.m +++ b/Signal/src/contact/OWSContactsManager.m @@ -214,9 +214,14 @@ NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_Account { OWSAssert([NSThread isMainThread]); - NSMutableDictionary *cachedAccountNameMap = [NSMutableDictionary new]; - NSMutableDictionary *cachedFirstNameMap = [NSMutableDictionary new]; - NSMutableDictionary *cachedLastNameMap = [NSMutableDictionary new]; + // Preserve any existing values, so that contacts that have been removed + // from system contacts still show up properly in the app. + NSMutableDictionary *cachedAccountNameMap + = (self.cachedAccountNameMap ? [self.cachedAccountNameMap mutableCopy] : [NSMutableDictionary new]); + NSMutableDictionary *cachedFirstNameMap + = (self.cachedFirstNameMap ? [self.cachedFirstNameMap mutableCopy] : [NSMutableDictionary new]); + NSMutableDictionary *cachedLastNameMap + = (self.cachedLastNameMap ? [self.cachedLastNameMap mutableCopy] : [NSMutableDictionary new]); for (SignalAccount *signalAccount in self.signalAccounts) { NSString *baseName = (signalAccount.contact.fullName.length > 0 ? signalAccount.contact.fullName : signalAccount.recipientId); @@ -292,6 +297,13 @@ NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_Account }]; }]; + if (self.cachedAccountNameMap || self.cachedFirstNameMap || self.cachedLastNameMap) { + // If these properties have already been populated from system contacts, + // don't overwrite. In practice this should never happen. + OWSAssert(0); + return; + } + self.cachedAccountNameMap = [cachedAccountNameMap copy]; self.cachedFirstNameMap = [cachedFirstNameMap copy]; self.cachedLastNameMap = [cachedLastNameMap copy]; @@ -300,21 +312,21 @@ NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_Account - (NSString *_Nullable)cachedDisplayNameForRecipientId:(NSString *)recipientId { - OWSAssert(recipientId.lastPathComponent > 0); + OWSAssert(recipientId.length > 0); return self.cachedAccountNameMap[recipientId]; } - (NSString *_Nullable)cachedFirstNameForRecipientId:(NSString *)recipientId { - OWSAssert(recipientId.lastPathComponent > 0); + OWSAssert(recipientId.length > 0); return self.cachedFirstNameMap[recipientId]; } - (NSString *_Nullable)cachedLastNameForRecipientId:(NSString *)recipientId { - OWSAssert(recipientId.lastPathComponent > 0); + OWSAssert(recipientId.length > 0); return self.cachedLastNameMap[recipientId]; } From 63f014fab42642319a3e048aa1f08f514b820704 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 30 May 2017 09:59:14 -0400 Subject: [PATCH 5/5] [SSK] Cache display names for accounts. // FREEBIE --- Podfile | 4 ++-- Podfile.lock | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Podfile b/Podfile index 624665393..55d7dcd08 100644 --- a/Podfile +++ b/Podfile @@ -5,8 +5,8 @@ target 'Signal' do pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git' pod 'AxolotlKit', git: 'https://github.com/WhisperSystems/SignalProtocolKit.git' #pod 'AxolotlKit', path: '../SignalProtocolKit' - #pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git' - pod 'SignalServiceKit', path: '../SignalServiceKit' + pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git' + #pod 'SignalServiceKit', path: '../SignalServiceKit' pod 'OpenSSL' pod 'JSQMessagesViewController', git: 'https://github.com/WhisperSystems/JSQMessagesViewController.git', branch: 'mkirk/position-edit-menu' #pod 'JSQMessagesViewController' path: '../JSQMessagesViewController' diff --git a/Podfile.lock b/Podfile.lock index 4b8ae2071..3b351500e 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -114,7 +114,7 @@ DEPENDENCIES: - OpenSSL - PureLayout - Reachability - - SignalServiceKit (from `../SignalServiceKit`) + - SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`) - SocketRocket (from `https://github.com/facebook/SocketRocket.git`) EXTERNAL SOURCES: @@ -124,7 +124,7 @@ EXTERNAL SOURCES: :branch: mkirk/position-edit-menu :git: https://github.com/WhisperSystems/JSQMessagesViewController.git SignalServiceKit: - :path: ../SignalServiceKit + :git: https://github.com/WhisperSystems/SignalServiceKit.git SocketRocket: :git: https://github.com/facebook/SocketRocket.git @@ -135,6 +135,9 @@ CHECKOUT OPTIONS: JSQMessagesViewController: :commit: 7054e4b13ee5bcd6d524adb6dc9a726e8c466308 :git: https://github.com/WhisperSystems/JSQMessagesViewController.git + SignalServiceKit: + :commit: c7cc023541c9c7ee098f08f91949263fe0341625 + :git: https://github.com/WhisperSystems/SignalServiceKit.git SocketRocket: :commit: 877ac7438be3ad0b45ef5ca3969574e4b97112bf :git: https://github.com/facebook/SocketRocket.git @@ -161,6 +164,6 @@ SPEC CHECKSUMS: UnionFind: c33be5adb12983981d6e827ea94fc7f9e370f52d YapDatabase: cd911121580ff16675f65ad742a9eb0ab4d9e266 -PODFILE CHECKSUM: 6f9ef5d9fa17469569e127a9f5719dafa11631b9 +PODFILE CHECKSUM: 48e80d7f1e049bbf544a689fdfdf33e8196c640a COCOAPODS: 1.2.1