Tweak 'group sender' avatars.

pull/1/head
Matthew Chen 7 years ago
parent e3a13dfd9e
commit 4effa56d50

@ -8,6 +8,7 @@ NS_ASSUME_NONNULL_BEGIN
@class ConversationViewCell; @class ConversationViewCell;
@class ConversationViewItem; @class ConversationViewItem;
@class OWSContactOffersInteraction; @class OWSContactOffersInteraction;
@class OWSContactsManager;
@class TSAttachmentPointer; @class TSAttachmentPointer;
@class TSAttachmentStream; @class TSAttachmentStream;
@class TSInteraction; @class TSInteraction;
@ -47,6 +48,10 @@ NS_ASSUME_NONNULL_BEGIN
- (void)didTapFailedOutgoingMessage:(TSOutgoingMessage *)message; - (void)didTapFailedOutgoingMessage:(TSOutgoingMessage *)message;
#pragma mark - Contacts
- (OWSContactsManager *)contactsManager;
@end @end
#pragma mark - #pragma mark -

@ -3,6 +3,7 @@
// //
#import "OWSMessageCell.h" #import "OWSMessageCell.h"
#import "OWSContactAvatarBuilder.h"
#import "OWSExpirationTimerView.h" #import "OWSExpirationTimerView.h"
#import "OWSMessageBubbleView.h" #import "OWSMessageBubbleView.h"
#import "Signal-Swift.h" #import "Signal-Swift.h"
@ -24,6 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) OWSMessageBubbleView *messageBubbleView; @property (nonatomic) OWSMessageBubbleView *messageBubbleView;
@property (nonatomic) UILabel *dateHeaderLabel; @property (nonatomic) UILabel *dateHeaderLabel;
@property (nonatomic) UIView *footerView; @property (nonatomic) UIView *footerView;
@property (nonatomic) AvatarImageView *avatarView;
@property (nonatomic) UILabel *footerLabel; @property (nonatomic) UILabel *footerLabel;
@property (nonatomic, nullable) OWSExpirationTimerView *expirationTimerView; @property (nonatomic, nullable) OWSExpirationTimerView *expirationTimerView;
@ -71,9 +73,15 @@ NS_ASSUME_NONNULL_BEGIN
self.footerLabel.textColor = [UIColor lightGrayColor]; self.footerLabel.textColor = [UIColor lightGrayColor];
[self.footerView addSubview:self.footerLabel]; [self.footerView addSubview:self.footerLabel];
self.avatarView = [[AvatarImageView alloc] init];
[self.contentView addSubview:self.avatarView];
[self.avatarView autoSetDimension:ALDimensionWidth toSize:self.avatarSize];
[self.avatarView autoSetDimension:ALDimensionHeight toSize:self.avatarSize];
// Hide these views by default. // Hide these views by default.
self.dateHeaderLabel.hidden = YES; self.dateHeaderLabel.hidden = YES;
self.footerLabel.hidden = YES; self.footerLabel.hidden = YES;
self.avatarView.hidden = YES;
[self.messageBubbleView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.dateHeaderLabel]; [self.messageBubbleView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.dateHeaderLabel];
@ -95,6 +103,11 @@ NS_ASSUME_NONNULL_BEGIN
[self addGestureRecognizer:panGesture]; [self addGestureRecognizer:panGesture];
} }
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)setLayoutInfo:(nullable ConversationLayoutInfo *)layoutInfo - (void)setLayoutInfo:(nullable ConversationLayoutInfo *)layoutInfo
{ {
[super setLayoutInfo:layoutInfo]; [super setLayoutInfo:layoutInfo];
@ -168,6 +181,20 @@ NS_ASSUME_NONNULL_BEGIN
[self updateDateHeader]; [self updateDateHeader];
[self updateFooter]; [self updateFooter];
if ([self updateAvatarView]) {
CGFloat avatarBottomMargin = round(self.layoutInfo.lastTextLineAxis - self.avatarSize * 0.5f);
[self.viewConstraints addObjectsFromArray:@[
[self.messageBubbleView autoPinLeadingToTrailingEdgeOfView:self.avatarView offset:8],
// TODO: We actually want a slightly different alignment.
[self.messageBubbleView autoPinEdge:ALEdgeBottom
toEdge:ALEdgeBottom
ofView:self.avatarView
withOffset:avatarBottomMargin],
]];
[self.messageBubbleView logFrameLaterWithLabel:@"messageBubbleView"];
[self.avatarView logFrameLaterWithLabel:@"avatarView"];
}
} }
// * If cell is visible, lazy-load (expensive) view contents. // * If cell is visible, lazy-load (expensive) view contents.
@ -382,6 +409,74 @@ NS_ASSUME_NONNULL_BEGIN
return UIFont.ows_dynamicTypeCaption1Font; return UIFont.ows_dynamicTypeCaption1Font;
} }
#pragma mark - Avatar
// Returns YES IFF the avatar view is appropriate and configured.
- (BOOL)updateAvatarView
{
if (!self.viewItem.isGroupThread) {
return NO;
}
if (self.viewItem.interaction.interactionType != OWSInteractionType_IncomingMessage) {
return NO;
}
if (self.viewItem.shouldHideAvatar) {
return NO;
}
OWSContactsManager *contactsManager = self.delegate.contactsManager;
if (contactsManager == nil) {
OWSFail(@"%@ contactsManager should not be nil", self.logTag);
return NO;
}
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)self.viewItem.interaction;
OWSAvatarBuilder *avatarBuilder = [[OWSContactAvatarBuilder alloc] initWithSignalId:incomingMessage.authorId
diameter:self.avatarSize
contactsManager:contactsManager];
self.avatarView.image = [avatarBuilder build];
self.avatarView.hidden = NO;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(otherUsersProfileDidChange:)
name:kNSNotificationName_OtherUsersProfileDidChange
object:nil];
return YES;
}
- (NSUInteger)avatarSize
{
return 24.f;
}
- (void)otherUsersProfileDidChange:(NSNotification *)notification
{
OWSAssertIsOnMainThread();
if (!self.viewItem.isGroupThread) {
return;
}
if (self.viewItem.interaction.interactionType != OWSInteractionType_IncomingMessage) {
return;
}
if (self.viewItem.shouldHideAvatar) {
return;
}
NSString *recipientId = notification.userInfo[kNSNotificationKey_ProfileRecipientId];
if (recipientId.length == 0) {
return;
}
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)self.viewItem.interaction;
if (![incomingMessage.authorId isEqualToString:recipientId]) {
return;
}
[self updateAvatarView];
}
#pragma mark - Measurement #pragma mark - Measurement
- (CGSize)cellSizeWithTransaction:(YapDatabaseReadTransaction *)transaction - (CGSize)cellSizeWithTransaction:(YapDatabaseReadTransaction *)transaction
@ -421,7 +516,7 @@ NS_ASSUME_NONNULL_BEGIN
} }
} }
#pragma mark - #pragma mark - Reuse
- (void)prepareForReuse - (void)prepareForReuse
{ {
@ -437,12 +532,16 @@ NS_ASSUME_NONNULL_BEGIN
self.dateHeaderLabel.hidden = YES; self.dateHeaderLabel.hidden = YES;
self.footerLabel.text = nil; self.footerLabel.text = nil;
self.footerLabel.hidden = YES; self.footerLabel.hidden = YES;
self.avatarView.image = nil;
self.avatarView.hidden = YES;
[self.expirationTimerView clearAnimations]; [self.expirationTimerView clearAnimations];
[self.expirationTimerView removeFromSuperview]; [self.expirationTimerView removeFromSuperview];
self.expirationTimerView = nil; self.expirationTimerView = nil;
[self hideMenuControllerIfNecessary]; [self hideMenuControllerIfNecessary];
[[NSNotificationCenter defaultCenter] removeObserver:self];
} }
#pragma mark - Notifications #pragma mark - Notifications
@ -673,11 +772,6 @@ NS_ASSUME_NONNULL_BEGIN
self.isPresentingMenuController = NO; self.isPresentingMenuController = NO;
} }
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

@ -70,6 +70,14 @@ public class ConversationLayoutInfo: NSObject {
@objc public var textInsets = OWSDirectionalEdgeInsets.zero @objc public var textInsets = OWSDirectionalEdgeInsets.zero
// We want to align "group sender" avatars with the v-center of the
// "last line" of the message body text - or where it would be for
// non-text content.
//
// This is the distance from that v-center to the bottom of the
// message bubble.
@objc public var lastTextLineAxis: CGFloat = 0
@objc @objc
public required init(thread: TSThread) { public required init(thread: TSThread) {
@ -131,5 +139,6 @@ public class ConversationLayoutInfo: NSObject {
leading: 12, leading: 12,
bottom: textInsetBottom, bottom: textInsetBottom,
trailing: 12) trailing: 12)
lastTextLineAxis = CGFloat(round(12 + messageTextFont.capHeight * 0.5))
} }
} }

@ -4882,7 +4882,7 @@ typedef enum : NSUInteger {
NSString *_Nullable lastIncomingSenderId = nil; NSString *_Nullable lastIncomingSenderId = nil;
for (ConversationViewItem *viewItem in viewItems.reverseObjectEnumerator) { for (ConversationViewItem *viewItem in viewItems.reverseObjectEnumerator) {
BOOL shouldHideRecipientStatus = NO; BOOL shouldHideRecipientStatus = NO;
BOOL shouldHideBubbleTail = NO; BOOL shouldHideAvatar = NO;
OWSInteractionType interactionType = viewItem.interaction.interactionType; OWSInteractionType interactionType = viewItem.interaction.interactionType;
if (interactionType == OWSInteractionType_OutgoingMessage) { if (interactionType == OWSInteractionType_OutgoingMessage) {
@ -4899,14 +4899,12 @@ typedef enum : NSUInteger {
= (interactionType == lastInteractionType && receiptStatus == lastReceiptStatus); = (interactionType == lastInteractionType && receiptStatus == lastReceiptStatus);
} }
shouldHideBubbleTail = interactionType == lastInteractionType;
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);
shouldHideBubbleTail = (interactionType == lastInteractionType && shouldHideAvatar = (interactionType == lastInteractionType &&
[NSObject isNullableObject:lastIncomingSenderId equalTo:incomingSenderId]); [NSObject isNullableObject:lastIncomingSenderId equalTo:incomingSenderId]);
lastIncomingSenderId = incomingSenderId; lastIncomingSenderId = incomingSenderId;
} }
@ -4919,7 +4917,7 @@ typedef enum : NSUInteger {
[rowsThatChangedSize addObject:@(viewItem.previousRow)]; [rowsThatChangedSize addObject:@(viewItem.previousRow)];
} }
viewItem.shouldHideRecipientStatus = shouldHideRecipientStatus; viewItem.shouldHideRecipientStatus = shouldHideRecipientStatus;
viewItem.shouldHideBubbleTail = shouldHideBubbleTail; viewItem.shouldHideAvatar = shouldHideAvatar;
} }
self.viewItems = viewItems; self.viewItems = viewItems;

@ -56,7 +56,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
@property (nonatomic) BOOL shouldShowDate; @property (nonatomic) BOOL shouldShowDate;
@property (nonatomic) BOOL shouldHideRecipientStatus; @property (nonatomic) BOOL shouldHideRecipientStatus;
@property (nonatomic) BOOL shouldHideBubbleTail; // Used to suppress "group sender" avatars.
@property (nonatomic) BOOL shouldHideAvatar;
@property (nonatomic) NSInteger row; @property (nonatomic) NSInteger row;
// During updates, we sometimes need the previous row index // During updates, we sometimes need the previous row index

@ -162,13 +162,13 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
[self clearCachedLayoutState]; [self clearCachedLayoutState];
} }
- (void)setShouldHideBubbleTail:(BOOL)shouldHideBubbleTail - (void)setShouldHideAvatar:(BOOL)shouldHideAvatar
{ {
if (_shouldHideBubbleTail == shouldHideBubbleTail) { if (_shouldHideAvatar == shouldHideAvatar) {
return; return;
} }
_shouldHideBubbleTail = shouldHideBubbleTail; _shouldHideAvatar = shouldHideAvatar;
[self clearCachedLayoutState]; [self clearCachedLayoutState];
} }

@ -64,7 +64,6 @@ NS_ASSUME_NONNULL_BEGIN
_viewConstraints = [NSMutableArray new]; _viewConstraints = [NSMutableArray new];
self.avatarView = [[AvatarImageView alloc] init]; self.avatarView = [[AvatarImageView alloc] init];
[self.contentView addSubview:self.avatarView]; [self.contentView addSubview:self.avatarView];
[self.avatarView autoSetDimension:ALDimensionWidth toSize:self.avatarSize]; [self.avatarView autoSetDimension:ALDimensionWidth toSize:self.avatarSize];
[self.avatarView autoSetDimension:ALDimensionHeight toSize:self.avatarSize]; [self.avatarView autoSetDimension:ALDimensionHeight toSize:self.avatarSize];
@ -125,6 +124,11 @@ NS_ASSUME_NONNULL_BEGIN
[self.unreadLabel setCompressionResistanceHigh]; [self.unreadLabel setCompressionResistanceHigh];
} }
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
+ (NSString *)cellReuseIdentifier + (NSString *)cellReuseIdentifier
{ {
return NSStringFromClass([self class]); return NSStringFromClass([self class]);
@ -418,6 +422,7 @@ NS_ASSUME_NONNULL_BEGIN
self.thread = nil; self.thread = nil;
self.contactsManager = nil; self.contactsManager = nil;
self.avatarView.image = nil;
[self.unreadBadge removeFromSuperview]; [self.unreadBadge removeFromSuperview];

Loading…
Cancel
Save