Tweak relative timestamps.
This commit is contained in:
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…
Reference in New Issue
Block a user