diff --git a/Signal/src/ViewControllers/ContactViewController.swift b/Signal/src/ViewControllers/ContactViewController.swift index 0465fa42a..37d02f53e 100644 --- a/Signal/src/ViewControllers/ContactViewController.swift +++ b/Signal/src/ViewControllers/ContactViewController.swift @@ -360,7 +360,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate for phoneNumber in contact.phoneNumbers { // TODO: Try to format the phone number nicely. - addRow(createNameValueRow(name: phoneNumber.labelString(), + addRow(createNameValueRow(name: phoneNumber.localizedLabel(), value: phoneNumber.phoneNumber, actionBlock: { guard let url = NSURL(string: "tel:\(phoneNumber.phoneNumber)") else { @@ -372,7 +372,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate } for email in contact.emails { - addRow(createNameValueRow(name: email.labelString(), + addRow(createNameValueRow(name: email.localizedLabel(), value: email.email, actionBlock: { guard let url = NSURL(string: "mailto:\(email.email)") else { diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index 4c74e3905..68b712d29 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -124,7 +124,7 @@ NS_ASSUME_NONNULL_BEGIN actionBlock:^{ [DebugUIMessages selectBackDatedAction:thread]; }], - [OWSTableItem itemWithTitle:@"Send All Contacts" + [OWSTableItem itemWithTitle:@"Send All Contact Shares" actionBlock:^{ [DebugUIMessages sendAllContacts:thread]; }], @@ -310,14 +310,6 @@ NS_ASSUME_NONNULL_BEGIN [self performActionNTimes:[self sendTextMessagesActionInThread:thread]]; } -+ (void)sendAllContacts:(TSThread *)thread -{ - NSArray *subactions = [self allSendContactShareActions:thread includeLabels:NO]; - DebugUIMessagesAction *action = - [DebugUIMessagesGroupAction allGroupActionWithLabel:@"Send All Share Contact" subactions:subactions]; - [action prepareAndPerformNTimes:subactions.count]; -} - + (DebugUIMessagesAction *)sendTextMessagesActionInThread:(TSThread *)thread { OWSAssert(thread); @@ -3095,21 +3087,24 @@ typedef OWSContact * (^OWSContactBlock)(void); { OWSAssert(thread); - return [DebugUIMessagesSingleAction - actionWithLabel:[NSString stringWithFormat:@"Fake Contact Share (%@)", label] - unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) { - OWSContact *contact = contactBlock(); - TSOutgoingMessage *message = [self createFakeOutgoingMessage:thread - messageBody:nil - fakeAssetLoader:nil - messageState:TSOutgoingMessageStateSent - isDelivered:NO - isRead:NO - quotedMessage:nil - contactShare:contact - transaction:transaction]; - [message saveWithTransaction:transaction]; - }]; + return + [DebugUIMessagesSingleAction actionWithLabel:[NSString stringWithFormat:@"Send Contact Share (%@)", label] + staggeredActionBlock:^(NSUInteger index, + YapDatabaseReadWriteTransaction *transaction, + ActionSuccessBlock success, + ActionFailureBlock failure) { + dispatch_async(dispatch_get_main_queue(), ^{ + OWSContact *contact = contactBlock(); + DDLogVerbose(@"%@ sending contact: %@", self.logTag, contact.debugDescription); + OWSMessageSender *messageSender = [Environment current].messageSender; + [ThreadUtil sendMessageWithContactShare:contact + inThread:thread + messageSender:messageSender + completion:nil]; + + success(); + }); + }]; } + (NSArray *)allSendContactShareActions:(TSThread *)thread includeLabels:(BOOL)includeLabels @@ -3210,10 +3205,58 @@ typedef OWSContact * (^OWSContactBlock)(void); // TODO: Avatar return contact; }]]; + [actions addObject:[self sendContactShareMessageAction:thread + label:@"Long values" + contactBlock:^{ + OWSContact *contact = [OWSContact new]; + contact.givenName = @"Bobasdjasdlkjasldkjas"; + contact.familyName = @"Bobasdjasdlkjasldkjas"; + OWSContactEmail *email = [OWSContactEmail new]; + email.emailType = OWSContactEmailType_Mobile; + email.email = @"asdlakjsaldkjasldkjasdlkjasdlkjasdlkajsa@b.com"; + contact.emails = @[ + email, + ]; + return contact; + }]]; + [actions addObject:[self sendContactShareMessageAction:thread + label:@"System Contact w/o Signal" + contactBlock:^{ + OWSContact *contact = [OWSContact new]; + contact.givenName = @"Add Me To Your Contacts"; + OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new]; + phoneNumber.phoneType = OWSContactPhoneType_Work; + phoneNumber.phoneNumber = @"+324602053911"; + contact.phoneNumbers = @[ + phoneNumber, + ]; + return contact; + }]]; + [actions addObject:[self sendContactShareMessageAction:thread + label:@"System Contact w. Signal" + contactBlock:^{ + OWSContact *contact = [OWSContact new]; + contact.givenName = @"Add Me To Your Contacts"; + OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new]; + phoneNumber.phoneType = OWSContactPhoneType_Work; + phoneNumber.phoneNumber = @"+32460205392"; + contact.phoneNumbers = @[ + phoneNumber, + ]; + return contact; + }]]; return actions; } ++ (void)sendAllContacts:(TSThread *)thread +{ + NSArray *subactions = [self allSendContactShareActions:thread includeLabels:NO]; + DebugUIMessagesAction *action = + [DebugUIMessagesGroupAction allGroupActionWithLabel:@"Send All Contact Shares" subactions:subactions]; + [action prepareAndPerformNTimes:subactions.count]; +} + #pragma mark - + (NSString *)randomOversizeText @@ -3282,7 +3325,6 @@ typedef OWSContact * (^OWSContactBlock)(void); [ThreadUtil sendMessageWithAttachment:attachment inThread:thread quotedReplyModel:nil - contactShare:nil messageSender:messageSender ignoreErrors:YES completion:nil]; diff --git a/SignalMessaging/utils/ThreadUtil.h b/SignalMessaging/utils/ThreadUtil.h index 8a93ab51b..197cc1c67 100644 --- a/SignalMessaging/utils/ThreadUtil.h +++ b/SignalMessaging/utils/ThreadUtil.h @@ -72,11 +72,15 @@ NS_ASSUME_NONNULL_BEGIN + (TSOutgoingMessage *)sendMessageWithAttachment:(SignalAttachment *)attachment inThread:(TSThread *)thread quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel - contactShare:(nullable OWSContact *)contactShare messageSender:(OWSMessageSender *)messageSender ignoreErrors:(BOOL)ignoreErrors completion:(void (^_Nullable)(NSError *_Nullable error))completion; ++ (TSOutgoingMessage *)sendMessageWithContactShare:(OWSContact *)contactShare + inThread:(TSThread *)thread + messageSender:(OWSMessageSender *)messageSender + completion:(void (^_Nullable)(NSError *_Nullable error))completion; + // This method will create and/or remove any offers and indicators // necessary for this thread. This includes: // diff --git a/SignalMessaging/utils/ThreadUtil.m b/SignalMessaging/utils/ThreadUtil.m index 60d6b5e40..c5df4a72a 100644 --- a/SignalMessaging/utils/ThreadUtil.m +++ b/SignalMessaging/utils/ThreadUtil.m @@ -107,7 +107,6 @@ NS_ASSUME_NONNULL_BEGIN return [self sendMessageWithAttachment:attachment inThread:thread quotedReplyModel:quotedReplyModel - contactShare:nil messageSender:messageSender ignoreErrors:NO completion:completion]; @@ -116,7 +115,6 @@ NS_ASSUME_NONNULL_BEGIN + (TSOutgoingMessage *)sendMessageWithAttachment:(SignalAttachment *)attachment inThread:(TSThread *)thread quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel - contactShare:(nullable OWSContact *)contactShare messageSender:(OWSMessageSender *)messageSender ignoreErrors:(BOOL)ignoreErrors completion:(void (^_Nullable)(NSError *_Nullable error))completion @@ -142,7 +140,7 @@ NS_ASSUME_NONNULL_BEGIN isVoiceMessage:[attachment isVoiceMessage] groupMetaMessage:TSGroupMessageUnspecified quotedMessage:[quotedReplyModel buildQuotedMessage] - contactShare:contactShare]; + contactShare:nil]; [messageSender enqueueAttachment:attachment.dataSource contentType:attachment.mimeType @@ -168,6 +166,54 @@ NS_ASSUME_NONNULL_BEGIN return message; } ++ (TSOutgoingMessage *)sendMessageWithContactShare:(OWSContact *)contactShare + inThread:(TSThread *)thread + messageSender:(OWSMessageSender *)messageSender + completion:(void (^_Nullable)(NSError *_Nullable error))completion +{ + OWSAssertIsOnMainThread(); + OWSAssert(contactShare); + OWSAssert(contactShare.ows_isValid); + OWSAssert(thread); + OWSAssert(messageSender); + + OWSDisappearingMessagesConfiguration *configuration = + [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId]; + + uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0); + TSOutgoingMessage *message = + [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] + inThread:thread + messageBody:nil + attachmentIds:[NSMutableArray new] + expiresInSeconds:expiresInSeconds + expireStartedAt:0 + isVoiceMessage:NO + groupMetaMessage:TSGroupMessageUnspecified + quotedMessage:nil + contactShare:contactShare]; + + [messageSender enqueueMessage:message + success:^{ + DDLogDebug(@"%@ Successfully sent contact share.", self.logTag); + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^(void) { + completion(nil); + }); + } + } + failure:^(NSError *error) { + DDLogError(@"%@ Failed to send contact share with error: %@", self.logTag, error); + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^(void) { + completion(error); + }); + } + }]; + + return message; +} + + (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)thread contactsManager:(OWSContactsManager *)contactsManager blockingManager:(OWSBlockingManager *)blockingManager diff --git a/SignalServiceKit/src/Messages/Interactions/OWSContact.h b/SignalServiceKit/src/Messages/Interactions/OWSContact.h index 905c14147..517debc26 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSContact.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSContact.h @@ -22,6 +22,8 @@ typedef NS_ENUM(NSUInteger, OWSContactPhoneType) { OWSContactPhoneType_Custom, }; +NSString *NSStringForContactPhoneType(OWSContactPhoneType value); + @interface OWSContactPhoneNumber : MTLModel @property (nonatomic, readonly) OWSContactPhoneType phoneType; @@ -32,7 +34,9 @@ typedef NS_ENUM(NSUInteger, OWSContactPhoneType) { - (BOOL)ows_isValid; -- (NSString *)labelString; +- (NSString *)localizedLabel; + +- (NSString *)debugDescription; @end @@ -45,6 +49,8 @@ typedef NS_ENUM(NSUInteger, OWSContactEmailType) { OWSContactEmailType_Custom, }; +NSString *NSStringForContactEmailType(OWSContactEmailType value); + @interface OWSContactEmail : MTLModel @property (nonatomic, readonly) OWSContactEmailType emailType; @@ -55,7 +61,9 @@ typedef NS_ENUM(NSUInteger, OWSContactEmailType) { - (BOOL)ows_isValid; -- (NSString *)labelString; +- (NSString *)localizedLabel; + +- (NSString *)debugDescription; @end @@ -67,6 +75,8 @@ typedef NS_ENUM(NSUInteger, OWSContactAddressType) { OWSContactAddressType_Custom, }; +NSString *NSStringForContactAddressType(OWSContactAddressType value); + @interface OWSContactAddress : MTLModel @property (nonatomic, readonly) OWSContactAddressType addressType; @@ -83,7 +93,9 @@ typedef NS_ENUM(NSUInteger, OWSContactAddressType) { - (BOOL)ows_isValid; -- (NSString *)labelString; +- (NSString *)localizedLabel; + +- (NSString *)debugDescription; @end @@ -114,6 +126,8 @@ typedef NS_ENUM(NSUInteger, OWSContactAddressType) { - (BOOL)ows_isValid; +- (NSString *)debugDescription; + @end #pragma mark - diff --git a/SignalServiceKit/src/Messages/Interactions/OWSContact.m b/SignalServiceKit/src/Messages/Interactions/OWSContact.m index b07c0b0e6..b7c7f9fb2 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSContact.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSContact.m @@ -16,6 +16,20 @@ NS_ASSUME_NONNULL_BEGIN BOOL kIsSendingContactSharesEnabled = YES; +NSString *NSStringForContactPhoneType(OWSContactPhoneType value) +{ + switch (value) { + case OWSContactPhoneType_Home: + return @"Home"; + case OWSContactPhoneType_Mobile: + return @"Mobile"; + case OWSContactPhoneType_Work: + return @"Work"; + case OWSContactPhoneType_Custom: + return @"Custom"; + } +} + @interface OWSContactPhoneNumber () @property (nonatomic) OWSContactPhoneType phoneType; @@ -32,6 +46,7 @@ BOOL kIsSendingContactSharesEnabled = YES; - (BOOL)ows_isValid { if (![PhoneNumber tryParsePhoneNumberFromE164:self.phoneNumber]) { + DDLogWarn(@"%@ invalid phone number; not e164: %@.", self.logTag, self.phoneNumber); return NO; } switch (self.phoneType) { @@ -40,11 +55,15 @@ BOOL kIsSendingContactSharesEnabled = YES; case OWSContactPhoneType_Work: return YES; case OWSContactPhoneType_Custom: - return self.label.ows_stripped.length > 0; + if (self.label.ows_stripped.length < 1) { + DDLogWarn(@"%@ invalid phone number; missing custom label: %@.", self.logTag, self.label); + return NO; + } + return YES; } } -- (NSString *)labelString +- (NSString *)localizedLabel { switch (self.phoneType) { case OWSContactPhoneType_Home: @@ -58,10 +77,40 @@ BOOL kIsSendingContactSharesEnabled = YES; } } +- (NSString *)debugDescription +{ + NSMutableString *result = [NSMutableString new]; + [result appendFormat:@"[Phone Number: %@, ", NSStringForContactPhoneType(self.phoneType)]; + + if (self.label.length > 0) { + [result appendFormat:@"label: %@, ", self.label]; + } + if (self.phoneNumber.length > 0) { + [result appendFormat:@"phoneNumber: %@, ", self.phoneNumber]; + } + + [result appendString:@"]"]; + return result; +} + @end #pragma mark - +NSString *NSStringForContactEmailType(OWSContactEmailType value) +{ + switch (value) { + case OWSContactEmailType_Home: + return @"Home"; + case OWSContactEmailType_Mobile: + return @"Mobile"; + case OWSContactEmailType_Work: + return @"Work"; + case OWSContactEmailType_Custom: + return @"Custom"; + } +} + @interface OWSContactEmail () @property (nonatomic) OWSContactEmailType emailType; @@ -78,6 +127,7 @@ BOOL kIsSendingContactSharesEnabled = YES; - (BOOL)ows_isValid { if (self.email.ows_stripped.length < 1) { + DDLogWarn(@"%@ invalid email: %@.", self.logTag, self.email); return NO; } switch (self.emailType) { @@ -86,11 +136,15 @@ BOOL kIsSendingContactSharesEnabled = YES; case OWSContactEmailType_Work: return YES; case OWSContactEmailType_Custom: - return self.label.ows_stripped.length > 0; + if (self.label.ows_stripped.length < 1) { + DDLogWarn(@"%@ invalid email; missing custom label: %@.", self.logTag, self.label); + return NO; + } + return YES; } } -- (NSString *)labelString +- (NSString *)localizedLabel { switch (self.emailType) { case OWSContactEmailType_Home: @@ -104,10 +158,37 @@ BOOL kIsSendingContactSharesEnabled = YES; } } +- (NSString *)debugDescription +{ + NSMutableString *result = [NSMutableString new]; + [result appendFormat:@"[Email: %@, ", NSStringForContactEmailType(self.emailType)]; + + if (self.label.length > 0) { + [result appendFormat:@"label: %@, ", self.label]; + } + if (self.email.length > 0) { + [result appendFormat:@"email: %@, ", self.email]; + } + + [result appendString:@"]"]; + return result; +} + @end #pragma mark - +NSString *NSStringForContactAddressType(OWSContactAddressType value) +{ + switch (value) { + case OWSContactAddressType_Home: + return @"Home"; + case OWSContactAddressType_Work: + return @"Work"; + case OWSContactAddressType_Custom: + return @"Custom"; + } +} @interface OWSContactAddress () @property (nonatomic) OWSContactAddressType addressType; @@ -133,6 +214,7 @@ BOOL kIsSendingContactSharesEnabled = YES; && self.neighborhood.ows_stripped.length < 1 && self.city.ows_stripped.length < 1 && self.region.ows_stripped.length < 1 && self.postcode.ows_stripped.length < 1 && self.country.ows_stripped.length < 1) { + DDLogWarn(@"%@ invalid address; empty.", self.logTag); return NO; } switch (self.addressType) { @@ -140,11 +222,15 @@ BOOL kIsSendingContactSharesEnabled = YES; case OWSContactAddressType_Work: return YES; case OWSContactAddressType_Custom: - return self.label.ows_stripped.length > 0; + if (self.label.ows_stripped.length < 1) { + DDLogWarn(@"%@ invalid address; missing custom label: %@.", self.logTag, self.label); + return NO; + } + return YES; } } -- (NSString *)labelString +- (NSString *)localizedLabel { switch (self.addressType) { case OWSContactAddressType_Home: @@ -156,6 +242,40 @@ BOOL kIsSendingContactSharesEnabled = YES; } } +- (NSString *)debugDescription +{ + NSMutableString *result = [NSMutableString new]; + [result appendFormat:@"[Address: %@, ", NSStringForContactAddressType(self.addressType)]; + + if (self.label.length > 0) { + [result appendFormat:@"label: %@, ", self.label]; + } + if (self.street.length > 0) { + [result appendFormat:@"street: %@, ", self.street]; + } + if (self.pobox.length > 0) { + [result appendFormat:@"pobox: %@, ", self.pobox]; + } + if (self.neighborhood.length > 0) { + [result appendFormat:@"neighborhood: %@, ", self.neighborhood]; + } + if (self.city.length > 0) { + [result appendFormat:@"city: %@, ", self.city]; + } + if (self.region.length > 0) { + [result appendFormat:@"region: %@, ", self.region]; + } + if (self.postcode.length > 0) { + [result appendFormat:@"postcode: %@, ", self.postcode]; + } + if (self.country.length > 0) { + [result appendFormat:@"country: %@, ", self.country]; + } + + [result appendString:@"]"]; + return result; +} + @end #pragma mark - @@ -214,7 +334,8 @@ BOOL kIsSendingContactSharesEnabled = YES; - (BOOL)ows_isValid { - if (self.displayName.ows_stripped.length) { + if (self.displayName.ows_stripped.length < 1) { + DDLogWarn(@"%@ invalid contact; no display name.", self.logTag); return NO; } BOOL hasValue = NO; @@ -261,6 +382,44 @@ BOOL kIsSendingContactSharesEnabled = YES; } } +- (NSString *)debugDescription +{ + NSMutableString *result = [NSMutableString new]; + [result appendString:@"["]; + + if (self.givenName.length > 0) { + [result appendFormat:@"givenName: %@, ", self.givenName]; + } + if (self.familyName.length > 0) { + [result appendFormat:@"familyName: %@, ", self.familyName]; + } + if (self.middleName.length > 0) { + [result appendFormat:@"middleName: %@, ", self.middleName]; + } + if (self.namePrefix.length > 0) { + [result appendFormat:@"namePrefix: %@, ", self.namePrefix]; + } + if (self.nameSuffix.length > 0) { + [result appendFormat:@"nameSuffix: %@, ", self.nameSuffix]; + } + if (self.displayName.length > 0) { + [result appendFormat:@"displayName: %@, ", self.displayName]; + } + + for (OWSContactPhoneNumber *phoneNumber in self.phoneNumbers) { + [result appendFormat:@"%@, ", phoneNumber.debugDescription]; + } + for (OWSContactEmail *email in self.emails) { + [result appendFormat:@"%@, ", email.debugDescription]; + } + for (OWSContactAddress *address in self.addresses) { + [result appendFormat:@"%@, ", address.debugDescription]; + } + + [result appendString:@"]"]; + return result; +} + @end #pragma mark -