Ading pull to refresh on feed detail in iphone app. Not done, but also added extra parameter in feed canonical model that gives seconds since last update.

This commit is contained in:
Samuel Clay 2011-08-18 09:56:52 -07:00
parent bffb8a6d98
commit 442012fdeb
17 changed files with 126 additions and 32 deletions

View file

@ -26,6 +26,7 @@ from utils.fields import AutoOneToOneField
from utils.feed_functions import levenshtein_distance from utils.feed_functions import levenshtein_distance
from utils.feed_functions import timelimit, TimeoutError from utils.feed_functions import timelimit, TimeoutError
from utils.feed_functions import relative_timesince from utils.feed_functions import relative_timesince
from utils.feed_functions import seconds_timesince
from utils.story_functions import pre_process_story from utils.story_functions import pre_process_story
from utils.diff import HTMLDiff from utils.diff import HTMLDiff
@ -71,6 +72,7 @@ class Feed(models.Model):
'feed_link': self.feed_link, 'feed_link': self.feed_link,
'num_subscribers': self.num_subscribers, 'num_subscribers': self.num_subscribers,
'updated': relative_timesince(self.last_update), 'updated': relative_timesince(self.last_update),
'updated_seconds_ago': seconds_timesince(self.last_update),
'subs': self.num_subscribers, 'subs': self.num_subscribers,
'favicon_color': self.favicon_color, 'favicon_color': self.favicon_color,
'favicon_fetching': bool(not (self.favicon_not_found or self.favicon_color)) 'favicon_fetching': bool(not (self.favicon_not_found or self.favicon_color))

View file

@ -5,3 +5,5 @@ ServerAliveCountMax=6
StrictHostKeyChecking=no StrictHostKeyChecking=no
Compression=yes Compression=yes
ForwardAgent=yes ForwardAgent=yes
ControlMaster auto
ControlPath /tmp/ssh_mux_%h_%p_%r

View file

@ -1,4 +1,5 @@
# Path to your oh-my-zsh configuration. # Path to your oh-my-zsh configuration.
plugins=(git osx ruby gem github pip rails)
export ZSH=$HOME/.oh-my-zsh export ZSH=$HOME/.oh-my-zsh
# Set to the name theme to load. # Set to the name theme to load.
@ -10,7 +11,8 @@ export CASE_SENSITIVE="true"
export LC_COLLATE='C' export LC_COLLATE='C'
source $ZSH/oh-my-zsh.sh source $ZSH/oh-my-zsh.sh
export PROMPT='%{$fg_bold[green]%}%n@%M:%{$fg_bold[blue]%}%~ $(git_prompt_info)%{$reset_color%}%(!.#.$) '
export DISABLE_AUTO_UPDATE="true"
export PYTHONSTARTUP=$HOME/.pystartup export PYTHONSTARTUP=$HOME/.pystartup
export LSCOLORS='gxgxcxdxBxegedabagacad' export LSCOLORS='gxgxcxdxBxegedabagacad'
@ -32,7 +34,7 @@ zle -N expand-or-complete-with-dots
bindkey "^I" expand-or-complete-with-dots bindkey "^I" expand-or-complete-with-dots
unsetopt LIST_BEEP unsetopt LIST_BEEP
PROMPT='%{$fg_bold[green]%}%n%{$reset_color%}%{$fg[yellow]%}@%M:%{$fg_bold[blue]%}%~%b $(git_prompt_info)%{$reset_color%}%(!.#.$) ' PROMPT='%{$fg_bold[green]%}%n%{$reset_color%}%{$fg_bold[yellow]%}@%M:%{$fg_bold[blue]%}%~%b $(git_prompt_info)%{$reset_color%}%(!.#.$) '
ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg[red]%}%B" ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg[red]%}%B"
ZSH_THEME_GIT_PROMPT_SUFFIX="%b%{$fg[red]%}%{$reset_color%}" ZSH_THEME_GIT_PROMPT_SUFFIX="%b%{$fg[red]%}%{$reset_color%}"

View file

@ -13,6 +13,7 @@ body {
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
background-color: white;
} }
a, a:active, a:hover, a:visited, button { a, a:active, a:hover, a:visited, button {

View file

@ -7,6 +7,7 @@
// //
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "PullToRefreshView.h"
@class NewsBlurAppDelegate; @class NewsBlurAppDelegate;
@ -25,6 +26,7 @@
UISlider * feedScoreSlider; UISlider * feedScoreSlider;
UIBarButtonItem * feedMarkReadButton; UIBarButtonItem * feedMarkReadButton;
UISegmentedControl * intelligenceControl; UISegmentedControl * intelligenceControl;
PullToRefreshView *pull;
} }
- (void)fetchFeedDetail:(int)page; - (void)fetchFeedDetail:(int)page;
@ -34,6 +36,8 @@
- (NSDictionary *)getStoryAtRow:(NSInteger)indexPathRow; - (NSDictionary *)getStoryAtRow:(NSInteger)indexPathRow;
- (void)checkScroll; - (void)checkScroll;
- (void)markedAsRead; - (void)markedAsRead;
- (void)pullToRefreshViewShouldRefresh:(PullToRefreshView *)view;
- (NSDate *)pullToRefreshViewLastUpdated:(PullToRefreshView *)view;
@property (nonatomic, retain) IBOutlet NewsBlurAppDelegate *appDelegate; @property (nonatomic, retain) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic, retain) IBOutlet UITableView *storyTitlesTable; @property (nonatomic, retain) IBOutlet UITableView *storyTitlesTable;
@ -41,6 +45,7 @@
@property (nonatomic, retain) IBOutlet UISlider * feedScoreSlider; @property (nonatomic, retain) IBOutlet UISlider * feedScoreSlider;
@property (nonatomic, retain) IBOutlet UIBarButtonItem * feedMarkReadButton; @property (nonatomic, retain) IBOutlet UIBarButtonItem * feedMarkReadButton;
@property (nonatomic, retain) IBOutlet UISegmentedControl * intelligenceControl; @property (nonatomic, retain) IBOutlet UISegmentedControl * intelligenceControl;
@property (nonatomic, retain) PullToRefreshView *pull;
@property (nonatomic, retain) NSArray * stories; @property (nonatomic, retain) NSArray * stories;
@property (nonatomic, retain) NSMutableData * jsonString; @property (nonatomic, retain) NSMutableData * jsonString;

View file

@ -9,6 +9,7 @@
#import "FeedDetailViewController.h" #import "FeedDetailViewController.h"
#import "NewsBlurAppDelegate.h" #import "NewsBlurAppDelegate.h"
#import "FeedDetailTableCell.h" #import "FeedDetailTableCell.h"
#import "PullToRefreshView.h"
#import "ASIFormDataRequest.h" #import "ASIFormDataRequest.h"
#import "NSString+HTML.h" #import "NSString+HTML.h"
#import "JSON.h" #import "JSON.h"
@ -25,6 +26,7 @@
@synthesize pageFetching; @synthesize pageFetching;
@synthesize pageFinished; @synthesize pageFinished;
@synthesize intelligenceControl; @synthesize intelligenceControl;
@synthesize pull;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
@ -33,6 +35,13 @@
return self; return self;
} }
- (void)viewDidLoad {
pull = [[PullToRefreshView alloc] initWithScrollView:self.storyTitlesTable];
[pull setDelegate:self];
[self.storyTitlesTable addSubview:pull];
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
self.pageFinished = NO; self.pageFinished = NO;
self.title = [appDelegate.activeFeed objectForKey:@"feed_title"]; self.title = [appDelegate.activeFeed objectForKey:@"feed_title"];
@ -82,6 +91,7 @@
[appDelegate release]; [appDelegate release];
[jsonString release]; [jsonString release];
[intelligenceControl release]; [intelligenceControl release];
[pull release];
[super dealloc]; [super dealloc];
} }
@ -188,6 +198,13 @@
[error localizedDescription]); [error localizedDescription]);
self.pageFetching = NO; self.pageFetching = NO;
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
// User clicking on another link before the page loads is OK.
if ([error code] != NSURLErrorCancelled) {
[NewsBlurAppDelegate informError:error];
}
} }
- (UITableViewCell *)makeLoadingCell { - (UITableViewCell *)makeLoadingCell {
@ -415,4 +432,19 @@
return [appDelegate.activeFeedStories objectAtIndex:row]; return [appDelegate.activeFeedStories objectAtIndex:row];
} }
#pragma mark -
#pragma mark PullToRefresh
// called when the user pulls-to-refresh
- (void)pullToRefreshViewShouldRefresh:(PullToRefreshView *)view {
// [self fetchFeedList:NO];
}
// called when the date shown needs to be updated, optional
- (NSDate *)pullToRefreshViewLastUpdated:(PullToRefreshView *)view {
// return self.lastUpdate;
}
@end @end

View file

@ -83,6 +83,7 @@
- (void)markActiveFeedAllRead; - (void)markActiveFeedAllRead;
- (void)calculateStoryLocations; - (void)calculateStoryLocations;
+ (int)computeStoryScore:(NSDictionary *)intelligence; + (int)computeStoryScore:(NSDictionary *)intelligence;
+ (void)informError:(NSError *)error;
@end @end

View file

@ -283,4 +283,15 @@
return score; return score;
} }
+ (void)informError:(NSError *)error {
NSString* localizedDescription = [error localizedDescription];
UIAlertView* alertView = [[UIAlertView alloc]
initWithTitle:@"Error"
message:localizedDescription delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
}
@end @end

View file

@ -178,6 +178,16 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"%@", [NSString stringWithFormat:@"Connection failed: %@", [error description]]); NSLog(@"%@", [NSString stringWithFormat:@"Connection failed: %@", [error description]]);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[MBProgressHUD hideHUDForView:self.view animated:YES];
[pull finishedLoading];
// User clicking on another link before the page loads is OK.
if ([error code] != NSURLErrorCancelled) {
[NewsBlurAppDelegate informError:error];
}
} }
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
@ -329,6 +339,8 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
FeedTableCell *cell = (FeedTableCell *)[tableView dequeueReusableCellWithIdentifier:FeedCellIdentifier]; FeedTableCell *cell = (FeedTableCell *)[tableView dequeueReusableCellWithIdentifier:FeedCellIdentifier];
if (cell == nil) { if (cell == nil) {
cell = [[[FeedTableCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"FeedCellIdentifier"] autorelease]; cell = [[[FeedTableCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"FeedCellIdentifier"] autorelease];
cell.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate];
} }
NSString *folderName = [self.dictFoldersArray objectAtIndex:indexPath.section]; NSString *folderName = [self.dictFoldersArray objectAtIndex:indexPath.section];
@ -438,7 +450,10 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
// NSLog(@"Select Intelligence from %d to %d.", previousLevel, newLevel); // NSLog(@"Select Intelligence from %d to %d.", previousLevel, newLevel);
[self updateFeedsWithIntelligence:previousLevel newLevel:newLevel]; [self updateFeedsWithIntelligence:previousLevel newLevel:newLevel];
} }
// TODO: Refresh cells on screen to show correct unread pills.
for (UITableViewCell *cell in self.feedTitlesTable.visibleCells) {
[cell setNeedsDisplay];
}
} }
- (void)updateFeedsWithIntelligence:(int)previousLevel newLevel:(int)newLevel { - (void)updateFeedsWithIntelligence:(int)previousLevel newLevel:(int)newLevel {
@ -493,14 +508,17 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
} }
} }
BOOL isVisible = !![self.stillVisibleFeeds objectForKey:feedIdStr];
BOOL notDeletedYetVisible = !deleted && BOOL notDeletedYetVisible = !deleted &&
previousLevel != newLevel && previousLevel != newLevel &&
(maxScore < newLevel) && (maxScore < newLevel) &&
[self.stillVisibleFeeds objectForKey:feedIdStr]; isVisible;
if (notDeletedYetVisible) { if (notDeletedYetVisible) {
// NSLog(@"DELETING: %@ - %d - %d - %d - %d", [feed objectForKey:@"feed_title"], maxScore, newLevel, previousLevel, !deleted); // NSLog(@"DELETING: %@ - %d - %d - %d - %d", [feed objectForKey:@"feed_title"], maxScore, newLevel, previousLevel, !deleted);
[deleteIndexPaths addObject:indexPath]; [deleteIndexPaths addObject:indexPath];
[self.stillVisibleFeeds removeObjectForKey:feedIdStr]; [self.stillVisibleFeeds removeObjectForKey:feedIdStr];
} else if (deleted && isVisible) {
[self.stillVisibleFeeds removeObjectForKey:feedIdStr];
} }
} }
} }
@ -508,8 +526,8 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
for (id feedIdStr in [self.stillVisibleFeeds allKeys]) { for (id feedIdStr in [self.stillVisibleFeeds allKeys]) {
NSDictionary *feed = [self.dictFeeds objectForKey:feedIdStr]; NSDictionary *feed = [self.dictFeeds objectForKey:feedIdStr];
int maxScore = [NewsBlurViewController computeMaxScoreForFeed:feed]; int maxScore = [NewsBlurViewController computeMaxScoreForFeed:feed];
// NSLog(@"Still visible: %@ - %d - %d - %d", [feed objectForKey:@"feed_title"], maxScore, newLevel, previousLevel);
if (previousLevel != newLevel && maxScore < newLevel) { if (previousLevel != newLevel && maxScore < newLevel) {
NSLog(@"Still visible: %@ - %d - %d - %d", [feed objectForKey:@"feed_title"], maxScore, newLevel, previousLevel);
[deleteIndexPaths addObject:[self.stillVisibleFeeds objectForKey:feedIdStr]]; [deleteIndexPaths addObject:[self.stillVisibleFeeds objectForKey:feedIdStr]];
[self.stillVisibleFeeds removeObjectForKey:feedIdStr]; [self.stillVisibleFeeds removeObjectForKey:feedIdStr];
} }
@ -634,4 +652,15 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
[appDelegate reloadFeedsView]; [appDelegate reloadFeedsView];
} }
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"%@", [NSString stringWithFormat:@"Connection failed: %@", [error description]]);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
// User clicking on another link before the page loads is OK.
if ([error code] != NSURLErrorCancelled) {
[NewsBlurAppDelegate informError:error];
}
}
@end @end

View file

@ -52,6 +52,5 @@ static const CGFloat kButtonWidth = 48.0f;
- (void)updateTitle:(UIWebView*)aWebView; - (void)updateTitle:(UIWebView*)aWebView;
- (void)updateAddress:(NSURLRequest*)request; - (void)updateAddress:(NSURLRequest*)request;
- (void)updateButtons; - (void)updateButtons;
- (void)informError:(NSError*)error;
@end @end

View file

@ -180,7 +180,7 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
// User clicking on another link before the page loads is OK. // User clicking on another link before the page loads is OK.
if ([error code] != NSURLErrorCancelled) { if ([error code] != NSURLErrorCancelled) {
[self informError:error]; [NewsBlurAppDelegate informError:error];
} }
} }
- (void)updateTitle:(UIWebView*)aWebView - (void)updateTitle:(UIWebView*)aWebView
@ -200,17 +200,6 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
self.back.enabled = self.webView.canGoBack; self.back.enabled = self.webView.canGoBack;
// self.stop.enabled = self.webView.loading; // self.stop.enabled = self.webView.loading;
} }
- (void)informError:(NSError *)error
{
NSString* localizedDescription = [error localizedDescription];
UIAlertView* alertView = [[UIAlertView alloc]
initWithTitle:@"Error"
message:localizedDescription delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
}
- (void)didReceiveMemoryWarning { - (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview. // Releases the view if it doesn't have a superview.
@ -248,9 +237,9 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
NSArray *buttonTitles; NSArray *buttonTitles;
if ([[appDelegate.activeOriginalStoryURL absoluteString] isEqualToString:self.pageUrl.text]) { if ([[appDelegate.activeOriginalStoryURL absoluteString] isEqualToString:self.pageUrl.text]) {
buttonTitles = [NSArray arrayWithObjects:@"Open story in Safari", nil]; buttonTitles = [NSArray arrayWithObjects:@"Open Story in Safari", nil];
} else { } else {
buttonTitles = [NSArray arrayWithObjects:@"Open this page in Safari", @"Open original in Safari", nil]; buttonTitles = [NSArray arrayWithObjects:@"Open this Page in Safari", @"Open Original in Safari", nil];
} }
for (id title in buttonTitles) { for (id title in buttonTitles) {
[options addButtonWithTitle:title]; [options addButtonWithTitle:title];

View file

@ -308,7 +308,7 @@
- (void)didReceiveMemoryWarning { - (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview. // Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning]; [super didReceiveMemoryWarning];
self.activeStoryId = nil;
// Release any cached data, images, etc that aren't in use. // Release any cached data, images, etc that aren't in use.
} }

View file

@ -6,6 +6,8 @@
<string>English</string> <string>English</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string> <string>${PRODUCT_NAME}</string>
<key>CFBundleDocumentTypes</key>
<array/>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string> <string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
@ -24,20 +26,26 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1</string> <string></string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key>
<array/>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0b3</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSMainNibFile</key> <key>NSMainNibFile</key>
<string>MainWindow</string> <string>MainWindow</string>
<key>UIPrerenderedIcon</key>
<true/>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
</array> </array>
<key>UIPrerenderedIcon</key> <key>UTExportedTypeDeclarations</key>
<true/> <array/>
<key>UTImportedTypeDeclarations</key>
<array/>
</dict> </dict>
</plist> </plist>

View file

@ -7,10 +7,14 @@
// //
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "NewsBlurAppDelegate.h"
#import "ABTableViewCell.h" #import "ABTableViewCell.h"
@class NewsBlurAppDelegate;
@interface FeedTableCell : ABTableViewCell { @interface FeedTableCell : ABTableViewCell {
NewsBlurAppDelegate *appDelegate;
NSString *feedTitle; NSString *feedTitle;
UIImage *feedFavicon; UIImage *feedFavicon;
int _positiveCount; int _positiveCount;
@ -21,6 +25,7 @@
NSString *_negativeCountStr; NSString *_negativeCountStr;
} }
@property (nonatomic, retain) NewsBlurAppDelegate *appDelegate;
@property (nonatomic, retain) NSString *feedTitle; @property (nonatomic, retain) NSString *feedTitle;
@property (nonatomic, retain) UIImage *feedFavicon; @property (nonatomic, retain) UIImage *feedFavicon;
@property (assign, nonatomic) int positiveCount; @property (assign, nonatomic) int positiveCount;

View file

@ -6,6 +6,7 @@
// Copyright 2011 NewsBlur. All rights reserved. // Copyright 2011 NewsBlur. All rights reserved.
// //
#import "NewsBlurAppDelegate.h"
#import "FeedTableCell.h" #import "FeedTableCell.h"
#import "ABTableViewCell.h" #import "ABTableViewCell.h"
#import "UIView+TKCategory.h" #import "UIView+TKCategory.h"
@ -26,6 +27,7 @@ static CGFloat *psColors = nil;
@implementation FeedTableCell @implementation FeedTableCell
@synthesize appDelegate;
@synthesize feedTitle; @synthesize feedTitle;
@synthesize feedFavicon; @synthesize feedFavicon;
@synthesize positiveCount = _positiveCount; @synthesize positiveCount = _positiveCount;
@ -138,7 +140,7 @@ static CGFloat *psColors = nil;
drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos) drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos)
withFont:indicatorFont]; withFont:indicatorFont];
} }
if(_neutralCount > 0){ if(_neutralCount > 0 && appDelegate.selectedIntelligence <= 0){
[neutralBackgroundColor set]; [neutralBackgroundColor set];
CGRect rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntOffset, 10, ntWidth, 18); CGRect rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntOffset, 10, ntWidth, 18);
[UIView drawRoundRectangleInRect:rr withRadius:5]; [UIView drawRoundRectangleInRect:rr withRadius:5];
@ -152,7 +154,7 @@ static CGFloat *psColors = nil;
drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos) drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos)
withFont:indicatorFont]; withFont:indicatorFont];
} }
if(_negativeCount > 0){ if(_negativeCount > 0 && appDelegate.selectedIntelligence <= -1){
[negativeBackgroundColor set]; [negativeBackgroundColor set];
CGRect rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntWidth - ntPadding - ngOffset, 10, ngWidth, 18); CGRect rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntWidth - ntPadding - ngOffset, 10, ngWidth, 18);
[UIView drawRoundRectangleInRect:rr withRadius:5]; [UIView drawRoundRectangleInRect:rr withRadius:5];

View file

@ -579,8 +579,8 @@
"-all_load", "-all_load",
"-ObjC", "-ObjC",
); );
PROVISIONING_PROFILE = "7B7D4B1E-A24A-4E63-9C14-F251D6C5B095"; PROVISIONING_PROFILE = "9390CCB7-7064-41BA-A292-53CE11BB3BEB";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "7B7D4B1E-A24A-4E63-9C14-F251D6C5B095"; "PROVISIONING_PROFILE[sdk=iphoneos*]" = "9390CCB7-7064-41BA-A292-53CE11BB3BEB";
RUN_CLANG_STATIC_ANALYZER = YES; RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
}; };
@ -597,8 +597,8 @@
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 4.0; IPHONEOS_DEPLOYMENT_TARGET = 4.0;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
PROVISIONING_PROFILE = "7B7D4B1E-A24A-4E63-9C14-F251D6C5B095"; PROVISIONING_PROFILE = "9390CCB7-7064-41BA-A292-53CE11BB3BEB";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "7B7D4B1E-A24A-4E63-9C14-F251D6C5B095"; "PROVISIONING_PROFILE[sdk=iphoneos*]" = "9390CCB7-7064-41BA-A292-53CE11BB3BEB";
SDKROOT = iphoneos; SDKROOT = iphoneos;
}; };
name = Release; name = Release;

View file

@ -131,6 +131,12 @@ def relative_timeuntil(value):
return _do_timesince(now, chunks, value) return _do_timesince(now, chunks, value)
def seconds_timesince(value):
now = datetime.datetime.utcnow()
delta = now - value
return delta.days * 24 * 60 * 60 + delta.seconds
def format_relative_date(date, future=False): def format_relative_date(date, future=False):
if not date or date < datetime.datetime(2010, 1, 1): if not date or date < datetime.datetime(2010, 1, 1):
return "Soon" return "Soon"