diff --git a/apps/rss_feeds/models.py b/apps/rss_feeds/models.py index 9bf937be8..d2d325e19 100644 --- a/apps/rss_feeds/models.py +++ b/apps/rss_feeds/models.py @@ -26,6 +26,7 @@ from utils.fields import AutoOneToOneField from utils.feed_functions import levenshtein_distance from utils.feed_functions import timelimit, TimeoutError from utils.feed_functions import relative_timesince +from utils.feed_functions import seconds_timesince from utils.story_functions import pre_process_story from utils.diff import HTMLDiff @@ -71,6 +72,7 @@ class Feed(models.Model): 'feed_link': self.feed_link, 'num_subscribers': self.num_subscribers, 'updated': relative_timesince(self.last_update), + 'updated_seconds_ago': seconds_timesince(self.last_update), 'subs': self.num_subscribers, 'favicon_color': self.favicon_color, 'favicon_fetching': bool(not (self.favicon_not_found or self.favicon_color)) diff --git a/config/ssh.conf b/config/ssh.conf index 41852887e..1a723cbc4 100644 --- a/config/ssh.conf +++ b/config/ssh.conf @@ -5,3 +5,5 @@ ServerAliveCountMax=6 StrictHostKeyChecking=no Compression=yes ForwardAgent=yes +ControlMaster auto +ControlPath /tmp/ssh_mux_%h_%p_%r diff --git a/config/zshrc b/config/zshrc index 89aedb590..5249208d0 100644 --- a/config/zshrc +++ b/config/zshrc @@ -1,4 +1,5 @@ # Path to your oh-my-zsh configuration. +plugins=(git osx ruby gem github pip rails) export ZSH=$HOME/.oh-my-zsh # Set to the name theme to load. @@ -10,7 +11,8 @@ export CASE_SENSITIVE="true" export LC_COLLATE='C' 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 LSCOLORS='gxgxcxdxBxegedabagacad' @@ -32,7 +34,7 @@ zle -N expand-or-complete-with-dots bindkey "^I" expand-or-complete-with-dots 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_SUFFIX="%b%{$fg[red]%}›%{$reset_color%}" diff --git a/media/css/reader.css b/media/css/reader.css index 9fba1c0d6..2364d69f0 100644 --- a/media/css/reader.css +++ b/media/css/reader.css @@ -13,6 +13,7 @@ body { height: 100%; overflow: hidden; text-rendering: optimizeLegibility; + background-color: white; } a, a:active, a:hover, a:visited, button { diff --git a/media/iphone/Classes/FeedDetailViewController.h b/media/iphone/Classes/FeedDetailViewController.h index f5ae01a29..7baa02bd4 100644 --- a/media/iphone/Classes/FeedDetailViewController.h +++ b/media/iphone/Classes/FeedDetailViewController.h @@ -7,6 +7,7 @@ // #import +#import "PullToRefreshView.h" @class NewsBlurAppDelegate; @@ -25,6 +26,7 @@ UISlider * feedScoreSlider; UIBarButtonItem * feedMarkReadButton; UISegmentedControl * intelligenceControl; + PullToRefreshView *pull; } - (void)fetchFeedDetail:(int)page; @@ -34,6 +36,8 @@ - (NSDictionary *)getStoryAtRow:(NSInteger)indexPathRow; - (void)checkScroll; - (void)markedAsRead; +- (void)pullToRefreshViewShouldRefresh:(PullToRefreshView *)view; +- (NSDate *)pullToRefreshViewLastUpdated:(PullToRefreshView *)view; @property (nonatomic, retain) IBOutlet NewsBlurAppDelegate *appDelegate; @property (nonatomic, retain) IBOutlet UITableView *storyTitlesTable; @@ -41,6 +45,7 @@ @property (nonatomic, retain) IBOutlet UISlider * feedScoreSlider; @property (nonatomic, retain) IBOutlet UIBarButtonItem * feedMarkReadButton; @property (nonatomic, retain) IBOutlet UISegmentedControl * intelligenceControl; +@property (nonatomic, retain) PullToRefreshView *pull; @property (nonatomic, retain) NSArray * stories; @property (nonatomic, retain) NSMutableData * jsonString; diff --git a/media/iphone/Classes/FeedDetailViewController.m b/media/iphone/Classes/FeedDetailViewController.m index 16548152d..f482f41ea 100644 --- a/media/iphone/Classes/FeedDetailViewController.m +++ b/media/iphone/Classes/FeedDetailViewController.m @@ -9,6 +9,7 @@ #import "FeedDetailViewController.h" #import "NewsBlurAppDelegate.h" #import "FeedDetailTableCell.h" +#import "PullToRefreshView.h" #import "ASIFormDataRequest.h" #import "NSString+HTML.h" #import "JSON.h" @@ -25,6 +26,7 @@ @synthesize pageFetching; @synthesize pageFinished; @synthesize intelligenceControl; +@synthesize pull; - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { @@ -33,6 +35,13 @@ return self; } +- (void)viewDidLoad { + pull = [[PullToRefreshView alloc] initWithScrollView:self.storyTitlesTable]; + [pull setDelegate:self]; + [self.storyTitlesTable addSubview:pull]; + [super viewDidLoad]; +} + - (void)viewWillAppear:(BOOL)animated { self.pageFinished = NO; self.title = [appDelegate.activeFeed objectForKey:@"feed_title"]; @@ -82,6 +91,7 @@ [appDelegate release]; [jsonString release]; [intelligenceControl release]; + [pull release]; [super dealloc]; } @@ -188,6 +198,13 @@ [error localizedDescription]); 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 { @@ -415,4 +432,19 @@ 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 diff --git a/media/iphone/Classes/NewsBlurAppDelegate.h b/media/iphone/Classes/NewsBlurAppDelegate.h index 07f29194c..ec7e238da 100644 --- a/media/iphone/Classes/NewsBlurAppDelegate.h +++ b/media/iphone/Classes/NewsBlurAppDelegate.h @@ -83,6 +83,7 @@ - (void)markActiveFeedAllRead; - (void)calculateStoryLocations; + (int)computeStoryScore:(NSDictionary *)intelligence; ++ (void)informError:(NSError *)error; @end diff --git a/media/iphone/Classes/NewsBlurAppDelegate.m b/media/iphone/Classes/NewsBlurAppDelegate.m index bf913d5d0..77b460d9d 100644 --- a/media/iphone/Classes/NewsBlurAppDelegate.m +++ b/media/iphone/Classes/NewsBlurAppDelegate.m @@ -283,4 +283,15 @@ 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 diff --git a/media/iphone/Classes/NewsBlurViewController.m b/media/iphone/Classes/NewsBlurViewController.m index bc7b0023d..0682e75c7 100644 --- a/media/iphone/Classes/NewsBlurViewController.m +++ b/media/iphone/Classes/NewsBlurViewController.m @@ -178,6 +178,16 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 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 { @@ -329,6 +339,8 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] FeedTableCell *cell = (FeedTableCell *)[tableView dequeueReusableCellWithIdentifier:FeedCellIdentifier]; if (cell == nil) { cell = [[[FeedTableCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"FeedCellIdentifier"] autorelease]; + cell.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate]; + } 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); [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 { @@ -493,14 +508,17 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] } } + BOOL isVisible = !![self.stillVisibleFeeds objectForKey:feedIdStr]; BOOL notDeletedYetVisible = !deleted && previousLevel != newLevel && (maxScore < newLevel) && - [self.stillVisibleFeeds objectForKey:feedIdStr]; + isVisible; if (notDeletedYetVisible) { // NSLog(@"DELETING: %@ - %d - %d - %d - %d", [feed objectForKey:@"feed_title"], maxScore, newLevel, previousLevel, !deleted); [deleteIndexPaths addObject:indexPath]; [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]) { NSDictionary *feed = [self.dictFeeds objectForKey:feedIdStr]; int maxScore = [NewsBlurViewController computeMaxScoreForFeed:feed]; -// NSLog(@"Still visible: %@ - %d - %d - %d", [feed objectForKey:@"feed_title"], maxScore, newLevel, previousLevel); if (previousLevel != newLevel && maxScore < newLevel) { + NSLog(@"Still visible: %@ - %d - %d - %d", [feed objectForKey:@"feed_title"], maxScore, newLevel, previousLevel); [deleteIndexPaths addObject:[self.stillVisibleFeeds objectForKey:feedIdStr]]; [self.stillVisibleFeeds removeObjectForKey:feedIdStr]; } @@ -634,4 +652,15 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] [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 \ No newline at end of file diff --git a/media/iphone/Classes/OriginalStoryViewController.h b/media/iphone/Classes/OriginalStoryViewController.h index edef732b2..e2bb47243 100644 --- a/media/iphone/Classes/OriginalStoryViewController.h +++ b/media/iphone/Classes/OriginalStoryViewController.h @@ -52,6 +52,5 @@ static const CGFloat kButtonWidth = 48.0f; - (void)updateTitle:(UIWebView*)aWebView; - (void)updateAddress:(NSURLRequest*)request; - (void)updateButtons; -- (void)informError:(NSError*)error; @end diff --git a/media/iphone/Classes/OriginalStoryViewController.m b/media/iphone/Classes/OriginalStoryViewController.m index 4bc7422b4..acfeb252a 100644 --- a/media/iphone/Classes/OriginalStoryViewController.m +++ b/media/iphone/Classes/OriginalStoryViewController.m @@ -180,7 +180,7 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] // User clicking on another link before the page loads is OK. if ([error code] != NSURLErrorCancelled) { - [self informError:error]; + [NewsBlurAppDelegate informError:error]; } } - (void)updateTitle:(UIWebView*)aWebView @@ -200,17 +200,6 @@ blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] self.back.enabled = self.webView.canGoBack; // 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 { // 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; 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 { - 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) { [options addButtonWithTitle:title]; diff --git a/media/iphone/Classes/StoryDetailViewController.m b/media/iphone/Classes/StoryDetailViewController.m index 861384081..3e417eb10 100644 --- a/media/iphone/Classes/StoryDetailViewController.m +++ b/media/iphone/Classes/StoryDetailViewController.m @@ -308,7 +308,7 @@ - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; - + self.activeStoryId = nil; // Release any cached data, images, etc that aren't in use. } diff --git a/media/iphone/NewsBlur-Info.plist b/media/iphone/NewsBlur-Info.plist index a1e2446c5..5dd49fbf3 100644 --- a/media/iphone/NewsBlur-Info.plist +++ b/media/iphone/NewsBlur-Info.plist @@ -6,6 +6,8 @@ English CFBundleDisplayName ${PRODUCT_NAME} + CFBundleDocumentTypes + CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIconFile @@ -24,20 +26,26 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1 + CFBundleSignature ???? + CFBundleURLTypes + CFBundleVersion - 1.0 + 1.0b3 LSRequiresIPhoneOS NSMainNibFile MainWindow + UIPrerenderedIcon + UISupportedInterfaceOrientations UIInterfaceOrientationPortrait - UIPrerenderedIcon - + UTExportedTypeDeclarations + + UTImportedTypeDeclarations + diff --git a/media/iphone/NewsBlur.xcodeproj/FeedTableCell.h b/media/iphone/NewsBlur.xcodeproj/FeedTableCell.h index 84fbf4549..4cfca8266 100644 --- a/media/iphone/NewsBlur.xcodeproj/FeedTableCell.h +++ b/media/iphone/NewsBlur.xcodeproj/FeedTableCell.h @@ -7,10 +7,14 @@ // #import +#import "NewsBlurAppDelegate.h" #import "ABTableViewCell.h" +@class NewsBlurAppDelegate; @interface FeedTableCell : ABTableViewCell { + NewsBlurAppDelegate *appDelegate; + NSString *feedTitle; UIImage *feedFavicon; int _positiveCount; @@ -21,6 +25,7 @@ NSString *_negativeCountStr; } +@property (nonatomic, retain) NewsBlurAppDelegate *appDelegate; @property (nonatomic, retain) NSString *feedTitle; @property (nonatomic, retain) UIImage *feedFavicon; @property (assign, nonatomic) int positiveCount; diff --git a/media/iphone/NewsBlur.xcodeproj/FeedTableCell.m b/media/iphone/NewsBlur.xcodeproj/FeedTableCell.m index d7876f66e..80f4c0b16 100644 --- a/media/iphone/NewsBlur.xcodeproj/FeedTableCell.m +++ b/media/iphone/NewsBlur.xcodeproj/FeedTableCell.m @@ -6,6 +6,7 @@ // Copyright 2011 NewsBlur. All rights reserved. // +#import "NewsBlurAppDelegate.h" #import "FeedTableCell.h" #import "ABTableViewCell.h" #import "UIView+TKCategory.h" @@ -26,6 +27,7 @@ static CGFloat *psColors = nil; @implementation FeedTableCell +@synthesize appDelegate; @synthesize feedTitle; @synthesize feedFavicon; @synthesize positiveCount = _positiveCount; @@ -138,7 +140,7 @@ static CGFloat *psColors = nil; drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos) withFont:indicatorFont]; } - if(_neutralCount > 0){ + if(_neutralCount > 0 && appDelegate.selectedIntelligence <= 0){ [neutralBackgroundColor set]; CGRect rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntOffset, 10, ntWidth, 18); [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) withFont:indicatorFont]; } - if(_negativeCount > 0){ + if(_negativeCount > 0 && appDelegate.selectedIntelligence <= -1){ [negativeBackgroundColor set]; CGRect rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntWidth - ntPadding - ngOffset, 10, ngWidth, 18); [UIView drawRoundRectangleInRect:rr withRadius:5]; diff --git a/media/iphone/NewsBlur.xcodeproj/project.pbxproj b/media/iphone/NewsBlur.xcodeproj/project.pbxproj index 4b11ec42f..6edfab981 100755 --- a/media/iphone/NewsBlur.xcodeproj/project.pbxproj +++ b/media/iphone/NewsBlur.xcodeproj/project.pbxproj @@ -579,8 +579,8 @@ "-all_load", "-ObjC", ); - PROVISIONING_PROFILE = "7B7D4B1E-A24A-4E63-9C14-F251D6C5B095"; - "PROVISIONING_PROFILE[sdk=iphoneos*]" = "7B7D4B1E-A24A-4E63-9C14-F251D6C5B095"; + PROVISIONING_PROFILE = "9390CCB7-7064-41BA-A292-53CE11BB3BEB"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "9390CCB7-7064-41BA-A292-53CE11BB3BEB"; RUN_CLANG_STATIC_ANALYZER = YES; SDKROOT = iphoneos; }; @@ -597,8 +597,8 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 4.0; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; - PROVISIONING_PROFILE = "7B7D4B1E-A24A-4E63-9C14-F251D6C5B095"; - "PROVISIONING_PROFILE[sdk=iphoneos*]" = "7B7D4B1E-A24A-4E63-9C14-F251D6C5B095"; + PROVISIONING_PROFILE = "9390CCB7-7064-41BA-A292-53CE11BB3BEB"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "9390CCB7-7064-41BA-A292-53CE11BB3BEB"; SDKROOT = iphoneos; }; name = Release; diff --git a/utils/feed_functions.py b/utils/feed_functions.py index 45fdd0b64..f5557243e 100644 --- a/utils/feed_functions.py +++ b/utils/feed_functions.py @@ -130,7 +130,13 @@ def relative_timeuntil(value): now = datetime.datetime.utcnow() 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): if not date or date < datetime.datetime(2010, 1, 1): return "Soon"