|
|
|
//
|
|
|
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "OWSMessageFooterView.h"
|
|
|
|
#import "DateUtil.h"
|
|
|
|
#import "Signal-Swift.h"
|
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
|
|
|
|
@interface OWSMessageFooterView ()
|
|
|
|
|
|
|
|
@property (nonatomic) UILabel *timestampLabel;
|
|
|
|
@property (nonatomic) UIImageView *statusIndicatorImageView;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation OWSMessageFooterView
|
|
|
|
|
|
|
|
// `[UIView init]` invokes `[self initWithFrame:...]`.
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame
|
|
|
|
{
|
|
|
|
if (self = [super initWithFrame:frame]) {
|
|
|
|
[self commontInit];
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)commontInit
|
|
|
|
{
|
|
|
|
// Ensure only called once.
|
|
|
|
OWSAssert(!self.timestampLabel);
|
|
|
|
|
|
|
|
self.layoutMargins = UIEdgeInsetsZero;
|
|
|
|
|
|
|
|
self.axis = UILayoutConstraintAxisHorizontal;
|
|
|
|
self.spacing = self.hSpacing;
|
|
|
|
self.alignment = UIStackViewAlignmentCenter;
|
|
|
|
|
|
|
|
self.timestampLabel = [UILabel new];
|
|
|
|
[self addArrangedSubview:self.timestampLabel];
|
|
|
|
|
|
|
|
self.statusIndicatorImageView = [UIImageView new];
|
|
|
|
[self.statusIndicatorImageView setContentHuggingHigh];
|
|
|
|
[self addArrangedSubview:self.statusIndicatorImageView];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)configureFonts
|
|
|
|
{
|
|
|
|
self.timestampLabel.font = UIFont.ows_dynamicTypeCaption1Font;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)hSpacing
|
|
|
|
{
|
|
|
|
// TODO: Review constant.
|
|
|
|
return 8.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)maxImageWidth
|
|
|
|
{
|
|
|
|
return 18.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)imageHeight
|
|
|
|
{
|
|
|
|
return 12.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Load
|
|
|
|
|
|
|
|
- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem hasShadows:(BOOL)hasShadows
|
|
|
|
{
|
|
|
|
OWSAssert(viewItem);
|
|
|
|
|
|
|
|
[self configureLabelsWithConversationViewItem:viewItem];
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Constants
|
|
|
|
for (UIView *subview in @[
|
|
|
|
self.timestampLabel,
|
|
|
|
self.statusIndicatorImageView,
|
|
|
|
]) {
|
|
|
|
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) {
|
|
|
|
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
|
|
|
|
{
|
|
|
|
OWSAssert(viewItem);
|
|
|
|
|
|
|
|
[self configureFonts];
|
|
|
|
|
|
|
|
self.timestampLabel.text = [DateUtil formatTimestampShort:viewItem.interaction.timestamp];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (CGSize)measureWithConversationViewItem:(ConversationViewItem *)viewItem
|
|
|
|
{
|
|
|
|
OWSAssert(viewItem);
|
|
|
|
|
|
|
|
[self configureLabelsWithConversationViewItem:viewItem];
|
|
|
|
|
|
|
|
CGSize result = CGSizeZero;
|
|
|
|
result.height = MAX(self.timestampLabel.font.lineHeight, self.imageHeight);
|
|
|
|
if (viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) {
|
|
|
|
result.width = ([self.timestampLabel sizeThatFits:CGSizeZero].width + self.maxImageWidth + self.hSpacing);
|
|
|
|
} else {
|
|
|
|
result.width = [self.timestampLabel sizeThatFits:CGSizeZero].width;
|
|
|
|
}
|
|
|
|
return CGSizeCeil(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (nullable NSString *)messageStatusTextForConversationViewItem:(ConversationViewItem *)viewItem
|
|
|
|
{
|
|
|
|
OWSAssert(viewItem);
|
|
|
|
if (viewItem.interaction.interactionType != OWSInteractionType_OutgoingMessage) {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)viewItem.interaction;
|
|
|
|
NSString *statusMessage =
|
|
|
|
[MessageRecipientStatusUtils receiptMessageWithOutgoingMessage:outgoingMessage referenceView:self];
|
|
|
|
return statusMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_END
|