Tweak sender names.
This commit is contained in:
parent
538194aba7
commit
966e6a1156
|
@ -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<UIView *> *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<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)
|
||||
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue