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 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))

View file

@ -5,3 +5,5 @@ ServerAliveCountMax=6
StrictHostKeyChecking=no
Compression=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.
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%}"

View file

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

View file

@ -7,6 +7,7 @@
//
#import <UIKit/UIKit.h>
#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;

View file

@ -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

View file

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

View file

@ -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

View file

@ -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

View file

@ -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

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.
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];

View file

@ -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.
}

View file

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

View file

@ -7,10 +7,14 @@
//
#import <UIKit/UIKit.h>
#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;

View file

@ -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];

View file

@ -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;

View file

@ -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"