Rewriting short and long date formatting, both for server and ios. Also loading offline stories when server fails half-way through.

This commit is contained in:
Samuel Clay 2013-10-10 12:58:40 -07:00
parent fe237cb80c
commit a56f44cdef
12 changed files with 169 additions and 45 deletions

View file

@ -611,7 +611,7 @@ def load_single_feed(request, feed_id):
if not include_story_content:
del story['story_content']
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
story['short_parsed_date'] = format_story_link_date__short(story_date, now)
story['short_parsed_date'] = format_story_link_date__short(story_date)
story['long_parsed_date'] = format_story_link_date__long(story_date, now)
if usersub:
story['read_status'] = 1
@ -683,6 +683,9 @@ def load_single_feed(request, feed_id):
# if page <= 1:
# import random
# time.sleep(random.randint(0, 3))
# if page == 2:
# assert False
return data
@ -797,7 +800,7 @@ def load_starred_stories(request):
for story in stories:
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
story['short_parsed_date'] = format_story_link_date__short(story_date, now)
story['short_parsed_date'] = format_story_link_date__short(story_date)
story['long_parsed_date'] = format_story_link_date__long(story_date, now)
starred_date = localtime_for_timezone(story['starred_date'], user.profile.timezone)
story['starred_date'] = format_story_link_date__long(starred_date, now)
@ -960,7 +963,7 @@ def load_river_stories__redis(request):
story['story_hash'] not in unread_feed_story_hashes):
story['read_status'] = 1
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
story['short_parsed_date'] = format_story_link_date__short(story_date, now)
story['short_parsed_date'] = format_story_link_date__short(story_date)
story['long_parsed_date'] = format_story_link_date__long(story_date, now)
if story['story_hash'] in starred_stories:
story['starred'] = True

View file

@ -125,8 +125,8 @@ def load_social_stories(request, user_id, username=None):
story['social_user_id'] = social_user_id
# story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
shared_date = localtime_for_timezone(story['shared_date'], user.profile.timezone)
story['short_parsed_date'] = format_story_link_date__short(shared_date, now)
story['long_parsed_date'] = format_story_link_date__long(shared_date, now)
story['short_parsed_date'] = format_story_link_date__short(shared_date)
story['long_parsed_date'] = format_story_link_date__long(shared_date)
story['read_status'] = 1
if (read_filter == 'all' or query) and socialsub:
@ -279,7 +279,7 @@ def load_river_blurblog(request):
if story['story_hash'] not in unread_feed_story_hashes:
story['read_status'] = 1
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
story['short_parsed_date'] = format_story_link_date__short(story_date, now)
story['short_parsed_date'] = format_story_link_date__short(story_date)
story['long_parsed_date'] = format_story_link_date__long(story_date, now)
if story['story_hash'] in starred_stories:
story['starred'] = True

View file

@ -17,6 +17,7 @@
NSString *storyTitle;
NSString *storyAuthor;
NSString *storyDate;
NSInteger storyTimestamp;
int storyScore;
BOOL isStarred;
BOOL isShared;
@ -44,6 +45,7 @@
@property (nonatomic) NSString *storyTitle;
@property (nonatomic) NSString *storyAuthor;
@property (nonatomic) NSString *storyDate;
@property (nonatomic) NSInteger storyTimestamp;
@property (nonatomic) UIColor *feedColorBar;
@property (nonatomic) UIColor *feedColorBarTopBorder;

View file

@ -23,6 +23,7 @@ static UIFont *indicatorFont = nil;
@synthesize storyTitle;
@synthesize storyAuthor;
@synthesize storyDate;
@synthesize storyTimestamp;
@synthesize storyScore;
@synthesize siteTitle;
@synthesize siteFavicon;
@ -223,7 +224,8 @@ static UIFont *indicatorFont = nil;
}
paragraphStyle.alignment = NSTextAlignmentRight;
[cell.storyDate
NSString *date = [Utilities formatShortDateFromTimestamp:cell.storyTimestamp];
[date
drawInRect:CGRectMake(leftMargin + (rect.size.width) / 2 - 10, storyAuthorDateY, (rect.size.width) / 2 + 10, 15.0)
withAttributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,

View file

@ -301,7 +301,8 @@
- (void)beginOfflineTimer {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
if (!appDelegate.storyLocationsCount && self.feedPage == 1 && !self.isOffline) {
if (!appDelegate.storyLocationsCount && !self.pageFinished &&
self.feedPage == 1 && !self.isOffline) {
self.isShowingOffline = YES;
self.isOffline = YES;
[self showLoadingNotifier];
@ -381,13 +382,11 @@
if (request.isCancelled) {
NSLog(@"Cancelled");
return;
} else if (self.feedPage == 1) {
} else {
self.isOffline = YES;
self.feedPage = 1;
[self loadOfflineStories];
[self showOfflineNotifier];
} else {
[self informError:[request error]];
self.pageFinished = YES;
}
[self.storyTitlesTable reloadData];
}];
@ -574,15 +573,12 @@
if (request.isCancelled) {
NSLog(@"Cancelled");
return;
} else if (self.feedPage == 1) {
} else {
self.isOffline = YES;
self.isShowingOffline = NO;
self.feedPage = 1;
[self loadOfflineStories];
[self showOfflineNotifier];
} else {
[self informError:[request error]];
self.pageFinished = YES;
[self.storyTitlesTable reloadData];
}
}];
[request setCompletionBlock:^(void) {
@ -604,12 +600,11 @@
NSLog(@"Cancelled");
return;
} else if ([request responseStatusCode] >= 500) {
if (self.feedPage == 1) {
self.isOffline = YES;
self.isShowingOffline = NO;
[self loadOfflineStories];
[self showOfflineNotifier];
}
self.isOffline = YES;
self.isShowingOffline = NO;
self.feedPage = 1;
[self loadOfflineStories];
[self showOfflineNotifier];
if ([request responseStatusCode] == 503) {
[self informError:@"In maintenance mode"];
self.pageFinished = YES;
@ -902,8 +897,9 @@
NSString *title = [story objectForKey:@"story_title"];
cell.storyTitle = [title stringByDecodingHTMLEntities];
cell.storyDate = [story objectForKey:@"short_parsed_date"];
cell.storyTimestamp = [[story objectForKey:@"story_timestamp"] integerValue];
cell.isStarred = [[story objectForKey:@"starred"] boolValue];
cell.isShared = [[story objectForKey:@"shared"] boolValue];

View file

@ -1922,7 +1922,7 @@
NSMutableDictionary *newStory = [story mutableCopy];
[newStory setValue:[NSNumber numberWithBool:saved] forKey:@"starred"];
if (saved) {
[newStory setValue:[Utilities formatDateFromTimestamp:nil] forKey:@"starred_date"];
[newStory setValue:[Utilities formatLongDateFromTimestamp:nil] forKey:@"starred_date"];
} else {
[newStory removeObjectForKey:@"starred_date"];
}

View file

@ -360,6 +360,9 @@
}
}
NSString *storyDate = [Utilities formatLongDateFromTimestamp:[[self.activeStory
objectForKey:@"story_timestamp"]
integerValue]];
NSString *storyHeader = [NSString stringWithFormat:@
"<div class=\"NB-header\"><div class=\"NB-header-inner\">"
"<div class=\"NB-story-title\">"
@ -373,7 +376,7 @@
"</div></div>",
storyUnread,
storyTitle,
[self.activeStory objectForKey:@"long_parsed_date"],
storyDate,
storyAuthor,
storyTags,
storyStarred];

View file

@ -22,6 +22,7 @@ void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor
+ (void)saveimagesToDisk;
+ (UIImage *)roundCorneredImage:(UIImage *)orig radius:(CGFloat)r;
+ (NSString *)md5:(NSString *)string;
+ (NSString *)formatDateFromTimestamp:(NSInteger)timestamp;
+ (NSString *)formatLongDateFromTimestamp:(NSInteger)timestamp;
+ (NSString *)formatShortDateFromTimestamp:(NSInteger)timestamp;
@end

View file

@ -154,14 +154,117 @@ static NSMutableDictionary *imageCache;
];
}
+ (NSString *)formatDateFromTimestamp:(NSInteger)timestamp {
+ (NSString *)formatLongDateFromTimestamp:(NSInteger)timestamp {
if (!timestamp) timestamp = [[NSDate date] timeIntervalSince1970];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:(double)timestamp];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"LLLL, d Y h:mma"];
static NSDateFormatter *dateFormatter = nil;
static NSDateFormatter *todayFormatter = nil;
static NSDateFormatter *yesterdayFormatter = nil;
static NSDateFormatter *formatterPeriod = nil;
NSDate *today = [NSDate date];
NSDateComponents *components = [[NSCalendar currentCalendar]
components:NSIntegerMax
fromDate:today];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
NSDate *midnight = [[NSCalendar currentCalendar] dateFromComponents:components];
NSDate *yesterday = [NSDate dateWithTimeInterval:-60*60*24 sinceDate:midnight];
if (!dateFormatter || !todayFormatter || !yesterdayFormatter || !formatterPeriod) {
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"EEEE, MMMM d'Sth', y h:mm"];
todayFormatter = [[NSDateFormatter alloc] init];
[todayFormatter setDateFormat:@"'Today', MMMM d'Sth' h:mm"];
yesterdayFormatter = [[NSDateFormatter alloc] init];
[yesterdayFormatter setDateFormat:@"'Yesterday', MMMM d'Sth' h:mm"];
formatterPeriod = [[NSDateFormatter alloc] init];
[formatterPeriod setDateFormat:@"a"];
}
NSString *dateString;
if ([date compare:midnight] == NSOrderedDescending) {
dateString = [NSString stringWithFormat:@"%@%@",
[todayFormatter stringFromDate:date],
[[formatterPeriod stringFromDate:date] lowercaseString]];
} else if ([date compare:yesterday] == NSOrderedDescending) {
dateString = [NSString stringWithFormat:@"%@%@",
[yesterdayFormatter stringFromDate:date],
[[formatterPeriod stringFromDate:date] lowercaseString]];
} else {
dateString = [NSString stringWithFormat:@"%@%@",
[dateFormatter stringFromDate:date],
[[formatterPeriod stringFromDate:date] lowercaseString]];
}
dateString = [dateString stringByReplacingOccurrencesOfString:@"Sth"
withString:[Utilities suffixForDayInDate:date]];
return [formatter stringFromDate:date];
return dateString;
}
+ (NSString *)formatShortDateFromTimestamp:(NSInteger)timestamp {
if (!timestamp) timestamp = [[NSDate date] timeIntervalSince1970];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:(double)timestamp];
static NSDateFormatter *dateFormatter = nil;
static NSDateFormatter *todayFormatter = nil;
static NSDateFormatter *yesterdayFormatter = nil;
static NSDateFormatter *formatterPeriod = nil;
NSDate *today = [NSDate date];
NSDateComponents *components = [[NSCalendar currentCalendar]
components:NSIntegerMax
fromDate:today];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
NSDate *midnight = [[NSCalendar currentCalendar] dateFromComponents:components];
NSDate *yesterday = [NSDate dateWithTimeInterval:-60*60*24 sinceDate:midnight];
if (!dateFormatter || !todayFormatter || !yesterdayFormatter || !formatterPeriod) {
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"dd LLL y, h:mm"];
todayFormatter = [[NSDateFormatter alloc] init];
[todayFormatter setDateFormat:@"h:mm"];
yesterdayFormatter = [[NSDateFormatter alloc] init];
[yesterdayFormatter setDateFormat:@"'Yesterday', h:mm"];
formatterPeriod = [[NSDateFormatter alloc] init];
[formatterPeriod setDateFormat:@"a"];
}
NSString *dateString;
if ([date compare:midnight] == NSOrderedDescending) {
dateString = [NSString stringWithFormat:@"%@%@",
[todayFormatter stringFromDate:date],
[[formatterPeriod stringFromDate:date] lowercaseString]];
} else if ([date compare:yesterday] == NSOrderedDescending) {
dateString = [NSString stringWithFormat:@"%@%@",
[yesterdayFormatter stringFromDate:date],
[[formatterPeriod stringFromDate:date] lowercaseString]];
} else {
dateString = [NSString stringWithFormat:@"%@%@",
[dateFormatter stringFromDate:date],
[[formatterPeriod stringFromDate:date] lowercaseString]];
}
return dateString;
}
+ (NSString *)suffixForDayInDate:(NSDate *)date {
NSInteger day = [[[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] components:NSDayCalendarUnit fromDate:date] day];
if (day == 11) {
return @"th";
} else if (day % 10 == 1) {
return @"st";
} else if (day % 10 == 2) {
return @"nd";
} else if (day % 10 == 3) {
return @"rd";
} else {
return @"th";
}
}
@end

View file

@ -2606,7 +2606,7 @@
"-all_load",
);
PRODUCT_NAME = NewsBlur;
PROVISIONING_PROFILE = "A0156932-124B-4F8E-8B93-EE6598D778F0";
PROVISIONING_PROFILE = "EB97D956-BB90-4F2F-9919-F71949B04B3F";
TARGETED_DEVICE_FAMILY = "1,2";
"WARNING_CFLAGS[arch=*]" = "-Wall";
};
@ -2638,7 +2638,7 @@
"-all_load",
);
PRODUCT_NAME = NewsBlur;
PROVISIONING_PROFILE = "A0156932-124B-4F8E-8B93-EE6598D778F0";
PROVISIONING_PROFILE = "EB97D956-BB90-4F2F-9919-F71949B04B3F";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};

View file

@ -5,7 +5,7 @@
#import <SystemConfiguration/SystemConfiguration.h>
#import <MobileCoreServices/MobileCoreServices.h>
//#define DEBUG 1
#define DEBUG 1
#ifdef DEBUG
#define BACKGROUND_REFRESH_SECONDS -5

View file

@ -17,25 +17,39 @@ from vendor import reseekfile
# COMMENTS_RE = re.compile('\<![ \r\n\t]*(--([^\-]|[\r\n]|-[^\-])*--[ \r\n\t]*)\>')
COMMENTS_RE = re.compile('\<!--.*?--\>')
def format_story_link_date__short(date, now=None):
if not now: now = datetime.datetime.now()
diff = date.date() - now.date()
if diff.days == 0:
def midnight_today():
return datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
def midnight_yesterday(midnight=None):
if not midnight:
midnight = midnight_today()
return midnight - datetime.timedelta(days=1)
def beginning_of_this_month():
return datetime.datetime.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0)
def format_story_link_date__short(date):
date = date.replace(tzinfo=None)
midnight = midnight_today()
if date > midnight:
return date.strftime('%I:%M%p').lstrip('0').lower()
elif diff.days == 1:
elif date > midnight_yesterday(midnight):
return 'Yesterday, ' + date.strftime('%I:%M%p').lstrip('0').lower()
else:
return date.strftime('%d %b %Y, ') + date.strftime('%I:%M%p').lstrip('0').lower()
def format_story_link_date__long(date, now=None):
if not now: now = datetime.datetime.utcnow()
diff = now.date() - date.date()
if not now:
now = datetime.datetime.now()
date = date.replace(tzinfo=None)
midnight = midnight_today()
parsed_date = DateFormat(date)
if diff.days == 0:
if date > midnight:
return 'Today, ' + parsed_date.format('F jS ') + date.strftime('%I:%M%p').lstrip('0').lower()
elif diff.days == 1:
elif date > midnight_yesterday(midnight):
return 'Yesterday, ' + parsed_date.format('F jS g:ia').replace('.','')
elif date.date().timetuple()[7] == now.date().timetuple()[7]:
elif date > beginning_of_this_month():
return parsed_date.format('l, F jS g:ia').replace('.','')
else:
return parsed_date.format('l, F jS, Y g:ia').replace('.','')