Tweak relative timestamps.

pull/1/head
Matthew Chen 7 years ago
parent 418d33287e
commit 712d6d89e1

@ -1343,6 +1343,7 @@ NS_ASSUME_NONNULL_BEGIN
if (self.hasBottomFooter) {
CGSize footerSize = [self.footerView measureWithConversationViewItem:self.viewItem];
footerSize.width = MIN(footerSize.width, self.conversationStyle.maxMessageWidth);
[textViewSizes addObject:[NSValue valueWithCGSize:footerSize]];
}

@ -168,10 +168,16 @@ NS_ASSUME_NONNULL_BEGIN
self.timestampLabel.text
= NSLocalizedString(@"MESSAGE_STATUS_SEND_FAILED", @"Label indicating that a message failed to send.");
} else {
self.timestampLabel.text = [DateUtil formatTimestampAsTimeShort:viewItem.interaction.timestamp];
self.timestampLabel.text = [DateUtil formatTimestampAsTime:viewItem.interaction.timestamp
maxRelativeDurationMinutes:self.maxRelativeDurationMinutes];
}
}
- (NSInteger)maxRelativeDurationMinutes
{
return 59;
}
- (CGSize)measureWithConversationViewItem:(ConversationViewItem *)viewItem
{
OWSAssert(viewItem);
@ -180,7 +186,35 @@ NS_ASSUME_NONNULL_BEGIN
CGSize result = CGSizeZero;
result.height = MAX(self.timestampLabel.font.lineHeight, self.imageHeight);
result.width = [self.timestampLabel sizeThatFits:CGSizeZero].width;
// Measure the actual current width, to be safe.
CGFloat timestampLabelWidth = [self.timestampLabel sizeThatFits:CGSizeZero].width;
// Measuring the timestamp label's width is non-trivial since its
// contents can be relative the current time. We avoid having
// message bubbles' "visually vibrate" as their timestamp labels
// vary in width. So we try to leave enough space for all possible
// contents of this label.
if ([DateUtil isTimestampRelative:viewItem.interaction.timestamp
maxRelativeDurationMinutes:self.maxRelativeDurationMinutes]) {
// Measure the "now" case.
self.timestampLabel.text = [DateUtil exemplaryNowTimeFormat];
timestampLabelWidth = MAX(timestampLabelWidth, [self.timestampLabel sizeThatFits:CGSizeZero].width);
// Measure the "relative time" case.
// Since this case varies with time, we multiply to leave
// space for the worst case (whose exact value, due to localization,
// is unpredictable).
self.timestampLabel.text =
[DateUtil exemplaryRelativeTimeFormatWithMaxRelativeDurationMinutes:self.maxRelativeDurationMinutes];
timestampLabelWidth = MAX(timestampLabelWidth,
[self.timestampLabel sizeThatFits:CGSizeZero].width + self.timestampLabel.font.lineHeight * 0.5f);
// Re-configure the labels with the current appropriate value in case
// we are configuring this view for display.
[self configureLabelsWithConversationViewItem:viewItem];
}
result.width = timestampLabelWidth;
if (viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) {
if (![self isFailedOutgoingMessage:viewItem]) {
result.width += (self.maxImageWidth + self.hSpacing);

@ -23,8 +23,15 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSString *)formatTimestampShort:(uint64_t)timestamp;
+ (NSString *)formatDateShort:(NSDate *)date;
+ (NSString *)formatTimestampAsTimeShort:(uint64_t)timestamp;
+ (NSString *)formatDateAsTimeShort:(NSDate *)date;
+ (NSString *)formatTimestampAsTime:(uint64_t)timestamp;
+ (NSString *)formatDateAsTime:(NSDate *)date;
+ (NSString *)formatTimestampAsTime:(uint64_t)timestamp
maxRelativeDurationMinutes:(NSInteger)maxRelativeDurationMinutes;
+ (BOOL)isTimestampRelative:(uint64_t)timestamp maxRelativeDurationMinutes:(NSInteger)maxRelativeDurationMinutes;
+ (NSString *)exemplaryNowTimeFormat;
+ (NSString *)exemplaryRelativeTimeFormatWithMaxRelativeDurationMinutes:(NSInteger)maxRelativeDurationMinutes;
+ (BOOL)isSameDayWithTimestamp:(uint64_t)timestamp1 timestamp:(uint64_t)timestamp2;
+ (BOOL)isSameDayWithDate:(NSDate *)date1 date:(NSDate *)date2;

@ -4,6 +4,7 @@
#import "DateUtil.h"
#import <SignalMessaging/NSString+OWS.h>
#import <SignalMessaging/OWSFormat.h>
#import <SignalServiceKit/NSDate+OWS.h>
NS_ASSUME_NONNULL_BEGIN
@ -187,12 +188,12 @@ static NSString *const DATE_FORMAT_WEEKDAY = @"EEEE";
return dateTimeString.uppercaseString;
}
+ (NSString *)formatTimestampAsTimeShort:(uint64_t)timestamp
+ (NSString *)formatTimestampAsTime:(uint64_t)timestamp
{
return [self formatDateAsTimeShort:[NSDate ows_dateWithMillisecondsSince1970:timestamp]];
return [self formatDateAsTime:[NSDate ows_dateWithMillisecondsSince1970:timestamp]];
}
+ (NSString *)formatDateAsTimeShort:(NSDate *)date
+ (NSString *)formatDateAsTime:(NSDate *)date
{
OWSAssert(date);
@ -200,6 +201,56 @@ static NSString *const DATE_FORMAT_WEEKDAY = @"EEEE";
return dateTimeString.uppercaseString;
}
+ (NSString *)formatTimestampAsTime:(uint64_t)timestamp maxRelativeDurationMinutes:(NSInteger)maxRelativeDurationMinutes
{
NSDate *date = [NSDate ows_dateWithMillisecondsSince1970:timestamp];
NSDate *now = [NSDate new];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSInteger minutesDiff
= MAX(0, [[calendar components:NSCalendarUnitMinute fromDate:date toDate:now options:0] minute]);
// Treat anything in the last two minutes as "now", so that we
// don't have to worry about pluralization while formatting.
if (minutesDiff <= 1) {
return NSLocalizedString(@"DATE_NOW", @"The present; the current time.");
} else if (minutesDiff <= maxRelativeDurationMinutes) {
NSString *minutesString = [OWSFormat formatInt:(int)minutesDiff];
return [NSString stringWithFormat:NSLocalizedString(@"DATE_MINUTES_AGO_FORMAT",
@"Format string for a relative time, expressed as a certain number of "
@"minutes in the past. Embeds {{The number of minutes}}."),
minutesString];
} else {
return [self formatDateAsTime:date];
}
}
+ (BOOL)isTimestampRelative:(uint64_t)timestamp maxRelativeDurationMinutes:(NSInteger)maxRelativeDurationMinutes
{
NSDate *date = [NSDate ows_dateWithMillisecondsSince1970:timestamp];
NSDate *now = [NSDate new];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSInteger minutesDiff
= MAX(0, [[calendar components:NSCalendarUnitMinute fromDate:date toDate:now options:0] minute]);
return minutesDiff <= maxRelativeDurationMinutes;
}
+ (NSString *)exemplaryNowTimeFormat
{
return NSLocalizedString(@"DATE_NOW", @"The present; the current time.");
}
+ (NSString *)exemplaryRelativeTimeFormatWithMaxRelativeDurationMinutes:(NSInteger)maxRelativeDurationMinutes
{
NSString *minutesString = [OWSFormat formatInt:(int)maxRelativeDurationMinutes];
return [NSString stringWithFormat:NSLocalizedString(@"DATE_MINUTES_AGO_FORMAT",
@"Format string for a relative time, expressed as a certain number of "
@"minutes in the past. Embeds {{The number of minutes}}."),
minutesString];
}
+ (BOOL)isSameDayWithTimestamp:(uint64_t)timestamp1 timestamp:(uint64_t)timestamp2
{
return [self isSameDayWithDate:[NSDate ows_dateWithMillisecondsSince1970:timestamp1]
@ -211,6 +262,7 @@ static NSString *const DATE_FORMAT_WEEKDAY = @"EEEE";
NSInteger dayDifference = [self daysFromFirstDate:date1 toSecondDate:date2];
return dayDifference == 0;
}
@end
NS_ASSUME_NONNULL_END

@ -611,6 +611,12 @@
/* Title shown while the app is updating its database. */
"DATABASE_VIEW_OVERLAY_TITLE" = "Optimizing Database";
/* Format string for a duration of time, expressed as a certain number of minutes. Embeds {{The number of minutes}}. */
"DATE_MINUTES_AGO_FORMAT" = "%@ Min Ago";
/* The present; the current time. */
"DATE_NOW" = "Now";
/* The current day. */
"DATE_TODAY" = "Today";

@ -168,6 +168,6 @@ CG_INLINE CGSize CGSizeMax(CGSize size1, CGSize size2)
return CGSizeMake(MAX(size1.width, size2.width), MAX(size1.height, size2.height));
}
CGFloat CGHairlineWidth();
CGFloat CGHairlineWidth(void);
NS_ASSUME_NONNULL_END

Loading…
Cancel
Save