Merge branch 'mkirk/date-breaks'

pull/1/head
Michael Kirk 6 years ago
commit bfebd1202c

@ -8,6 +8,8 @@
NS_ASSUME_NONNULL_BEGIN
extern const CGFloat OWSMessageCellDateHeaderVMargin;
@interface OWSMessageCell : ConversationViewCell
@property (nonatomic, readonly) OWSMessageBubbleView *messageBubbleView;

@ -9,6 +9,8 @@
NS_ASSUME_NONNULL_BEGIN
const CGFloat OWSMessageCellDateHeaderVMargin = 23;
@interface OWSMessageCell ()
// The nullable properties are created as needed.
@ -16,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
// to always keep one around.
@property (nonatomic) OWSMessageBubbleView *messageBubbleView;
@property (nonatomic) UIStackView *dateHeaderView;
@property (nonatomic) UIView *dateHeaderView;
@property (nonatomic) UILabel *dateHeaderLabel;
@property (nonatomic) AvatarImageView *avatarView;
@property (nonatomic, nullable) UIImageView *sendFailureBadgeView;
@ -58,10 +60,10 @@ NS_ASSUME_NONNULL_BEGIN
self.dateHeaderLabel.textAlignment = NSTextAlignmentCenter;
self.dateHeaderLabel.textColor = [UIColor ows_light60Color];
self.dateHeaderView = [[UIStackView alloc] initWithArrangedSubviews:@[
self.dateHeaderLabel,
]];
self.dateHeaderView.axis = NSTextLayoutOrientationVertical;
self.dateHeaderView = [UIView new];
self.dateHeaderView.layoutMargins = UIEdgeInsetsMake(0, 0, OWSMessageCellDateHeaderVMargin, 0);
[self.dateHeaderView addSubview:self.dateHeaderLabel];
[self.dateHeaderLabel autoPinToSuperviewMargins];
self.avatarView = [[AvatarImageView alloc] init];
[self.avatarView autoSetDimension:ALDimensionWidth toSize:self.avatarSize];
@ -253,45 +255,14 @@ NS_ASSUME_NONNULL_BEGIN
{
OWSAssert(self.conversationStyle);
static NSDateFormatter *dateHeaderDateFormatter = nil;
static NSDateFormatter *dateHeaderTimeFormatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dateHeaderDateFormatter = [NSDateFormatter new];
[dateHeaderDateFormatter setLocale:[NSLocale currentLocale]];
[dateHeaderDateFormatter setDoesRelativeDateFormatting:YES];
[dateHeaderDateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateHeaderDateFormatter setTimeStyle:NSDateFormatterNoStyle];
dateHeaderTimeFormatter = [NSDateFormatter new];
[dateHeaderTimeFormatter setLocale:[NSLocale currentLocale]];
[dateHeaderTimeFormatter setDoesRelativeDateFormatting:YES];
[dateHeaderTimeFormatter setDateStyle:NSDateFormatterNoStyle];
[dateHeaderTimeFormatter setTimeStyle:NSDateFormatterShortStyle];
});
if (self.viewItem.shouldShowDate) {
self.dateHeaderLabel.font = self.dateHeaderFont;
self.dateHeaderLabel.textColor = self.conversationStyle.dateBreakTextColor;
NSDate *date = self.viewItem.interaction.dateForSorting;
NSString *dateString = [dateHeaderDateFormatter stringFromDate:date];
NSString *timeString = [dateHeaderTimeFormatter stringFromDate:date];
NSAttributedString *attributedText = [NSAttributedString new];
attributedText = [attributedText rtlSafeAppend:dateString.localizedUppercaseString
attributes:@{
NSFontAttributeName : self.dateHeaderFont,
NSForegroundColorAttributeName : [UIColor lightGrayColor],
}];
attributedText = [attributedText rtlSafeAppend:@" "
attributes:@{
NSFontAttributeName : self.dateHeaderFont,
}];
attributedText = [attributedText rtlSafeAppend:timeString
attributes:@{
NSFontAttributeName : self.dateHeaderFont,
NSForegroundColorAttributeName : [UIColor lightGrayColor],
}];
self.dateHeaderLabel.attributedText = attributedText;
NSString *dateString = [DateUtil formatDateForConversationDateBreaks:date];
self.dateHeaderLabel.text = dateString.localizedUppercaseString;
[self.contentView addSubview:self.dateHeaderView];
[self.viewConstraints addObjectsFromArray:@[
@ -300,16 +271,7 @@ NS_ASSUME_NONNULL_BEGIN
[self.dateHeaderView
autoPinTrailingToSuperviewMarginWithInset:self.conversationStyle.fullWidthGutterTrailing],
[self.dateHeaderView autoPinEdgeToSuperviewEdge:ALEdgeTop],
// DO NOT pin to the bottom of dateHeaderView.
//
// Being a UIStackView, it doesn't reflect the spacing below the date
// header contents. Instead pin using dateHeaderHeight which includes
// the spacing.
[self.messageBubbleView autoPinEdge:ALEdgeTop
toEdge:ALEdgeTop
ofView:self.dateHeaderView
withOffset:self.dateHeaderHeight],
[self.messageBubbleView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.dateHeaderView],
]];
} else {
[self.viewConstraints addObjectsFromArray:@[
@ -425,21 +387,11 @@ NS_ASSUME_NONNULL_BEGIN
return cellSize;
}
- (CGFloat)dateHeaderStrokeThickness
{
return CGHairlineWidth();
}
- (CGFloat)dateHeaderBottomMargin
{
return 20.f;
}
- (CGFloat)dateHeaderHeight
{
if (self.viewItem.shouldShowDate) {
CGFloat textHeight = self.dateHeaderFont.capHeight;
return (CGFloat)ceil(self.dateHeaderStrokeThickness + textHeight + self.dateHeaderBottomMargin);
CGFloat textHeight = self.dateHeaderFont.lineHeight;
return (CGFloat)ceil(textHeight + OWSMessageCellDateHeaderVMargin);
} else {
return 0.f;
}

@ -4879,25 +4879,15 @@ typedef enum : NSUInteger {
OWSAssert(viewItemTimestamp > 0);
BOOL shouldShowDate = NO;
if (!canShowDate) {
shouldShowDate = NO;
if (previousViewItemTimestamp == 0) {
shouldShowDateOnNextViewItem = YES;
} else if (shouldShowDateOnNextViewItem) {
shouldShowDate = YES;
shouldShowDateOnNextViewItem = NO;
} else if (previousViewItemTimestamp > 0
&& ![DateUtil isSameDayWithTimestamp:previousViewItemTimestamp timestamp:viewItemTimestamp]) {
// Ensure we always have a date break between messages on different days.
} else if (![DateUtil isSameDayWithTimestamp:previousViewItemTimestamp timestamp:viewItemTimestamp]) {
shouldShowDateOnNextViewItem = YES;
}
if (shouldShowDateOnNextViewItem && canShowDate) {
shouldShowDate = YES;
shouldShowDateOnNextViewItem = NO;
} else {
OWSAssert(previousViewItemTimestamp > 0);
uint64_t timeDifferenceMs = viewItemTimestamp - previousViewItemTimestamp;
static const uint64_t kShowTimeIntervalMs = 5 * kMinuteInMs;
if (timeDifferenceMs > kShowTimeIntervalMs) {
shouldShowDate = YES;
}
shouldShowDateOnNextViewItem = NO;
}
viewItem.shouldShowDate = shouldShowDate;

@ -256,11 +256,14 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
OWSAssert(previousLayoutItem);
if (self.interaction.interactionType == OWSInteractionType_UnreadIndicator
|| previousLayoutItem.interaction.interactionType == OWSInteractionType_UnreadIndicator
|| self.shouldShowDate) {
|| previousLayoutItem.interaction.interactionType == OWSInteractionType_UnreadIndicator) {
return 20.f;
}
if (self.shouldShowDate) {
return OWSMessageCellDateHeaderVMargin;
}
// "Bubble Collapse". Adjacent messages with the same author should be close together.
if (self.interaction.interactionType == OWSInteractionType_IncomingMessage
&& previousLayoutItem.interaction.interactionType == OWSInteractionType_IncomingMessage) {

@ -34,6 +34,8 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSString *)exemplaryNowTimeFormat;
+ (NSString *)exemplaryMinutesTimeFormat;
+ (NSString *)formatDateForConversationDateBreaks:(NSDate *)date;
+ (BOOL)isSameDayWithTimestamp:(uint64_t)timestamp1 timestamp:(uint64_t)timestamp2;
+ (BOOL)isSameDayWithDate:(NSDate *)date1 date:(NSDate *)date2;

@ -25,6 +25,64 @@ static NSString *const DATE_FORMAT_WEEKDAY = @"EEEE";
return formatter;
}
+ (NSDateFormatter *)dateBreakRelativeDateFormatter
{
static NSDateFormatter *formatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [NSDateFormatter new];
formatter.locale = [NSLocale currentLocale];
formatter.dateStyle = NSDateFormatterShortStyle;
formatter.timeStyle = NSDateFormatterNoStyle;
formatter.doesRelativeDateFormatting = YES;
});
return formatter;
}
+ (NSDateFormatter *)dateBreakThisWeekDateFormatter
{
static NSDateFormatter *formatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [NSDateFormatter new];
formatter.locale = [NSLocale currentLocale];
// "Monday", "Tuesday", etc.
formatter.dateFormat = @"EEEE";
});
return formatter;
}
+ (NSDateFormatter *)dateBreakThisYearDateFormatter
{
static NSDateFormatter *formatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [NSDateFormatter new];
formatter.locale = [NSLocale currentLocale];
// Tue, Jun 6
formatter.dateFormat = @"EE, MMM d";
});
return formatter;
}
+ (NSDateFormatter *)dateBreakOldDateFormatter
{
static NSDateFormatter *formatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [NSDateFormatter new];
formatter.locale = [NSLocale currentLocale];
formatter.dateStyle = NSDateFormatterMediumStyle;
formatter.timeStyle = NSDateFormatterNoStyle;
formatter.doesRelativeDateFormatting = YES;
});
return formatter;
}
+ (NSDateFormatter *)weekdayFormatter {
static NSDateFormatter *formatter;
static dispatch_once_t onceToken;
@ -83,6 +141,17 @@ static NSString *const DATE_FORMAT_WEEKDAY = @"EEEE";
return dayDifference > 0;
}
+ (BOOL)dateIsOlderThanYesterday:(NSDate *)date
{
return [self dateIsOlderThanYesterday:date now:[NSDate date]];
}
+ (BOOL)dateIsOlderThanYesterday:(NSDate *)date now:(NSDate *)now
{
NSInteger dayDifference = [self daysFromFirstDate:date toSecondDate:now];
return dayDifference > 1;
}
+ (BOOL)dateIsOlderThanOneWeek:(NSDate *)date
{
return [self dateIsOlderThanOneWeek:date now:[NSDate date]];
@ -206,6 +275,25 @@ static NSString *const DATE_FORMAT_WEEKDAY = @"EEEE";
return dateTimeString.localizedUppercaseString;
}
+ (NSString *)formatDateForConversationDateBreaks:(NSDate *)date
{
OWSAssert(date);
if (![self dateIsThisYear:date]) {
// last year formatter: Nov 11, 2017
return [self.dateBreakOldDateFormatter stringFromDate:date];
} else if ([self dateIsOlderThanOneWeek:date]) {
// this year formatter: Tue, Jun 23
return [self.dateBreakThisYearDateFormatter stringFromDate:date];
} else if ([self dateIsOlderThanYesterday:date]) {
// day of week formatter: Thursday
return [self.dateBreakThisWeekDateFormatter stringFromDate:date];
} else {
// relative format: Today / Yesterday
return [self.dateBreakRelativeDateFormatter stringFromDate:date];
}
}
+ (NSString *)formatTimestampAsTime:(uint64_t)timestamp
{
return [self formatDateAsTime:[NSDate ows_dateWithMillisecondsSince1970:timestamp]];

@ -30,8 +30,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (class, readonly, nonatomic) UIFont *ows_dynamicTypeTitle2Font;
@property (class, readonly, nonatomic) UIFont *ows_dynamicTypeTitle3Font;
@property (class, readonly, nonatomic) UIFont *ows_dynamicTypeHeadlineFont;
@property (class, readonly, nonatomic) UIFont *ows_dynamicTypeSubheadlineFont;
@property (class, readonly, nonatomic) UIFont *ows_dynamicTypeBodyFont;
@property (class, readonly, nonatomic) UIFont *ows_dynamicTypeSubheadlineFont;
@property (class, readonly, nonatomic) UIFont *ows_dynamicTypeFootnoteFont;
@property (class, readonly, nonatomic) UIFont *ows_dynamicTypeCaption1Font;
@property (class, readonly, nonatomic) UIFont *ows_dynamicTypeCaption2Font;

@ -72,14 +72,14 @@ NS_ASSUME_NONNULL_BEGIN
return [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
}
+ (UIFont *)ows_dynamicTypeSubheadlineFont
+ (UIFont *)ows_dynamicTypeBodyFont
{
return [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
return [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
}
+ (UIFont *)ows_dynamicTypeBodyFont
+ (UIFont *)ows_dynamicTypeSubheadlineFont
{
return [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
return [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
}
+ (UIFont *)ows_dynamicTypeFootnoteFont

@ -145,6 +145,9 @@ public class ConversationStyle: NSObject {
@objc
public let bubbleColorOutgoingSent = UIColor.ows_darkSkyBlue
@objc
public let dateBreakTextColor = UIColor.ows_light60
@objc
public var primaryColor: UIColor

Loading…
Cancel
Save