diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index b2dbb37ea..f0c29f4b5 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -188,6 +188,7 @@ 34C6B0AB1FA0E46F00D35993 /* test-mp3.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 34C6B0A71FA0E46F00D35993 /* test-mp3.mp3 */; }; 34C6B0AC1FA0E46F00D35993 /* test-mp4.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 34C6B0A81FA0E46F00D35993 /* test-mp4.mp4 */; }; 34C6B0AE1FA0E4AA00D35993 /* test-jpg.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 34C6B0AD1FA0E4AA00D35993 /* test-jpg.jpg */; }; + 34CA631B2097806F00E526A0 /* OWSContactShareView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CA631A2097806E00E526A0 /* OWSContactShareView.m */; }; 34CCAF381F0C0599004084F4 /* AppUpdateNag.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CCAF371F0C0599004084F4 /* AppUpdateNag.m */; }; 34CE88E71F2FB9A10098030F /* ProfileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CE88E61F2FB9A10098030F /* ProfileViewController.m */; }; 34CF0787203E6B78005C4D61 /* busy_tone_ansi.caf in Resources */ = {isa = PBXBuildFile; fileRef = 34CF0783203E6B77005C4D61 /* busy_tone_ansi.caf */; }; @@ -802,6 +803,8 @@ 34C6B0AD1FA0E4AA00D35993 /* test-jpg.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "test-jpg.jpg"; sourceTree = ""; }; 34CA1C261F7156F300E51C51 /* MessageDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageDetailViewController.swift; sourceTree = ""; }; 34CA1C281F7164F700E51C51 /* MediaMessageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaMessageView.swift; sourceTree = ""; }; + 34CA63192097806E00E526A0 /* OWSContactShareView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactShareView.h; sourceTree = ""; }; + 34CA631A2097806E00E526A0 /* OWSContactShareView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactShareView.m; sourceTree = ""; }; 34CCAF361F0C0599004084F4 /* AppUpdateNag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppUpdateNag.h; sourceTree = ""; }; 34CCAF371F0C0599004084F4 /* AppUpdateNag.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppUpdateNag.m; sourceTree = ""; }; 34CE88E51F2FB9A10098030F /* ProfileViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProfileViewController.h; sourceTree = ""; }; @@ -1711,6 +1714,8 @@ 34DBF001206BD5A500025978 /* OWSBubbleView.m */, 34D1F09A1F867BFC0066283D /* OWSContactOffersCell.h */, 34D1F09B1F867BFC0066283D /* OWSContactOffersCell.m */, + 34CA63192097806E00E526A0 /* OWSContactShareView.h */, + 34CA631A2097806E00E526A0 /* OWSContactShareView.m */, 34D1F09C1F867BFC0066283D /* OWSExpirableMessageView.h */, 34D1F09D1F867BFC0066283D /* OWSExpirationTimerView.h */, 34D1F09E1F867BFC0066283D /* OWSExpirationTimerView.m */, @@ -3276,6 +3281,7 @@ 34A910601FFEB114000C4745 /* OWSBackup.m in Sources */, 34D1F0B01F867BFC0066283D /* OWSSystemMessageCell.m in Sources */, 45A663C51F92EC760027B59E /* GroupTableViewCell.swift in Sources */, + 34CA631B2097806F00E526A0 /* OWSContactShareView.m in Sources */, 34D1F0861F8678AA0066283D /* ConversationViewController.m in Sources */, B90418E6183E9DD40038554A /* DateUtil.m in Sources */, 340FC8BD204DAC8D007AEB0F /* ShowGroupMembersViewController.m in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m index 35cf1e3b4..b462923dd 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m @@ -125,7 +125,7 @@ NS_ASSUME_NONNULL_BEGIN : [self audioColorWithOpacity:0.4f]); } -#pragma mark - JSQMessageMediaData protocol +#pragma mark - - (CGFloat)audioIconHMargin { diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSContactShareView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSContactShareView.h new file mode 100644 index 000000000..3a62a0813 --- /dev/null +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSContactShareView.h @@ -0,0 +1,19 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +@class OWSContact; + +@interface OWSContactShareView : UIView + +- (instancetype)initWithContactShare:(OWSContact *)contactShare isIncoming:(BOOL)isIncoming; + +- (void)createContents; + ++ (CGFloat)bubbleHeight; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSContactShareView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSContactShareView.m new file mode 100644 index 000000000..fad338677 --- /dev/null +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSContactShareView.m @@ -0,0 +1,176 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +#import "OWSContactShareView.h" +#import "UIColor+JSQMessages.h" +#import "UIColor+OWS.h" +#import "UIFont+OWS.h" +#import "UIView+OWS.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface OWSContactShareView () + +@property (nonatomic) OWSContact *contactShare; +@property (nonatomic) BOOL isIncoming; + +@end + +#pragma mark - + +@implementation OWSContactShareView + +- (instancetype)initWithContactShare:(OWSContact *)contactShare isIncoming:(BOOL)isIncoming +{ + self = [super init]; + + if (self) { + _contactShare = contactShare; + _isIncoming = isIncoming; + } + + return self; +} + +#pragma mark - + +- (CGFloat)iconHMargin +{ + return 12.f; +} + +- (CGFloat)iconHSpacing +{ + return 10.f; +} + ++ (CGFloat)iconVMargin +{ + return 12.f; +} + +- (CGFloat)iconVMargin +{ + return [OWSContactShareView iconVMargin]; +} + ++ (CGFloat)bubbleHeight +{ + return self.iconSize + self.iconVMargin * 2; +} + +- (CGFloat)bubbleHeight +{ + return [OWSContactShareView bubbleHeight]; +} + ++ (CGFloat)iconSize +{ + return 44.f; +} + +- (CGFloat)iconSize +{ + return [OWSContactShareView iconSize]; +} + +- (CGFloat)vMargin +{ + return 10.f; +} + +- (UIColor *)bubbleBackgroundColor +{ + return self.isIncoming ? [UIColor jsq_messageBubbleLightGrayColor] : [UIColor ows_materialBlueColor]; +} + +- (void)createContents +{ + self.backgroundColor = [UIColor colorWithRGBHex:0xefeff4]; + self.layoutMargins = UIEdgeInsetsZero; + + // TODO: Verify that this layout works in RTL. + const CGFloat kBubbleTailWidth = 6.f; + + UIView *contentView = [UIView containerView]; + [self addSubview:contentView]; + [contentView autoPinLeadingToSuperviewMarginWithInset:self.isIncoming ? kBubbleTailWidth : 0.f]; + [contentView autoPinTrailingToSuperviewMarginWithInset:self.isIncoming ? 0.f : kBubbleTailWidth]; + [contentView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.vMargin]; + [contentView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.vMargin]; + + UIView *iconCircleView = [UIView containerView]; + iconCircleView.backgroundColor = [UIColor colorWithRGBHex:0x00ffff]; + iconCircleView.layer.cornerRadius = self.iconSize * 0.5f; + [iconCircleView autoSetDimension:ALDimensionWidth toSize:self.iconSize]; + [iconCircleView autoSetDimension:ALDimensionHeight toSize:self.iconSize]; + [iconCircleView setCompressionResistanceHigh]; + [iconCircleView setContentHuggingHigh]; + + // TODO: Use avatar, if present and downloaded. else default. + UIImage *image = [UIImage imageNamed:@"attachment_file"]; + OWSAssert(image); + UIImageView *imageView = [UIImageView new]; + imageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + imageView.tintColor = self.bubbleBackgroundColor; + [iconCircleView addSubview:imageView]; + [imageView autoCenterInSuperview]; + + UILabel *topLabel = [UILabel new]; + topLabel.text = self.contactShare.displayName; + topLabel.textColor = [UIColor blackColor]; + topLabel.lineBreakMode = NSLineBreakByTruncatingTail; + topLabel.font = [UIFont ows_dynamicTypeBodyFont]; + + UIStackView *labelsView = [UIStackView new]; + labelsView.axis = UILayoutConstraintAxisVertical; + labelsView.spacing = 2; + [labelsView addArrangedSubview:topLabel]; + + // TODO: Should we just try to show the _first_ phone number? + // What about email? + // What if the second phone number is a signal account? + NSString *_Nullable firstPhoneNumber = self.contactShare.phoneNumbers.firstObject.phoneNumber; + if (firstPhoneNumber.length > 0) { + UILabel *bottomLabel = [UILabel new]; + bottomLabel.text = firstPhoneNumber; + // TODO: + bottomLabel.textColor = [UIColor ows_darkGrayColor]; + bottomLabel.lineBreakMode = NSLineBreakByTruncatingTail; + bottomLabel.font = [UIFont ows_dynamicTypeCaption1Font]; + [labelsView addArrangedSubview:bottomLabel]; + } + + UIImage *disclosureImage = + [UIImage imageNamed:(self.isRTL ? @"system_disclosure_indicator_rtl" : @"system_disclosure_indicator")]; + OWSAssert(disclosureImage); + UIImageView *disclosureImageView = [UIImageView new]; + disclosureImageView.image = [disclosureImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + disclosureImageView.tintColor = [UIColor blackColor]; + [disclosureImageView setCompressionResistanceHigh]; + [disclosureImageView setContentHuggingHigh]; + + UIStackView *stackView = [UIStackView new]; + stackView.axis = UILayoutConstraintAxisHorizontal; + stackView.spacing = self.iconHSpacing; + stackView.alignment = UIStackViewAlignmentCenter; + [contentView addSubview:stackView]; + [stackView autoPinLeadingToSuperviewMarginWithInset:self.iconHMargin]; + [stackView autoPinTrailingToSuperviewMarginWithInset:self.iconHMargin]; + [stackView autoVCenterInSuperview]; + // Ensure that the cell's contents never overflow the cell bounds. + // We pin pin to the superview _edge_ and not _margin_ for the purposes + // of overflow, so that changes to the margins do not trip these safe guards. + [stackView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:0 relation:NSLayoutRelationGreaterThanOrEqual]; + [stackView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:0 relation:NSLayoutRelationGreaterThanOrEqual]; + + [stackView addArrangedSubview:iconCircleView]; + [stackView addArrangedSubview:labelsView]; + [stackView addArrangedSubview:disclosureImageView]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m index 934178ac3..75f463572 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSGenericAttachmentView.m @@ -39,7 +39,7 @@ NS_ASSUME_NONNULL_BEGIN return self; } -#pragma mark - JSQMessageMediaData protocol +#pragma mark - - (CGFloat)iconHMargin { diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 3e05346ce..50cc326ce 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -8,6 +8,7 @@ #import "OWSAudioMessageView.h" #import "OWSBubbleStrokeView.h" #import "OWSBubbleView.h" +#import "OWSContactShareView.h" #import "OWSGenericAttachmentView.h" #import "OWSMessageTextView.h" #import "OWSQuotedMessageView.h" @@ -194,7 +195,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -- (BOOL)hasNonImageBodyContent +- (BOOL)hasBubbleBackground { switch (self.cellType) { case OWSMessageCellType_Unknown: @@ -202,6 +203,7 @@ NS_ASSUME_NONNULL_BEGIN case OWSMessageCellType_OversizeTextMessage: case OWSMessageCellType_GenericAttachment: case OWSMessageCellType_DownloadingAttachment: + case OWSMessageCellType_ContactShare: return YES; case OWSMessageCellType_StillImage: case OWSMessageCellType_AnimatedImage: @@ -226,6 +228,8 @@ NS_ASSUME_NONNULL_BEGIN case OWSMessageCellType_Video: // Is there a caption? return self.hasBodyText; + case OWSMessageCellType_ContactShare: + return NO; } } @@ -245,7 +249,7 @@ NS_ASSUME_NONNULL_BEGIN self.bubbleView.isOutgoing = self.isOutgoing; self.bubbleView.hideTail = self.viewItem.shouldHideBubbleTail && !self.alwaysShowBubbleTail; - if ([self.viewItem.interaction isKindOfClass:[TSMessage class]] && self.hasNonImageBodyContent) { + if ([self.viewItem.interaction isKindOfClass:[TSMessage class]] && self.hasBubbleBackground) { TSMessage *message = (TSMessage *)self.viewItem.interaction; self.bubbleView.bubbleColor = [self.bubbleFactory bubbleColorWithMessage:message]; } else { @@ -327,6 +331,10 @@ NS_ASSUME_NONNULL_BEGIN bodyMediaView = [self loadViewForDownloadingAttachment]; bodyMediaViewHasGreedyWidth = YES; break; + case OWSMessageCellType_ContactShare: + bodyMediaView = [self loadViewForContactShare]; + bodyMediaViewHasGreedyWidth = YES; + break; } if (bodyMediaView) { @@ -784,6 +792,26 @@ NS_ASSUME_NONNULL_BEGIN return customView; } +- (UIView *)loadViewForContactShare +{ + OWSAssert(self.viewItem.contactShare); + + OWSContactShareView *contactShareView = + [[OWSContactShareView alloc] initWithContactShare:self.viewItem.contactShare + isIncoming:self.isIncoming]; + [contactShareView createContents]; + // TODO: Should we change appearance if contact avatar is uploading? + + self.loadCellContentBlock = ^{ + // Do nothing. + }; + self.unloadCellContentBlock = ^{ + // Do nothing. + }; + + return contactShareView; +} + - (void)addAttachmentUploadViewIfNecessary:(UIView *)attachmentView { [self addAttachmentUploadViewIfNecessary:attachmentView @@ -902,6 +930,8 @@ NS_ASSUME_NONNULL_BEGIN return CGSizeMake(maxMessageWidth, [OWSGenericAttachmentView bubbleHeight]); case OWSMessageCellType_DownloadingAttachment: return CGSizeMake(200, 90); + case OWSMessageCellType_ContactShare: + return CGSizeMake(maxMessageWidth, [OWSContactShareView bubbleHeight]); } } @@ -1022,6 +1052,10 @@ NS_ASSUME_NONNULL_BEGIN if (self.cellType == OWSMessageCellType_DownloadingAttachment) { return NO; } + if (self.cellType == OWSMessageCellType_ContactShare) { + // TODO: Handle this case. + return NO; + } if (!self.attachmentStream) { return NO; } @@ -1168,6 +1202,9 @@ NS_ASSUME_NONNULL_BEGIN } break; } + case OWSMessageCellType_ContactShare: + // TODO: + break; } } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index dd175445e..32bfdb4ab 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -17,6 +17,7 @@ typedef NS_ENUM(NSInteger, OWSMessageCellType) { OWSMessageCellType_Video, OWSMessageCellType_GenericAttachment, OWSMessageCellType_DownloadingAttachment, + OWSMessageCellType_ContactShare, }; NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @@ -26,6 +27,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @class ConversationViewCell; @class DisplayableText; @class OWSAudioMessageView; +@class OWSContact; @class OWSQuotedReplyModel; @class TSAttachmentPointer; @class TSAttachmentStream; @@ -101,6 +103,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); // if a load has previously failed. @property (nonatomic) BOOL didCellMediaFailToLoad; +- (nullable OWSContact *)contactShare; + #pragma mark - UIMenuController - (NSArray *)textMenuControllerItems; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 4070f192a..909cff909 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -11,6 +11,7 @@ #import "Signal-Swift.h" #import #import +#import #import NS_ASSUME_NONNULL_BEGIN @@ -36,6 +37,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) return @"OWSMessageCellType_DownloadingAttachment"; case OWSMessageCellType_Unknown: return @"OWSMessageCellType_Unknown"; + case OWSMessageCellType_ContactShare: + return @"OWSMessageCellType_ContactShare"; } } @@ -62,6 +65,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) @property (nonatomic, readonly, nullable) NSString *quotedRecipientId; @property (nonatomic, nullable) TSAttachmentStream *attachmentStream; @property (nonatomic, nullable) TSAttachmentPointer *attachmentPointer; +@property (nonatomic, nullable) OWSContact *contactShare; @property (nonatomic) CGSize mediaSize; @end @@ -401,6 +405,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) self.hasViewState = YES; TSMessage *message = (TSMessage *)self.interaction; + if (message.contactShare) { + self.contactShare = message.contactShare; + self.messageCellType = OWSMessageCellType_ContactShare; + return; + } TSAttachment *_Nullable attachment = [self firstAttachmentIfAnyOfMessage:message transaction:transaction]; if (attachment) { if ([attachment isKindOfClass:[TSAttachmentStream class]]) { @@ -712,6 +721,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) OWSFail(@"%@ No text to copy", self.logTag); break; } + case OWSMessageCellType_ContactShare: { + // TODO: Implement copy contact. + OWSFail(@"%@ Not implemented yet", self.logTag); + break; + } } } @@ -720,7 +734,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextMessage: - case OWSMessageCellType_OversizeTextMessage: { + case OWSMessageCellType_OversizeTextMessage: + case OWSMessageCellType_ContactShare: { OWSFail(@"%@ No media to copy", self.logTag); break; } @@ -802,6 +817,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) case OWSMessageCellType_Unknown: case OWSMessageCellType_TextMessage: case OWSMessageCellType_OversizeTextMessage: + case OWSMessageCellType_ContactShare: return NO; case OWSMessageCellType_StillImage: case OWSMessageCellType_AnimatedImage: @@ -824,6 +840,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) case OWSMessageCellType_Unknown: case OWSMessageCellType_TextMessage: case OWSMessageCellType_OversizeTextMessage: + case OWSMessageCellType_ContactShare: OWSFail(@"%@ Cannot save text data.", self.logTag); break; case OWSMessageCellType_StillImage: @@ -879,6 +896,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) case OWSMessageCellType_Unknown: case OWSMessageCellType_TextMessage: case OWSMessageCellType_OversizeTextMessage: + case OWSMessageCellType_ContactShare: return NO; case OWSMessageCellType_StillImage: case OWSMessageCellType_AnimatedImage: diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index e308dbbad..181c97cca 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -97,7 +97,7 @@ NS_ASSUME_NONNULL_BEGIN // Exemplary [DebugUIMessages allFakeAction:thread], [DebugUIMessages allFakeBackDatedAction:thread], - [DebugUIMessages allShareContactAction:thread], + [DebugUIMessages allContactShareAction:thread], ]]]; [items addObjectsFromArray:@[ @@ -2658,7 +2658,7 @@ NS_ASSUME_NONNULL_BEGIN [actions addObjectsFromArray:[self allFakeSequenceActions:thread includeLabels:includeLabels]]; [actions addObjectsFromArray:[self allFakeQuotedReplyActions:thread includeLabels:includeLabels]]; [actions addObjectsFromArray:[self allFakeBackDatedActions:thread includeLabels:includeLabels]]; - [actions addObjectsFromArray:[self allShareContactActions:thread includeLabels:includeLabels]]; + [actions addObjectsFromArray:[self allContactShareActions:thread includeLabels:includeLabels]]; return actions; } @@ -2902,7 +2902,7 @@ NS_ASSUME_NONNULL_BEGIN typedef OWSContact * (^OWSContactBlock)(void); -+ (DebugUIMessagesAction *)fakeShareContactMessageAction:(TSThread *)thread ++ (DebugUIMessagesAction *)fakeContactShareMessageAction:(TSThread *)thread label:(NSString *)label contactBlock:(OWSContactBlock)contactBlock { @@ -2925,7 +2925,7 @@ typedef OWSContact * (^OWSContactBlock)(void); }]; } -+ (NSArray *)allShareContactActions:(TSThread *)thread includeLabels:(BOOL)includeLabels ++ (NSArray *)allContactShareActions:(TSThread *)thread includeLabels:(BOOL)includeLabels { OWSAssert(thread); @@ -2937,7 +2937,7 @@ typedef OWSContact * (^OWSContactBlock)(void); text:@"⚠️ Share Contact ⚠️"]]; } - [actions addObject:[self fakeShareContactMessageAction:thread + [actions addObject:[self fakeContactShareMessageAction:thread label:@"Name & Number" contactBlock:^{ OWSContact *contact = [OWSContact new]; @@ -2950,7 +2950,7 @@ typedef OWSContact * (^OWSContactBlock)(void); ]; return contact; }]]; - [actions addObject:[self fakeShareContactMessageAction:thread + [actions addObject:[self fakeContactShareMessageAction:thread label:@"Name & Email" contactBlock:^{ OWSContact *contact = [OWSContact new]; @@ -2963,7 +2963,7 @@ typedef OWSContact * (^OWSContactBlock)(void); ]; return contact; }]]; - [actions addObject:[self fakeShareContactMessageAction:thread + [actions addObject:[self fakeContactShareMessageAction:thread label:@"Complicated" contactBlock:^{ OWSContact *contact = [OWSContact new]; @@ -2973,7 +2973,6 @@ typedef OWSContact * (^OWSContactBlock)(void); contact.namePrefix = @"Ms."; contact.nameSuffix = @"Esq."; contact.organizationName = @"Falafel Hut"; - contact.displayName = @"Ms. Alice Bob Carol Esq."; OWSContactPhoneNumber *phoneNumber1 = [OWSContactPhoneNumber new]; phoneNumber1.phoneType = OWSContactPhoneType_Home; @@ -3028,12 +3027,12 @@ typedef OWSContact * (^OWSContactBlock)(void); return actions; } -+ (DebugUIMessagesAction *)allShareContactAction:(TSThread *)thread ++ (DebugUIMessagesAction *)allContactShareAction:(TSThread *)thread { OWSAssert(thread); - return [DebugUIMessagesGroupAction allGroupActionWithLabel:@"All Fake Share Contact" - subactions:[self allShareContactActions:thread includeLabels:YES]]; + return [DebugUIMessagesGroupAction allGroupActionWithLabel:@"All Fake Contact Shares" + subactions:[self allContactShareActions:thread includeLabels:YES]]; } #pragma mark - diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index f726826c6..283f35887 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -84,8 +84,8 @@ NS_ASSUME_NONNULL_BEGIN [self.payloadView autoPinLeadingToTrailingEdgeOfView:self.avatarView offset:self.avatarHSpacing]; [self.payloadView autoVCenterInSuperview]; // Ensure that the cell's contents never overflow the cell bounds. - // - // NOTE: It's critical that we pin to the superview top and bottom _edge_ and not _margin_. + // We pin pin to the superview _edge_ and not _margin_ for the purposes + // of overflow, so that changes to the margins do not trip these safe guards. [self.payloadView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:0 relation:NSLayoutRelationGreaterThanOrEqual]; [self.payloadView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:0 relation:NSLayoutRelationGreaterThanOrEqual]; // We pin the payloadView traillingEdge later, as part of the "Unread Badge" logic. diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 6345a4a3e..08d1adffa 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -748,7 +748,9 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations // Constrain to cell margins. [stackView autoPinEdgeToSuperviewMargin:ALEdgeLeading relation:NSLayoutRelationGreaterThanOrEqual]; [stackView autoPinEdgeToSuperviewMargin:ALEdgeTrailing relation:NSLayoutRelationGreaterThanOrEqual]; - // NOTE: It's critical that we pin to the superview top and bottom _edge_ and not _margin_. + // Ensure that the cell's contents never overflow the cell bounds. + // We pin pin to the superview _edge_ and not _margin_ for the purposes + // of overflow, so that changes to the margins do not trip these safe guards. [stackView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:0 relation:NSLayoutRelationGreaterThanOrEqual]; [stackView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:0 relation:NSLayoutRelationGreaterThanOrEqual]; diff --git a/SignalServiceKit/src/Messages/Interactions/OWSContact.m b/SignalServiceKit/src/Messages/Interactions/OWSContact.m index 15ebbf36d..aa15b985e 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSContact.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSContact.m @@ -186,6 +186,28 @@ NS_ASSUME_NONNULL_BEGIN return hasValue; } +- (nullable NSString *)displayName +{ + [self ensureDisplayName]; + + return _displayName; +} + +- (void)ensureDisplayName +{ + if (_displayName.length < 1) { + CNContact *_Nullable systemContact = [OWSContacts systemContactForContact:self]; + _displayName = [CNContactFormatter stringFromContact:systemContact style:CNContactFormatterStyleFullName]; + } + if (_displayName.length < 1) { + // Fall back to using the organization name. + _displayName = self.organizationName; + } + if (_displayName.length < 1) { + OWSProdLogAndFail(@"%@ could not derive a valid display name.", self.logTag); + } +} + @end #pragma mark - @@ -318,6 +340,8 @@ NS_ASSUME_NONNULL_BEGIN // @property (readonly, copy, nullable, NS_NONATOMIC_IOSONLY) NSData *imageData; // @property (readonly, copy, nullable, NS_NONATOMIC_IOSONLY) NSData *thumbnailImageData; + [contact ensureDisplayName]; + return contact; } @@ -635,6 +659,8 @@ NS_ASSUME_NONNULL_BEGIN // TODO: Avatar + [contact ensureDisplayName]; + return contact; }