Merge branch 'charlesmchen/senderNames'

pull/1/head
Matthew Chen 7 years ago
commit ec81e15582

@ -25,6 +25,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) UIStackView *stackView; @property (nonatomic) UIStackView *stackView;
@property (nonatomic) UILabel *senderNameLabel;
@property (nonatomic) OWSMessageTextView *bodyTextView; @property (nonatomic) OWSMessageTextView *bodyTextView;
@property (nonatomic, nullable) UIView *quotedMessageView; @property (nonatomic, nullable) UIView *quotedMessageView;
@ -77,6 +79,8 @@ NS_ASSUME_NONNULL_BEGIN
self.stackView.axis = UILayoutConstraintAxisVertical; self.stackView.axis = UILayoutConstraintAxisVertical;
self.stackView.alignment = UIStackViewAlignmentFill; self.stackView.alignment = UIStackViewAlignmentFill;
self.senderNameLabel = [UILabel new];
self.bodyTextView = [self newTextView]; self.bodyTextView = [self newTextView];
// Setting dataDetectorTypes is expensive. Do it just once. // Setting dataDetectorTypes is expensive. Do it just once.
self.bodyTextView.dataDetectorTypes self.bodyTextView.dataDetectorTypes
@ -206,24 +210,6 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - #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 - (BOOL)hasBodyTextContent
{ {
switch (self.cellType) { switch (self.cellType) {
@ -260,17 +246,18 @@ NS_ASSUME_NONNULL_BEGIN
[self.bubbleView addSubview:self.stackView]; [self.bubbleView addSubview:self.stackView];
[self.viewConstraints addObjectsFromArray:[self.stackView autoPinEdgesToSuperviewEdges]]; [self.viewConstraints addObjectsFromArray:[self.stackView autoPinEdgesToSuperviewEdges]];
NSMutableArray<UIView *> *textViews = [NSMutableArray new];
if ([self.viewItem.interaction isKindOfClass:[TSMessage class]] && self.hasBubbleBackground) { if (self.shouldShowSenderName) {
TSMessage *message = (TSMessage *)self.viewItem.interaction; [self configureSenderNameLabel];
self.bubbleView.bubbleColor = [self.bubbleFactory bubbleColorWithMessage:message]; [textViews addObject:self.senderNameLabel];
} 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.isQuotedReply) { if (self.isQuotedReply) {
// Flush any pending "text" subviews.
[self insertAnyTextViewsIntoStackView:textViews];
[textViews removeAllObjects];
BOOL isOutgoing = [self.viewItem.interaction isKindOfClass:TSOutgoingMessage.class]; BOOL isOutgoing = [self.viewItem.interaction isKindOfClass:TSOutgoingMessage.class];
DisplayableText *_Nullable displayableQuotedText DisplayableText *_Nullable displayableQuotedText
= (self.viewItem.hasQuotedText ? self.viewItem.displayableQuotedText : nil); = (self.viewItem.hasQuotedText ? self.viewItem.displayableQuotedText : nil);
@ -328,10 +315,17 @@ NS_ASSUME_NONNULL_BEGIN
break; break;
} }
BOOL shouldFooterOverlayMedia = NO;
if (bodyMediaView) { if (bodyMediaView) {
OWSAssert(self.loadCellContentBlock); OWSAssert(self.loadCellContentBlock);
OWSAssert(self.unloadCellContentBlock); OWSAssert(self.unloadCellContentBlock);
shouldFooterOverlayMedia = self.canFooterOverlayMedia;
// Flush any pending "text" subviews.
[self insertAnyTextViewsIntoStackView:textViews];
[textViews removeAllObjects];
bodyMediaView.clipsToBounds = YES; bodyMediaView.clipsToBounds = YES;
self.bodyMediaView = bodyMediaView; self.bodyMediaView = bodyMediaView;
@ -359,72 +353,124 @@ NS_ASSUME_NONNULL_BEGIN
} }
} }
UIStackView *_Nullable textStackView = nil;
// We render malformed messages as "empty text" messages, // We render malformed messages as "empty text" messages,
// so create a text view if there is no body media view. // so create a text view if there is no body media view.
if (self.hasBodyText || !bodyMediaView) { if (self.hasBodyText || !bodyMediaView) {
OWSMessageTextView *_Nullable bodyTextView = nil; [self configureBodyTextView];
bodyTextView = [self configureBodyTextView]; [textViews addObject:self.bodyTextView];
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.viewConstraints addObjectsFromArray:@[ [self.viewConstraints addObjectsFromArray:@[
[bodyTextView autoSetDimension:ALDimensionHeight toSize:bodyTextContentSize.height], [self.bodyTextView autoSetDimension:ALDimensionHeight toSize:bodyTextContentSize.height],
]]; ]];
UIView *_Nullable tapForMoreLabel = [self createTapForMoreLabelIfNecessary]; UIView *_Nullable tapForMoreLabel = [self createTapForMoreLabelIfNecessary];
if (tapForMoreLabel) { if (tapForMoreLabel) {
[textStackView addArrangedSubview:tapForMoreLabel]; [textViews addObject:tapForMoreLabel];
[self.viewConstraints addObjectsFromArray:@[ [self.viewConstraints addObjectsFromArray:@[
[tapForMoreLabel autoSetDimension:ALDimensionHeight toSize:self.tapForMoreHeight], [tapForMoreLabel autoSetDimension:ALDimensionHeight toSize:self.tapForMoreHeight],
]]; ]];
} }
} }
OWSMessageFooterView *footerView = self.footerView; if (self.viewItem.shouldHideFooter) {
[footerView configureWithConversationViewItem:self.viewItem]; // Do nothing.
if (textStackView) { } else if (shouldFooterOverlayMedia) {
// Display footer below text. OWSAssert(bodyMediaView);
[textStackView addArrangedSubview:self.footerView]; [self.footerView configureWithConversationViewItem:self.viewItem hasShadows:YES];
[self.footerView setHasShadows:NO viewItem:self.viewItem]; [bodyMediaView addSubview:self.footerView];
} else if (bodyMediaView) {
// Display footer over media.
[bodyMediaView addSubview:footerView];
bodyMediaView.layoutMargins = UIEdgeInsetsZero; bodyMediaView.layoutMargins = UIEdgeInsetsZero;
[self.viewConstraints addObjectsFromArray:@[ [self.viewConstraints addObjectsFromArray:@[
[footerView autoPinLeadingToSuperviewMarginWithInset:self.conversationStyle.textInsetHorizontal], [self.footerView autoPinLeadingToSuperviewMarginWithInset:self.conversationStyle.textInsetHorizontal],
[footerView autoPinTrailingToSuperviewMarginWithInset:self.conversationStyle.textInsetHorizontal], [self.footerView autoPinTrailingToSuperviewMarginWithInset:self.conversationStyle.textInsetHorizontal],
[footerView autoPinBottomToSuperviewMarginWithInset:self.conversationStyle.textInsetBottom], [self.footerView autoPinBottomToSuperviewMarginWithInset:self.conversationStyle.textInsetBottom],
]]; ]];
[self.footerView setHasShadows:YES viewItem:self.viewItem];
} else { } else {
OWSFail(@"%@ could not display footer.", self.logTag); [self.footerView configureWithConversationViewItem:self.viewItem hasShadows:NO];
[textViews addObject:self.footerView];
} }
if (textStackView) { [self insertAnyTextViewsIntoStackView:textViews];
CGSize bubbleSize = [self measureSize];
[NSLayoutConstraint autoSetPriority:UILayoutPriorityRequired CGSize bubbleSize = [self measureSize];
forConstraints:^{ [NSLayoutConstraint autoSetPriority:UILayoutPriorityRequired
[self.viewConstraints addObjectsFromArray:@[ forConstraints:^{
[self autoSetDimension:ALDimensionWidth toSize:bubbleSize.width], [self.viewConstraints addObjectsFromArray:@[
]]; [self autoSetDimension:ALDimensionWidth toSize:bubbleSize.width],
}]; ]];
}];
[self updateBubbleColorWithBodyMediaView:bodyMediaView];
}
- (void)updateBubbleColorWithBodyMediaView:(nullable UIView *)bodyMediaView
{
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
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 (!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)insertAnyTextViewsIntoStackView:(NSArray<UIView *> *)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) // We now eagerly create our view hierarchy (to do this exactly once per cell usage)
// but lazy-load any expensive media (photo, gif, etc.) used in those views. Note that // but lazy-load any expensive media (photo, gif, etc.) used in those views. Note that
// this lazy-load can fail, in which case we modify the view hierarchy to use an "error" // this lazy-load can fail, in which case we modify the view hierarchy to use an "error"
@ -464,7 +510,7 @@ NS_ASSUME_NONNULL_BEGIN
- (CGFloat)textViewVSpacing - (CGFloat)textViewVSpacing
{ {
return 5.f; return 2.f;
} }
#pragma mark - Load / Unload #pragma mark - Load / Unload
@ -485,7 +531,7 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - Subviews #pragma mark - Subviews
- (OWSMessageTextView *)configureBodyTextView - (void)configureBodyTextView
{ {
OWSAssert(self.hasBodyText); OWSAssert(self.hasBodyText);
@ -501,7 +547,6 @@ NS_ASSUME_NONNULL_BEGIN
textColor:self.bodyTextColor textColor:self.bodyTextColor
font:self.textMessageFont font:self.textMessageFont
shouldIgnoreEvents:shouldIgnoreEvents]; shouldIgnoreEvents:shouldIgnoreEvents];
return self.bodyTextView;
} }
+ (void)loadForTextDisplay:(OWSMessageTextView *)textView + (void)loadForTextDisplay:(OWSMessageTextView *)textView
@ -525,6 +570,22 @@ NS_ASSUME_NONNULL_BEGIN
textView.text = text; 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 - (BOOL)hasTapForMore
{ {
if (!self.hasBodyText) { if (!self.hasBodyText) {
@ -854,9 +915,9 @@ NS_ASSUME_NONNULL_BEGIN
const int maxTextWidth = (int)floor(self.conversationStyle.maxMessageWidth - hMargins); const int maxTextWidth = (int)floor(self.conversationStyle.maxMessageWidth - hMargins);
OWSMessageTextView *bodyTextView = [self configureBodyTextView]; [self configureBodyTextView];
const int kMaxIterations = 5; const int kMaxIterations = 5;
CGSize result = [bodyTextView compactSizeThatFitsMaxWidth:maxTextWidth maxIterations:kMaxIterations]; CGSize result = [self.bodyTextView compactSizeThatFitsMaxWidth:maxTextWidth maxIterations:kMaxIterations];
if (includeMargins) { if (includeMargins) {
result.width += hMargins; result.width += hMargins;
@ -961,6 +1022,34 @@ NS_ASSUME_NONNULL_BEGIN
return CGSizeCeil(result); 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 - (CGSize)measureSize
{ {
OWSAssert(self.conversationStyle); OWSAssert(self.conversationStyle);
@ -970,13 +1059,20 @@ NS_ASSUME_NONNULL_BEGIN
CGSize cellSize = CGSizeZero; CGSize cellSize = CGSizeZero;
// TODO: Reflect "sender name" and "footer" layout.
// shouldFooterOverlayMedia = self.canFooterOverlayMedia;
CGSize quotedMessageSize = [self quotedMessageSize]; CGSize quotedMessageSize = [self quotedMessageSize];
cellSize.width = MAX(cellSize.width, quotedMessageSize.width); cellSize.width = MAX(cellSize.width, quotedMessageSize.width);
cellSize.height += quotedMessageSize.height; cellSize.height += quotedMessageSize.height;
CGSize mediaContentSize = [self bodyMediaSize]; CGSize bodyMediaSize = [self bodyMediaSize];
cellSize.width = MAX(cellSize.width, mediaContentSize.width); cellSize.width = MAX(cellSize.width, bodyMediaSize.width);
cellSize.height += mediaContentSize.height; 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]; CGSize textContentSize = [self bodyTextSizeWithIncludeMargins:YES];
cellSize.width = MAX(cellSize.width, textContentSize.width); 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 // TODO: Update this to reflect generic attachment, downloading attachments and
// contact shares. // contact shares.
if (self.hasFooter && self.hasBodyText) { if (!self.viewItem.shouldHideFooter && !self.canFooterOverlayMedia) {
CGSize footerSize = [self.footerView measureWithConversationViewItem:self.viewItem]; CGSize footerSize = [self.footerView measureWithConversationViewItem:self.viewItem];
cellSize.width = MAX(cellSize.width, footerSize.width + self.conversationStyle.textInsetHorizontal * 2); cellSize.width = MAX(cellSize.width, footerSize.width + self.conversationStyle.textInsetHorizontal * 2);
cellSize.height += self.textViewVSpacing + footerSize.height; cellSize.height += self.textViewVSpacing + footerSize.height;
if (!self.hasBodyText) {
cellSize.height += (self.conversationStyle.textInsetTop + self.conversationStyle.textInsetBottom);
}
} }
cellSize = CGSizeCeil(cellSize); cellSize = CGSizeCeil(cellSize);
@ -1004,12 +1103,6 @@ NS_ASSUME_NONNULL_BEGIN
return cellSize; return cellSize;
} }
- (BOOL)hasFooter
{
// TODO:
return YES;
}
- (UIFont *)tapForMoreFont - (UIFont *)tapForMoreFont
{ {
return UIFont.ows_dynamicTypeCaption1Font; return UIFont.ows_dynamicTypeCaption1Font;

@ -15,12 +15,6 @@ NS_ASSUME_NONNULL_BEGIN
// The non-nullable properties are so frequently used that it's easier // The non-nullable properties are so frequently used that it's easier
// to always keep one around. // to always keep one around.
// The cell's contentView contains:
//
// * MessageView (message)
// * dateHeaderLabel (above message)
// * footerView (below message)
@property (nonatomic) OWSMessageBubbleView *messageBubbleView; @property (nonatomic) OWSMessageBubbleView *messageBubbleView;
@property (nonatomic) UIView *dateHeaderView; @property (nonatomic) UIView *dateHeaderView;
@property (nonatomic) UIView *dateStrokeView; @property (nonatomic) UIView *dateStrokeView;
@ -282,13 +276,15 @@ NS_ASSUME_NONNULL_BEGIN
// Returns YES IFF the avatar view is appropriate and configured. // Returns YES IFF the avatar view is appropriate and configured.
- (BOOL)updateAvatarView - (BOOL)updateAvatarView
{ {
if (!self.viewItem.isGroupThread) { if (!self.viewItem.shouldShowSenderAvatar) {
return NO; return NO;
} }
if (self.viewItem.interaction.interactionType != OWSInteractionType_IncomingMessage) { if (!self.viewItem.isGroupThread) {
OWSFail(@"%@ not a group thread.", self.logTag);
return NO; return NO;
} }
if (self.viewItem.shouldHideAvatar) { if (self.viewItem.interaction.interactionType != OWSInteractionType_IncomingMessage) {
OWSFail(@"%@ not an incoming message.", self.logTag);
return NO; return NO;
} }
@ -322,13 +318,15 @@ NS_ASSUME_NONNULL_BEGIN
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
if (!self.viewItem.isGroupThread) { if (!self.viewItem.shouldShowSenderAvatar) {
return; return;
} }
if (self.viewItem.interaction.interactionType != OWSInteractionType_IncomingMessage) { if (!self.viewItem.isGroupThread) {
OWSFail(@"%@ not a group thread.", self.logTag);
return; return;
} }
if (self.viewItem.shouldHideAvatar) { if (self.viewItem.interaction.interactionType != OWSInteractionType_IncomingMessage) {
OWSFail(@"%@ not an incoming message.", self.logTag);
return; return;
} }

@ -8,12 +8,10 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSMessageFooterView : UIStackView @interface OWSMessageFooterView : UIStackView
- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem; - (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem hasShadows:(BOOL)hasShadows;
- (CGSize)measureWithConversationViewItem:(ConversationViewItem *)viewItem; - (CGSize)measureWithConversationViewItem:(ConversationViewItem *)viewItem;
- (void)setHasShadows:(BOOL)hasShadows viewItem:(ConversationViewItem *)viewItem;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

@ -81,7 +81,7 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - Load #pragma mark - Load
- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem - (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem hasShadows:(BOOL)hasShadows
{ {
OWSAssert(viewItem); OWSAssert(viewItem);
@ -98,6 +98,8 @@ NS_ASSUME_NONNULL_BEGIN
]) { ]) {
subview.hidden = !isOutgoing; subview.hidden = !isOutgoing;
} }
[self setHasShadows:hasShadows viewItem:viewItem];
} }
- (void)configureLabelsWithConversationViewItem:(ConversationViewItem *)viewItem - (void)configureLabelsWithConversationViewItem:(ConversationViewItem *)viewItem

@ -4861,12 +4861,15 @@ typedef enum : NSUInteger {
} }
// Update the "shouldShowDate" property of the view items. // Update the "shouldShowDate" property of the view items.
//
// First iterate in reverse order.
OWSInteractionType lastInteractionType = OWSInteractionType_Unknown; OWSInteractionType lastInteractionType = OWSInteractionType_Unknown;
MessageReceiptStatus lastReceiptStatus = MessageReceiptStatusUploading; MessageReceiptStatus lastReceiptStatus = MessageReceiptStatusUploading;
NSString *_Nullable lastIncomingSenderId = nil; NSString *_Nullable lastIncomingSenderId = nil;
for (ConversationViewItem *viewItem in viewItems.reverseObjectEnumerator) { for (ConversationViewItem *viewItem in viewItems.reverseObjectEnumerator) {
BOOL shouldHideRecipientStatus = NO; BOOL shouldShowSenderAvatar = NO;
BOOL shouldHideAvatar = NO; BOOL shouldHideFooter = NO;
OWSInteractionType interactionType = viewItem.interaction.interactionType; OWSInteractionType interactionType = viewItem.interaction.interactionType;
if (interactionType == OWSInteractionType_OutgoingMessage) { if (interactionType == OWSInteractionType_OutgoingMessage) {
@ -4875,27 +4878,51 @@ typedef enum : NSUInteger {
[MessageRecipientStatusUtils recipientStatusWithOutgoingMessage:outgoingMessage [MessageRecipientStatusUtils recipientStatusWithOutgoingMessage:outgoingMessage
referenceView:self.view]; referenceView:self.view];
if (outgoingMessage.messageState == TSOutgoingMessageStateFailed) { // Always show "failed to send" status.
// always show "failed to send" status shouldHideFooter = (interactionType == lastInteractionType && receiptStatus == lastReceiptStatus
shouldHideRecipientStatus = NO; && outgoingMessage.messageState != TSOutgoingMessageStateFailed);
} else {
shouldHideRecipientStatus
= (interactionType == lastInteractionType && receiptStatus == lastReceiptStatus);
}
lastReceiptStatus = receiptStatus; lastReceiptStatus = receiptStatus;
} else if (interactionType == OWSInteractionType_IncomingMessage) { } else if (interactionType == OWSInteractionType_IncomingMessage) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)viewItem.interaction; TSIncomingMessage *incomingMessage = (TSIncomingMessage *)viewItem.interaction;
NSString *incomingSenderId = incomingMessage.authorId; NSString *incomingSenderId = incomingMessage.authorId;
OWSAssert(incomingSenderId.length > 0); OWSAssert(incomingSenderId.length > 0);
shouldHideAvatar = (interactionType == lastInteractionType && BOOL isCollapsed = (interactionType == lastInteractionType &&
[NSObject isNullableObject:lastIncomingSenderId equalTo:incomingSenderId]); [NSObject isNullableObject:lastIncomingSenderId equalTo:incomingSenderId]);
lastIncomingSenderId = incomingSenderId; lastIncomingSenderId = incomingSenderId;
shouldShowSenderAvatar = viewItem.isGroupThread && !isCollapsed;
}
lastInteractionType = interactionType;
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; lastInteractionType = interactionType;
viewItem.shouldHideRecipientStatus = shouldHideRecipientStatus; viewItem.senderName = senderName;
viewItem.shouldHideAvatar = shouldHideAvatar;
} }
self.viewItems = viewItems; self.viewItems = viewItems;

@ -55,10 +55,9 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
@property (nonatomic, readonly) BOOL hasQuotedText; @property (nonatomic, readonly) BOOL hasQuotedText;
@property (nonatomic) BOOL shouldShowDate; @property (nonatomic) BOOL shouldShowDate;
// TODO: Consider renaming to shouldHideFooter. @property (nonatomic) BOOL shouldShowSenderAvatar;
@property (nonatomic) BOOL shouldHideRecipientStatus; @property (nonatomic, nullable) NSString *senderName;
// Used to suppress "group sender" avatars. @property (nonatomic) BOOL shouldHideFooter;
@property (nonatomic) BOOL shouldHideAvatar;
@property (nonatomic, readonly) ConversationStyle *conversationStyle; @property (nonatomic, readonly) ConversationStyle *conversationStyle;

@ -149,24 +149,35 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
[self clearCachedLayoutState]; [self clearCachedLayoutState];
} }
- (void)setShouldHideRecipientStatus:(BOOL)shouldHideRecipientStatus - (void)setShouldShowSenderAvatar:(BOOL)shouldShowSenderAvatar
{ {
if (_shouldHideRecipientStatus == shouldHideRecipientStatus) { if (_shouldShowSenderAvatar == shouldShowSenderAvatar) {
return; return;
} }
_shouldHideRecipientStatus = shouldHideRecipientStatus; _shouldShowSenderAvatar = shouldShowSenderAvatar;
[self clearCachedLayoutState]; [self clearCachedLayoutState];
} }
- (void)setShouldHideAvatar:(BOOL)shouldHideAvatar - (void)setSenderName:(nullable NSString *)senderName
{ {
if (_shouldHideAvatar == shouldHideAvatar) { if ([NSObject isNullableObject:senderName equalTo:_senderName]) {
return; return;
} }
_shouldHideAvatar = shouldHideAvatar; _senderName = senderName;
[self clearCachedLayoutState];
}
- (void)setShouldHideFooter:(BOOL)shouldHideFooter
{
if (_shouldHideFooter == shouldHideFooter) {
return;
}
_shouldHideFooter = shouldHideFooter;
[self clearCachedLayoutState]; [self clearCachedLayoutState];
} }

Loading…
Cancel
Save