diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 7211ca7fa..d983f0cea 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -25,6 +25,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) UIStackView *stackView; +@property (nonatomic) UILabel *senderNameLabel; + @property (nonatomic) OWSMessageTextView *bodyTextView; @property (nonatomic, nullable) UIView *quotedMessageView; @@ -77,6 +79,8 @@ NS_ASSUME_NONNULL_BEGIN self.stackView.axis = UILayoutConstraintAxisVertical; self.stackView.alignment = UIStackViewAlignmentFill; + self.senderNameLabel = [UILabel new]; + self.bodyTextView = [self newTextView]; // Setting dataDetectorTypes is expensive. Do it just once. self.bodyTextView.dataDetectorTypes @@ -206,24 +210,6 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -- (BOOL)hasBubbleBackground -{ - switch (self.cellType) { - case OWSMessageCellType_Unknown: - case OWSMessageCellType_TextMessage: - case OWSMessageCellType_OversizeTextMessage: - case OWSMessageCellType_GenericAttachment: - case OWSMessageCellType_DownloadingAttachment: - case OWSMessageCellType_ContactShare: - return YES; - case OWSMessageCellType_StillImage: - case OWSMessageCellType_AnimatedImage: - case OWSMessageCellType_Audio: - case OWSMessageCellType_Video: - return self.hasBodyText; - } -} - - (BOOL)hasBodyTextContent { switch (self.cellType) { @@ -260,17 +246,18 @@ NS_ASSUME_NONNULL_BEGIN [self.bubbleView addSubview:self.stackView]; [self.viewConstraints addObjectsFromArray:[self.stackView autoPinEdgesToSuperviewEdges]]; + NSMutableArray *textViews = [NSMutableArray new]; - if ([self.viewItem.interaction isKindOfClass:[TSMessage class]] && self.hasBubbleBackground) { - TSMessage *message = (TSMessage *)self.viewItem.interaction; - self.bubbleView.bubbleColor = [self.bubbleFactory bubbleColorWithMessage:message]; - } else { - // Media-only messages should have no background color; they will fill the bubble's bounds - // and we don't want artifacts at the edges. - self.bubbleView.bubbleColor = nil; + if (self.shouldShowSenderName) { + [self configureSenderNameLabel]; + [textViews addObject:self.senderNameLabel]; } if (self.isQuotedReply) { + // Flush any pending "text" subviews. + [self insertTextViewsIntoStackViewIfNecessary:textViews]; + [textViews removeAllObjects]; + BOOL isOutgoing = [self.viewItem.interaction isKindOfClass:TSOutgoingMessage.class]; DisplayableText *_Nullable displayableQuotedText = (self.viewItem.hasQuotedText ? self.viewItem.displayableQuotedText : nil); @@ -328,10 +315,17 @@ NS_ASSUME_NONNULL_BEGIN break; } + BOOL shouldFooterOverlayMedia = NO; if (bodyMediaView) { OWSAssert(self.loadCellContentBlock); OWSAssert(self.unloadCellContentBlock); + shouldFooterOverlayMedia = self.canFooterOverlayMedia; + + // Flush any pending "text" subviews. + [self insertTextViewsIntoStackViewIfNecessary:textViews]; + [textViews removeAllObjects]; + bodyMediaView.clipsToBounds = YES; self.bodyMediaView = bodyMediaView; @@ -359,70 +353,122 @@ NS_ASSUME_NONNULL_BEGIN } } - UIStackView *_Nullable textStackView = nil; - // We render malformed messages as "empty text" messages, // so create a text view if there is no body media view. if (self.hasBodyText || !bodyMediaView) { - OWSMessageTextView *_Nullable bodyTextView = nil; - bodyTextView = [self configureBodyTextView]; - - textStackView = [UIStackView new]; - textStackView.axis = UILayoutConstraintAxisVertical; - textStackView.alignment = UIStackViewAlignmentFill; - // TODO: Review - textStackView.spacing = self.textViewVSpacing; - textStackView.layoutMarginsRelativeArrangement = YES; - textStackView.layoutMargins = UIEdgeInsetsMake(self.conversationStyle.textInsetTop, - self.conversationStyle.textInsetHorizontal, - self.conversationStyle.textInsetBottom, - self.conversationStyle.textInsetHorizontal); - [self.stackView addArrangedSubview:textStackView]; - [textStackView addArrangedSubview:bodyTextView]; + [self configureBodyTextView]; + [textViews addObject:self.bodyTextView]; [self.viewConstraints addObjectsFromArray:@[ - [bodyTextView autoSetDimension:ALDimensionHeight toSize:bodyTextContentSize.height], + [self.bodyTextView autoSetDimension:ALDimensionHeight toSize:bodyTextContentSize.height], ]]; UIView *_Nullable tapForMoreLabel = [self createTapForMoreLabelIfNecessary]; if (tapForMoreLabel) { - [textStackView addArrangedSubview:tapForMoreLabel]; + [textViews addObject:tapForMoreLabel]; [self.viewConstraints addObjectsFromArray:@[ [tapForMoreLabel autoSetDimension:ALDimensionHeight toSize:self.tapForMoreHeight], ]]; } } - OWSMessageFooterView *footerView = self.footerView; - [footerView configureWithConversationViewItem:self.viewItem]; - if (textStackView) { - // Display footer below text. - [textStackView addArrangedSubview:self.footerView]; - [self.footerView setHasShadows:NO viewItem:self.viewItem]; - } else if (bodyMediaView) { + if (self.viewItem.shouldHideFooter) { + // Do nothing. + } else if (shouldFooterOverlayMedia) { + OWSAssert(bodyMediaView); // Display footer over media. - [bodyMediaView addSubview:footerView]; + [self.footerView configureWithConversationViewItem:self.viewItem hasShadows:YES]; + [bodyMediaView addSubview:self.footerView]; bodyMediaView.layoutMargins = UIEdgeInsetsZero; [self.viewConstraints addObjectsFromArray:@[ - [footerView autoPinLeadingToSuperviewMarginWithInset:self.conversationStyle.textInsetHorizontal], - [footerView autoPinTrailingToSuperviewMarginWithInset:self.conversationStyle.textInsetHorizontal], - [footerView autoPinBottomToSuperviewMarginWithInset:self.conversationStyle.textInsetBottom], + [self.footerView autoPinLeadingToSuperviewMarginWithInset:self.conversationStyle.textInsetHorizontal], + [self.footerView autoPinTrailingToSuperviewMarginWithInset:self.conversationStyle.textInsetHorizontal], + [self.footerView autoPinBottomToSuperviewMarginWithInset:self.conversationStyle.textInsetBottom], ]]; - [self.footerView setHasShadows:YES viewItem:self.viewItem]; } else { - OWSFail(@"%@ could not display footer.", self.logTag); + // Display footer at bottom of message bubble. + [self.footerView configureWithConversationViewItem:self.viewItem hasShadows:NO]; + [textViews addObject:self.footerView]; } - if (textStackView) { - CGSize bubbleSize = [self measureSize]; - [NSLayoutConstraint autoSetPriority:UILayoutPriorityRequired - forConstraints:^{ - [self.viewConstraints addObjectsFromArray:@[ - [self autoSetDimension:ALDimensionWidth toSize:bubbleSize.width], - ]]; - }]; + [self insertTextViewsIntoStackViewIfNecessary:textViews]; + + CGSize bubbleSize = [self measureSize]; + [NSLayoutConstraint autoSetPriority:UILayoutPriorityRequired + forConstraints:^{ + [self.viewConstraints addObjectsFromArray:@[ + [self autoSetDimension:ALDimensionWidth toSize:bubbleSize.width], + ]]; + }]; + + [self updateBubbleColorWithBodyMediaView:bodyMediaView]; +} + +- (void)updateBubbleColorWithBodyMediaView:(nullable UIView *)bodyMediaView +{ + BOOL hasOnlyBodyMediaView = NO; + switch (self.cellType) { + case OWSMessageCellType_Unknown: + case OWSMessageCellType_TextMessage: + case OWSMessageCellType_OversizeTextMessage: + case OWSMessageCellType_GenericAttachment: + case OWSMessageCellType_DownloadingAttachment: + case OWSMessageCellType_ContactShare: + break; + case OWSMessageCellType_StillImage: + case OWSMessageCellType_AnimatedImage: + case OWSMessageCellType_Audio: + case OWSMessageCellType_Video: + hasOnlyBodyMediaView = (bodyMediaView && self.stackView.subviews.count == 1); + break; } + if ([self.viewItem.interaction isKindOfClass:[TSMessage class]] && !hasOnlyBodyMediaView) { + TSMessage *message = (TSMessage *)self.viewItem.interaction; + self.bubbleView.bubbleColor = [self.bubbleFactory bubbleColorWithMessage:message]; + } else { + // Media-only messages should have no background color; they will fill the bubble's bounds + // and we don't want artifacts at the edges. + self.bubbleView.bubbleColor = nil; + } +} + +- (BOOL)canFooterOverlayMedia +{ + switch (self.cellType) { + case OWSMessageCellType_Unknown: + case OWSMessageCellType_TextMessage: + case OWSMessageCellType_OversizeTextMessage: + return NO; + case OWSMessageCellType_StillImage: + case OWSMessageCellType_AnimatedImage: + case OWSMessageCellType_Video: + return YES; + case OWSMessageCellType_Audio: + case OWSMessageCellType_GenericAttachment: + case OWSMessageCellType_DownloadingAttachment: + case OWSMessageCellType_ContactShare: + return NO; + } +} + +- (void)insertTextViewsIntoStackViewIfNecessary:(NSArray *)textViews +{ + if (textViews.count < 1) { + return; + } + + UIStackView *textStackView = [[UIStackView alloc] initWithArrangedSubviews:textViews]; + textStackView.axis = UILayoutConstraintAxisVertical; + textStackView.alignment = UIStackViewAlignmentFill; + // TODO: Review + textStackView.spacing = self.textViewVSpacing; + textStackView.layoutMarginsRelativeArrangement = YES; + textStackView.layoutMargins = UIEdgeInsetsMake(self.conversationStyle.textInsetTop, + self.conversationStyle.textInsetHorizontal, + self.conversationStyle.textInsetBottom, + self.conversationStyle.textInsetHorizontal); + [self.stackView addArrangedSubview:textStackView]; } // We now eagerly create our view hierarchy (to do this exactly once per cell usage) @@ -464,7 +510,7 @@ NS_ASSUME_NONNULL_BEGIN - (CGFloat)textViewVSpacing { - return 5.f; + return 2.f; } #pragma mark - Load / Unload @@ -485,7 +531,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Subviews -- (OWSMessageTextView *)configureBodyTextView +- (void)configureBodyTextView { OWSAssert(self.hasBodyText); @@ -501,7 +547,6 @@ NS_ASSUME_NONNULL_BEGIN textColor:self.bodyTextColor font:self.textMessageFont shouldIgnoreEvents:shouldIgnoreEvents]; - return self.bodyTextView; } + (void)loadForTextDisplay:(OWSMessageTextView *)textView @@ -525,6 +570,22 @@ NS_ASSUME_NONNULL_BEGIN textView.text = text; } +- (BOOL)shouldShowSenderName +{ + return self.viewItem.senderName.length > 0; +} + +- (void)configureSenderNameLabel +{ + OWSAssert(self.senderNameLabel); + OWSAssert(self.shouldShowSenderName); + + self.senderNameLabel.text = self.viewItem.senderName.uppercaseString; + self.senderNameLabel.textColor = self.bodyTextColor; + self.senderNameLabel.font = UIFont.ows_dynamicTypeCaption2Font; + self.senderNameLabel.lineBreakMode = NSLineBreakByTruncatingTail; +} + - (BOOL)hasTapForMore { if (!self.hasBodyText) { @@ -854,9 +915,9 @@ NS_ASSUME_NONNULL_BEGIN const int maxTextWidth = (int)floor(self.conversationStyle.maxMessageWidth - hMargins); - OWSMessageTextView *bodyTextView = [self configureBodyTextView]; + [self configureBodyTextView]; const int kMaxIterations = 5; - CGSize result = [bodyTextView compactSizeThatFitsMaxWidth:maxTextWidth maxIterations:kMaxIterations]; + CGSize result = [self.bodyTextView compactSizeThatFitsMaxWidth:maxTextWidth maxIterations:kMaxIterations]; if (includeMargins) { result.width += hMargins; @@ -961,6 +1022,34 @@ NS_ASSUME_NONNULL_BEGIN return CGSizeCeil(result); } +- (CGSize)senderNameSizeWithBodyMediaSize:(CGSize)bodyMediaSize includeMargins:(BOOL)includeMargins +{ + OWSAssert(self.conversationStyle); + OWSAssert(self.conversationStyle.maxMessageWidth > 0); + + if (!self.shouldShowSenderName) { + return CGSizeZero; + } + + CGFloat hMargins = self.conversationStyle.textInsetHorizontal * 2; + const int maxTextWidth = (int)floor(self.conversationStyle.maxMessageWidth - hMargins); + [self configureSenderNameLabel]; + CGSize result = CGSizeCeil([self.senderNameLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]); + + BOOL hasSeparateTextStackView = (self.isQuotedReply || bodyMediaSize.width > 0 || bodyMediaSize.height > 0); + + if (includeMargins) { + result.width += hMargins; + + if (hasSeparateTextStackView) { + result.height += (self.conversationStyle.textInsetTop + self.conversationStyle.textInsetBottom); + } else { + result.height += self.textViewVSpacing; + } + } + return result; +} + - (CGSize)measureSize { OWSAssert(self.conversationStyle); @@ -970,13 +1059,20 @@ NS_ASSUME_NONNULL_BEGIN CGSize cellSize = CGSizeZero; + // TODO: Reflect "sender name" and "footer" layout. + // shouldFooterOverlayMedia = self.canFooterOverlayMedia; + CGSize quotedMessageSize = [self quotedMessageSize]; cellSize.width = MAX(cellSize.width, quotedMessageSize.width); cellSize.height += quotedMessageSize.height; - CGSize mediaContentSize = [self bodyMediaSize]; - cellSize.width = MAX(cellSize.width, mediaContentSize.width); - cellSize.height += mediaContentSize.height; + CGSize bodyMediaSize = [self bodyMediaSize]; + cellSize.width = MAX(cellSize.width, bodyMediaSize.width); + cellSize.height += bodyMediaSize.height; + + CGSize senderNameSize = [self senderNameSizeWithBodyMediaSize:bodyMediaSize includeMargins:YES]; + cellSize.width = MAX(cellSize.width, senderNameSize.width); + cellSize.height += senderNameSize.height; CGSize textContentSize = [self bodyTextSizeWithIncludeMargins:YES]; cellSize.width = MAX(cellSize.width, textContentSize.width); @@ -993,10 +1089,13 @@ NS_ASSUME_NONNULL_BEGIN // TODO: Update this to reflect generic attachment, downloading attachments and // contact shares. - if (self.hasFooter && self.hasBodyText) { + if (!self.viewItem.shouldHideFooter && !self.canFooterOverlayMedia) { CGSize footerSize = [self.footerView measureWithConversationViewItem:self.viewItem]; cellSize.width = MAX(cellSize.width, footerSize.width + self.conversationStyle.textInsetHorizontal * 2); cellSize.height += self.textViewVSpacing + footerSize.height; + if (!self.hasBodyText) { + cellSize.height += (self.conversationStyle.textInsetTop + self.conversationStyle.textInsetBottom); + } } cellSize = CGSizeCeil(cellSize); @@ -1004,12 +1103,6 @@ NS_ASSUME_NONNULL_BEGIN return cellSize; } -- (BOOL)hasFooter -{ - // TODO: - return YES; -} - - (UIFont *)tapForMoreFont { return UIFont.ows_dynamicTypeCaption1Font; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index d815afea6..fba184f8f 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -15,12 +15,6 @@ NS_ASSUME_NONNULL_BEGIN // The non-nullable properties are so frequently used that it's easier // to always keep one around. -// The cell's contentView contains: -// -// * MessageView (message) -// * dateHeaderLabel (above message) -// * footerView (below message) - @property (nonatomic) OWSMessageBubbleView *messageBubbleView; @property (nonatomic) UIView *dateHeaderView; @property (nonatomic) UIView *dateStrokeView; @@ -282,13 +276,15 @@ NS_ASSUME_NONNULL_BEGIN // Returns YES IFF the avatar view is appropriate and configured. - (BOOL)updateAvatarView { + if (!self.viewItem.shouldShowSenderAvatar) { + return NO; + } if (!self.viewItem.isGroupThread) { + OWSFail(@"%@ not a group thread.", self.logTag); return NO; } if (self.viewItem.interaction.interactionType != OWSInteractionType_IncomingMessage) { - return NO; - } - if (self.viewItem.shouldHideAvatar) { + OWSFail(@"%@ not an incoming message.", self.logTag); return NO; } @@ -322,13 +318,15 @@ NS_ASSUME_NONNULL_BEGIN { OWSAssertIsOnMainThread(); + if (!self.viewItem.shouldShowSenderAvatar) { + return; + } if (!self.viewItem.isGroupThread) { + OWSFail(@"%@ not a group thread.", self.logTag); return; } if (self.viewItem.interaction.interactionType != OWSInteractionType_IncomingMessage) { - return; - } - if (self.viewItem.shouldHideAvatar) { + OWSFail(@"%@ not an incoming message.", self.logTag); return; } diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.h index 28836b316..6fbe932ad 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.h @@ -8,12 +8,10 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSMessageFooterView : UIStackView -- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem; +- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem hasShadows:(BOOL)hasShadows; - (CGSize)measureWithConversationViewItem:(ConversationViewItem *)viewItem; -- (void)setHasShadows:(BOOL)hasShadows viewItem:(ConversationViewItem *)viewItem; - @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m index dec5be284..35c13d3bc 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m @@ -81,7 +81,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Load -- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem +- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem hasShadows:(BOOL)hasShadows { OWSAssert(viewItem); @@ -98,6 +98,8 @@ NS_ASSUME_NONNULL_BEGIN ]) { subview.hidden = !isOutgoing; } + + [self setHasShadows:hasShadows viewItem:viewItem]; } - (void)configureLabelsWithConversationViewItem:(ConversationViewItem *)viewItem diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 9bbb365fa..cdb02ce4e 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -4861,12 +4861,15 @@ typedef enum : NSUInteger { } // Update the "shouldShowDate" property of the view items. + // + // First iterate in reverse order. OWSInteractionType lastInteractionType = OWSInteractionType_Unknown; MessageReceiptStatus lastReceiptStatus = MessageReceiptStatusUploading; NSString *_Nullable lastIncomingSenderId = nil; for (ConversationViewItem *viewItem in viewItems.reverseObjectEnumerator) { - BOOL shouldHideRecipientStatus = NO; - BOOL shouldHideAvatar = NO; + BOOL shouldShowSenderAvatar = NO; + BOOL shouldHideFooter = NO; + OWSInteractionType interactionType = viewItem.interaction.interactionType; if (interactionType == OWSInteractionType_OutgoingMessage) { @@ -4875,27 +4878,51 @@ typedef enum : NSUInteger { [MessageRecipientStatusUtils recipientStatusWithOutgoingMessage:outgoingMessage referenceView:self.view]; - if (outgoingMessage.messageState == TSOutgoingMessageStateFailed) { - // always show "failed to send" status - shouldHideRecipientStatus = NO; - } else { - shouldHideRecipientStatus - = (interactionType == lastInteractionType && receiptStatus == lastReceiptStatus); - } + // Always show "failed to send" status. + shouldHideFooter = (interactionType == lastInteractionType && receiptStatus == lastReceiptStatus + && outgoingMessage.messageState != TSOutgoingMessageStateFailed); lastReceiptStatus = receiptStatus; } else if (interactionType == OWSInteractionType_IncomingMessage) { TSIncomingMessage *incomingMessage = (TSIncomingMessage *)viewItem.interaction; NSString *incomingSenderId = incomingMessage.authorId; OWSAssert(incomingSenderId.length > 0); - shouldHideAvatar = (interactionType == lastInteractionType && + BOOL isCollapsed = (interactionType == lastInteractionType && [NSObject isNullableObject:lastIncomingSenderId equalTo:incomingSenderId]); lastIncomingSenderId = incomingSenderId; + + shouldShowSenderAvatar = viewItem.isGroupThread && !isCollapsed; } lastInteractionType = interactionType; - viewItem.shouldHideRecipientStatus = shouldHideRecipientStatus; - viewItem.shouldHideAvatar = shouldHideAvatar; + viewItem.shouldShowSenderAvatar = shouldShowSenderAvatar; + viewItem.shouldHideFooter = shouldHideFooter; + } + + // Iterate again in forward order. + lastInteractionType = OWSInteractionType_Unknown; + lastReceiptStatus = MessageReceiptStatusUploading; + lastIncomingSenderId = nil; + for (ConversationViewItem *viewItem in viewItems) { + NSString *_Nullable senderName = nil; + + OWSInteractionType interactionType = viewItem.interaction.interactionType; + + if (interactionType == OWSInteractionType_IncomingMessage) { + TSIncomingMessage *incomingMessage = (TSIncomingMessage *)viewItem.interaction; + NSString *incomingSenderId = incomingMessage.authorId; + OWSAssert(incomingSenderId.length > 0); + BOOL isCollapsed = (interactionType == lastInteractionType && + [NSObject isNullableObject:lastIncomingSenderId equalTo:incomingSenderId]); + lastIncomingSenderId = incomingSenderId; + + if (viewItem.isGroupThread && !isCollapsed) { + senderName = [self.contactsManager displayNameForPhoneIdentifier:incomingSenderId]; + } + } + lastInteractionType = interactionType; + + viewItem.senderName = senderName; } self.viewItems = viewItems; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index a29c22fc5..d69ce4b24 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -55,10 +55,9 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, readonly) BOOL hasQuotedText; @property (nonatomic) BOOL shouldShowDate; -// TODO: Consider renaming to shouldHideFooter. -@property (nonatomic) BOOL shouldHideRecipientStatus; -// Used to suppress "group sender" avatars. -@property (nonatomic) BOOL shouldHideAvatar; +@property (nonatomic) BOOL shouldShowSenderAvatar; +@property (nonatomic, nullable) NSString *senderName; +@property (nonatomic) BOOL shouldHideFooter; @property (nonatomic, readonly) ConversationStyle *conversationStyle; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index e5a67aca4..ab7062928 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -149,24 +149,35 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) [self clearCachedLayoutState]; } -- (void)setShouldHideRecipientStatus:(BOOL)shouldHideRecipientStatus +- (void)setShouldShowSenderAvatar:(BOOL)shouldShowSenderAvatar { - if (_shouldHideRecipientStatus == shouldHideRecipientStatus) { + if (_shouldShowSenderAvatar == shouldShowSenderAvatar) { return; } - _shouldHideRecipientStatus = shouldHideRecipientStatus; + _shouldShowSenderAvatar = shouldShowSenderAvatar; [self clearCachedLayoutState]; } -- (void)setShouldHideAvatar:(BOOL)shouldHideAvatar +- (void)setSenderName:(nullable NSString *)senderName { - if (_shouldHideAvatar == shouldHideAvatar) { + if ([NSObject isNullableObject:senderName equalTo:_senderName]) { return; } - _shouldHideAvatar = shouldHideAvatar; + _senderName = senderName; + + [self clearCachedLayoutState]; +} + +- (void)setShouldHideFooter:(BOOL)shouldHideFooter +{ + if (_shouldHideFooter == shouldHideFooter) { + return; + } + + _shouldHideFooter = shouldHideFooter; [self clearCachedLayoutState]; }