Merge branch 'origin/tweakMessageFooters'

pull/1/head
Matthew Chen 7 years ago
commit 2ab7e644cd

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "double check@1x.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "double check@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "double check@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "sending@1x.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "sending@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "sending@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "check@1x.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "check@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "check@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

@ -11,9 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSMessageFooterView ()
@property (nonatomic) UILabel *timestampLabel;
@property (nonatomic) UIView *spacerView;
@property (nonatomic) UILabel *statusLabel;
@property (nonatomic) UIView *statusIndicatorView;
@property (nonatomic) UIImageView *statusIndicatorImageView;
@end
@ -41,42 +39,32 @@ NS_ASSUME_NONNULL_BEGIN
self.alignment = UIStackViewAlignmentCenter;
self.timestampLabel = [UILabel new];
// TODO: Color
self.timestampLabel.textColor = [UIColor lightGrayColor];
[self addArrangedSubview:self.timestampLabel];
self.spacerView = [UIView new];
[self.spacerView setContentHuggingLow];
[self addArrangedSubview:self.spacerView];
self.statusLabel = [UILabel new];
// TODO: Color
self.statusLabel.textColor = [UIColor lightGrayColor];
[self addArrangedSubview:self.statusLabel];
self.statusIndicatorView = [UIView new];
[self.statusIndicatorView autoSetDimension:ALDimensionWidth toSize:self.statusIndicatorSize];
[self.statusIndicatorView autoSetDimension:ALDimensionHeight toSize:self.statusIndicatorSize];
self.statusIndicatorView.layer.cornerRadius = self.statusIndicatorSize * 0.5f;
[self addArrangedSubview:self.statusIndicatorView];
self.statusIndicatorImageView = [UIImageView new];
[self.statusIndicatorImageView setContentHuggingHigh];
[self addArrangedSubview:self.statusIndicatorImageView];
}
- (void)configureFonts
{
self.timestampLabel.font = UIFont.ows_dynamicTypeCaption2Font;
self.statusLabel.font = UIFont.ows_dynamicTypeCaption2Font;
self.timestampLabel.font = UIFont.ows_dynamicTypeCaption1Font;
}
- (CGFloat)statusIndicatorSize
- (CGFloat)hSpacing
{
// TODO: Review constant.
return 12.f;
return 8.f;
}
- (CGFloat)hSpacing
- (CGFloat)maxImageWidth
{
// TODO: Review constant.
return 8.f;
return 18.f;
}
- (CGFloat)imageHeight
{
return 12.f;
}
#pragma mark - Load
@ -87,19 +75,75 @@ NS_ASSUME_NONNULL_BEGIN
[self configureLabelsWithConversationViewItem:viewItem];
// TODO:
self.statusIndicatorView.backgroundColor = [UIColor orangeColor];
BOOL isOutgoing = (viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage);
// TODO: Constants
for (UIView *subview in @[
self.spacerView,
self.statusLabel,
self.statusIndicatorView,
self.timestampLabel,
self.statusIndicatorImageView,
]) {
subview.hidden = !isOutgoing;
if (hasShadows) {
subview.layer.shadowColor = [UIColor blackColor].CGColor;
subview.layer.shadowOpacity = 0.35f;
subview.layer.shadowOffset = CGSizeZero;
subview.layer.shadowRadius = 0.5f;
} else {
subview.layer.shadowColor = nil;
subview.layer.shadowOpacity = 0.f;
subview.layer.shadowOffset = CGSizeZero;
subview.layer.shadowRadius = 0.f;
}
}
[self setHasShadows:hasShadows viewItem:viewItem];
UIColor *textColor;
if (hasShadows) {
textColor = [UIColor whiteColor];
} else if (viewItem.interaction.interactionType == OWSInteractionType_IncomingMessage) {
textColor = [UIColor colorWithWhite:1.f alpha:0.7f];
} else {
textColor = [UIColor ows_light60Color];
}
self.timestampLabel.textColor = textColor;
if (viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) {
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)viewItem.interaction;
UIImage *_Nullable statusIndicatorImage = nil;
MessageReceiptStatus messageStatus =
[MessageRecipientStatusUtils recipientStatusWithOutgoingMessage:outgoingMessage referenceView:self];
switch (messageStatus) {
case MessageReceiptStatusUploading:
case MessageReceiptStatusSending:
statusIndicatorImage = [UIImage imageNamed:@"message_status_sending"];
break;
case MessageReceiptStatusSent:
case MessageReceiptStatusSkipped:
statusIndicatorImage = [UIImage imageNamed:@"message_status_sent"];
break;
case MessageReceiptStatusDelivered:
case MessageReceiptStatusRead:
statusIndicatorImage = [UIImage imageNamed:@"message_status_delivered"];
break;
case MessageReceiptStatusFailed:
// TODO:
statusIndicatorImage = [UIImage imageNamed:@"message_status_sending"];
break;
}
OWSAssert(statusIndicatorImage);
OWSAssert(statusIndicatorImage.size.width <= self.maxImageWidth);
self.statusIndicatorImageView.image =
[statusIndicatorImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
if (messageStatus == MessageReceiptStatusRead) {
// TODO: Tint the icon with the conversation color.
self.statusIndicatorImageView.tintColor = textColor;
} else {
self.statusIndicatorImageView.tintColor = textColor;
}
self.statusIndicatorImageView.hidden = NO;
} else {
self.statusIndicatorImageView.image = nil;
self.statusIndicatorImageView.hidden = YES;
}
}
- (void)configureLabelsWithConversationViewItem:(ConversationViewItem *)viewItem
@ -109,7 +153,6 @@ NS_ASSUME_NONNULL_BEGIN
[self configureFonts];
self.timestampLabel.text = [DateUtil formatTimestampShort:viewItem.interaction.timestamp];
self.statusLabel.text = [self messageStatusTextForConversationViewItem:viewItem];
}
- (CGSize)measureWithConversationViewItem:(ConversationViewItem *)viewItem
@ -119,11 +162,9 @@ NS_ASSUME_NONNULL_BEGIN
[self configureLabelsWithConversationViewItem:viewItem];
CGSize result = CGSizeZero;
result.height
= MAX(self.timestampLabel.font.lineHeight, MAX(self.statusLabel.font.lineHeight, self.statusIndicatorSize));
result.height = MAX(self.timestampLabel.font.lineHeight, self.imageHeight);
if (viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) {
result.width = ([self.timestampLabel sizeThatFits:CGSizeZero].width +
[self.statusLabel sizeThatFits:CGSizeZero].width + self.statusIndicatorSize + self.hSpacing * 3.f);
result.width = ([self.timestampLabel sizeThatFits:CGSizeZero].width + self.maxImageWidth + self.hSpacing);
} else {
result.width = [self.timestampLabel sizeThatFits:CGSizeZero].width;
}
@ -143,42 +184,6 @@ NS_ASSUME_NONNULL_BEGIN
return statusMessage;
}
#pragma mark - Shadows
- (void)setHasShadows:(BOOL)hasShadows viewItem:(ConversationViewItem *)viewItem
{
// TODO: Constants
for (UIView *subview in @[
self.timestampLabel,
self.statusLabel,
self.statusIndicatorView,
]) {
if (hasShadows) {
subview.layer.shadowColor = [UIColor blackColor].CGColor;
subview.layer.shadowOpacity = 0.35f;
subview.layer.shadowOffset = CGSizeZero;
subview.layer.shadowRadius = 0.5f;
} else {
subview.layer.shadowColor = nil;
subview.layer.shadowOpacity = 0.f;
subview.layer.shadowOffset = CGSizeZero;
subview.layer.shadowRadius = 0.f;
}
}
UIColor *textColor;
if (hasShadows) {
textColor = [UIColor whiteColor];
} else if (viewItem.interaction.interactionType == OWSInteractionType_IncomingMessage) {
// TODO:
textColor = [UIColor lightGrayColor];
} else {
textColor = [UIColor whiteColor];
}
self.timestampLabel.textColor = textColor;
self.statusLabel.textColor = textColor;
}
@end
NS_ASSUME_NONNULL_END

@ -4860,17 +4860,19 @@ typedef enum : NSUInteger {
previousViewItemTimestamp = viewItem.interaction.timestampForSorting;
}
// Update the "shouldShowDate" property of the view items.
// Update the properties 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) {
// NOTE: This logic uses shouldShowDate which is set in the previous pass.
for (NSUInteger i = 0; i < viewItems.count; i++) {
ConversationViewItem *viewItem = viewItems[i];
ConversationViewItem *_Nullable previousViewItem = (i > 0 ? viewItems[i - 1] : nil);
ConversationViewItem *_Nullable nextViewItem = (i + 1 < viewItems.count ? viewItems[i + 1] : nil);
BOOL shouldShowSenderAvatar = NO;
BOOL shouldHideFooter = NO;
NSString *_Nullable senderName = nil;
OWSInteractionType interactionType = viewItem.interaction.interactionType;
NSString *timestampText = [DateUtil formatTimestampShort:viewItem.interaction.timestamp];
if (interactionType == OWSInteractionType_OutgoingMessage) {
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)viewItem.interaction;
@ -4878,50 +4880,69 @@ typedef enum : NSUInteger {
[MessageRecipientStatusUtils recipientStatusWithOutgoingMessage:outgoingMessage
referenceView:self.view];
// Always show "failed to send" status.
shouldHideFooter = (interactionType == lastInteractionType && receiptStatus == lastReceiptStatus
&& outgoingMessage.messageState != TSOutgoingMessageStateFailed);
lastReceiptStatus = receiptStatus;
if (nextViewItem && nextViewItem.interaction.interactionType == interactionType) {
TSOutgoingMessage *nextOutgoingMessage = (TSOutgoingMessage *)nextViewItem.interaction;
MessageReceiptStatus nextReceiptStatus =
[MessageRecipientStatusUtils recipientStatusWithOutgoingMessage:nextOutgoingMessage
referenceView:self.view];
NSString *nextTimestampText = [DateUtil formatTimestampShort:nextViewItem.interaction.timestamp];
// We can skip the "outgoing message status" footer if the next message
// has the same footer and no "date break" separates us...
// ...but always show "failed to send" status.
shouldHideFooter = ([timestampText isEqualToString:nextTimestampText]
&& receiptStatus == nextReceiptStatus
&& outgoingMessage.messageState != TSOutgoingMessageStateFailed && !nextViewItem.shouldShowDate);
}
} else 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;
shouldShowSenderAvatar = viewItem.isGroupThread && !isCollapsed;
}
lastInteractionType = interactionType;
viewItem.shouldShowSenderAvatar = shouldShowSenderAvatar;
viewItem.shouldHideFooter = shouldHideFooter;
}
if (nextViewItem && nextViewItem.interaction.interactionType == interactionType) {
NSString *nextTimestampText = [DateUtil formatTimestampShort:nextViewItem.interaction.timestamp];
// We can skip the "incoming message status" footer if the next message
// has the same footer and no "date break" separates us.
shouldHideFooter = ([timestampText isEqualToString:nextTimestampText] && !nextViewItem.shouldShowDate);
}
// Iterate again in forward order.
lastInteractionType = OWSInteractionType_Unknown;
lastReceiptStatus = MessageReceiptStatusUploading;
lastIncomingSenderId = nil;
for (ConversationViewItem *viewItem in viewItems) {
NSString *_Nullable senderName = nil;
if (viewItem.isGroupThread) {
// Show the sender name for incoming group messages unless
// the previous message has the same sender name and
// no "date break" separates us.
BOOL shouldShowSenderName = YES;
if (previousViewItem && previousViewItem.interaction.interactionType == interactionType) {
OWSInteractionType interactionType = viewItem.interaction.interactionType;
TSIncomingMessage *previousIncomingMessage = (TSIncomingMessage *)previousViewItem.interaction;
NSString *previousIncomingSenderId = previousIncomingMessage.authorId;
OWSAssert(previousIncomingSenderId.length > 0);
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;
shouldShowSenderName
= (![NSObject isNullableObject:previousIncomingSenderId equalTo:incomingSenderId]
|| viewItem.shouldShowDate);
}
if (shouldShowSenderName) {
senderName = [self.contactsManager displayNameForPhoneIdentifier:incomingSenderId];
}
if (viewItem.isGroupThread && !isCollapsed) {
senderName = [self.contactsManager displayNameForPhoneIdentifier:incomingSenderId];
// Show the sender avatar for incoming group messages unless
// the next message has the same sender avatar and
// no "date break" separates us.
shouldShowSenderAvatar = YES;
if (nextViewItem && nextViewItem.interaction.interactionType == interactionType) {
TSIncomingMessage *nextIncomingMessage = (TSIncomingMessage *)nextViewItem.interaction;
NSString *nextIncomingSenderId = nextIncomingMessage.authorId;
OWSAssert(nextIncomingSenderId.length > 0);
shouldShowSenderAvatar = (![NSObject isNullableObject:nextIncomingSenderId equalTo:incomingSenderId]
|| nextViewItem.shouldShowDate);
}
}
}
lastInteractionType = interactionType;
viewItem.shouldShowSenderAvatar = shouldShowSenderAvatar;
viewItem.shouldHideFooter = shouldHideFooter;
viewItem.senderName = senderName;
}

Loading…
Cancel
Save