NewsBlur/media/iphone/Classes/FeedDetailViewController.m

444 lines
18 KiB
Mathematica
Raw Normal View History

//
// FeedDetailViewController.m
// NewsBlur
//
// Created by Samuel Clay on 6/20/10.
// Copyright 2010 NewsBlur. All rights reserved.
//
#import "FeedDetailViewController.h"
#import "NewsBlurAppDelegate.h"
#import "FeedDetailTableCell.h"
#import "ASIFormDataRequest.h"
#import "JSON.h"
#define kTableViewRowHeight 60;
@implementation FeedDetailViewController
@synthesize storyTitlesTable, feedViewToolbar, feedScoreSlider, feedMarkReadButton;
@synthesize stories;
@synthesize appDelegate;
@synthesize jsonString;
@synthesize feedPage;
@synthesize pageFetching;
@synthesize pageFinished;
@synthesize intelligenceControl;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
}
return self;
}
- (void)viewWillAppear:(BOOL)animated {
// NSLog(@"Loaded Feed view: %@", appDelegate.activeFeed);
self.pageFinished = NO;
self.title = [appDelegate.activeFeed objectForKey:@"feed_title"];
NSMutableArray *indexPaths = [NSMutableArray array];
for (id i in appDelegate.recentlyReadStories) {
NSLog(@"Read story %d: %@", [i intValue], appDelegate.recentlyReadStories);
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[i intValue]
inSection:0];
[indexPaths addObject:indexPath];
}
[appDelegate.recentlyReadStories removeAllObjects];
if ([indexPaths count] > 0) {
NSLog(@"Having read %d stories: %@", [appDelegate.recentlyReadStories count], appDelegate.recentlyReadStories);
[self.storyTitlesTable beginUpdates];
[self.storyTitlesTable reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
[self.storyTitlesTable endUpdates];
}
[self.intelligenceControl setImage:[UIImage imageNamed:@"bullet_red.png"] forSegmentAtIndex:0];
[self.intelligenceControl setImage:[UIImage imageNamed:@"bullet_yellow.png"] forSegmentAtIndex:1];
[self.intelligenceControl setImage:[UIImage imageNamed:@"bullet_green.png"] forSegmentAtIndex:2];
[self.intelligenceControl addTarget:self
action:@selector(selectIntelligence)
forControlEvents:UIControlEventValueChanged];
[self.intelligenceControl setSelectedSegmentIndex:[appDelegate selectedIntelligence]+1];
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
//[appDelegate showNavigationBar:animated];
[[storyTitlesTable cellForRowAtIndexPath:[storyTitlesTable indexPathForSelectedRow]] setSelected:NO]; // TODO: DESELECT CELL
[super viewDidAppear:animated];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
//NSLog(@"Unloading detail view: %@", self);
self.appDelegate = nil;
self.jsonString = nil;
self.intelligenceControl = nil;
}
- (void)dealloc {
[appDelegate release];
[stories release];
[jsonString release];
[intelligenceControl release];
[super dealloc];
}
#pragma mark -
#pragma mark Initialization
- (void)fetchFeedDetail:(int)page {
// NSLog(@"fetching page %d. %d fetching, %d finished.", self.feedPage, self.pageFetching, self.pageFinished);
if ([appDelegate.activeFeed objectForKey:@"id"] != nil && !self.pageFetching && !self.pageFinished) {
self.feedPage = page;
self.pageFetching = YES;
int storyCount = appDelegate.storyCount;
if (storyCount == 0) {
[self.storyTitlesTable reloadData];
[storyTitlesTable scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
}
NSString *theFeedDetailURL = [[NSString alloc]
initWithFormat:@"http://nb.local.host:8000/reader/feed/%@?page=%d",
[appDelegate.activeFeed objectForKey:@"id"],
self.feedPage];
//NSLog(@"Url: %@", theFeedDetailURL);
NSURL *urlFeedDetail = [NSURL URLWithString:theFeedDetailURL];
[theFeedDetailURL release];
jsonString = [[NSMutableData data] retain];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: urlFeedDetail];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection release];
[request release];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[jsonString setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[jsonString appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *jsonS = [[NSString alloc]
initWithData:jsonString
encoding:NSUTF8StringEncoding];
NSDictionary *results = [[NSDictionary alloc]
initWithDictionary:[jsonS JSONValue]];
NSArray *newStories = [results objectForKey:@"stories"];
NSInteger newStoriesCount = [newStories count];
NSInteger existingStoriesCount = appDelegate.storyCount;
int storyCount = 0;
if (self.feedPage == 1) {
[appDelegate setStories:newStories];
} else if (newStoriesCount > 0) {
for (int i=0; i < [appDelegate storyCount]; i++) {
NSDictionary *story = [appDelegate.activeFeedStories objectAtIndex:i];
int score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]];
int intelligenceLevel = [appDelegate selectedIntelligence];
if (score >= intelligenceLevel) {
storyCount += 1;
}
}
[appDelegate addStories:newStories];
}
// NSLog(@"Stories: %d stories, page %d. %d new stories.", existingStoriesCount, self.feedPage, newStoriesCount);
if (existingStoriesCount > 0 && newStoriesCount > 0) {
NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
int visibleRows = 0;
int intelligenceLevel = [appDelegate selectedIntelligence];
for (int i=0; i < newStoriesCount; i++) {
NSDictionary *story = [appDelegate.activeFeedStories objectAtIndex:existingStoriesCount+i];
int score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]];
if (score >= intelligenceLevel) {
visibleRows += 1;
int row = storyCount+visibleRows;
[indexPaths addObject:[NSIndexPath indexPathForRow:row inSection:0]];
}
}
[self.storyTitlesTable insertRowsAtIndexPaths:indexPaths
withRowAnimation:UITableViewRowAnimationNone];
[indexPaths release];
} else if (newStoriesCount > 0) {
[self.storyTitlesTable reloadData];
} else if (newStoriesCount == 0) {
self.pageFinished = YES;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:existingStoriesCount
inSection:0];
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
[self.storyTitlesTable beginUpdates];
[self.storyTitlesTable reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
[self.storyTitlesTable endUpdates];
}
self.pageFetching = NO;
[results release];
[jsonS release];
[jsonString release];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[jsonString release];
// inform the user
2010-11-15 19:40:17 -05:00
NSLog(@"Connection failed! Error - %@",
[error localizedDescription]);
self.pageFetching = NO;
}
- (UITableViewCell *)makeLoadingCell {
UITableViewCell *cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:@"NoReuse"] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
if (self.pageFinished) {
UIView * blue = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 321, 17)];
[cell.contentView addSubview:blue];
blue.backgroundColor = [UIColor whiteColor];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5f];
blue.backgroundColor = [UIColor colorWithRed:.7f green:0.7f blue:0.7f alpha:1.0f];
[UIView commitAnimations];
[blue release];
} else {
cell.textLabel.text = @"Loading...";
UIActivityIndicatorView *spinner = [[[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray] autorelease];
// Spacer is a 1x1 transparent png
UIImage *spacer = [UIImage imageNamed:@"spacer"];
UIGraphicsBeginImageContext(spinner.frame.size);
[spacer drawInRect:CGRectMake(0,0,spinner.frame.size.width,spinner.frame.size.height)];
UIImage* resizedSpacer = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
cell.imageView.image = resizedSpacer;
[cell.imageView addSubview:spinner];
[spinner startAnimating];
}
return cell;
}
#pragma mark -
#pragma mark Table View - Feed List
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// The + 1 is for the finished/loading bar.
NSLog(@"Current intelligence: %d", [appDelegate selectedIntelligence]);
int storyCount = 0;
for (int i=0; i < [appDelegate storyCount]; i++) {
NSDictionary *story = [appDelegate.activeFeedStories objectAtIndex:i];
int score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]];
int intelligenceLevel = [appDelegate selectedIntelligence];
if (score >= intelligenceLevel) {
storyCount += 1;
} else {
NSLog(@"Skipping %@", [story objectForKey:@"story_title"]);
}
}
return storyCount + 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *FeedDetailCellIdentifier = @"FeedDetailCellIdentifier";
FeedDetailTableCell *cell = (FeedDetailTableCell *)[tableView dequeueReusableCellWithIdentifier:FeedDetailCellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"FeedDetailTableCell"
owner:self
options:nil];
for (id oneObject in nib) {
if ([oneObject isKindOfClass:[FeedDetailTableCell class]]) {
cell = (FeedDetailTableCell *)oneObject;
}
}
}
if (indexPath.row >= appDelegate.storyCount) {
return [self makeLoadingCell];
}
NSDictionary *story = [self getStoryAtRow:indexPath.row];
if ([[story objectForKey:@"story_authors"] class] != [NSNull class]) {
cell.storyAuthor.text = [[story objectForKey:@"story_authors"] uppercaseString];
} else {
cell.storyAuthor.text = @"";
}
cell.storyTitle.text = [story objectForKey:@"story_title"];
cell.storyDate.text = [story objectForKey:@"short_parsed_date"];
int score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]];
if (score > 0) {
cell.storyUnreadIndicator.image = [UIImage imageNamed:@"bullet_green.png"];
} else if (score == 0) {
cell.storyUnreadIndicator.image = [UIImage imageNamed:@"bullet_yellow.png"];
} else if (score < 0) {
cell.storyUnreadIndicator.image = [UIImage imageNamed:@"bullet_red.png"];
}
if ([[story objectForKey:@"read_status"] intValue] != 1) {
// Unread story
cell.storyTitle.textColor = [UIColor colorWithRed:0.1f green:0.1f blue:0.1f alpha:1.0];
cell.storyTitle.font = [UIFont fontWithName:@"Helvetica-Bold" size:12];
cell.storyAuthor.textColor = [UIColor colorWithRed:0.58f green:0.58f blue:0.58f alpha:1.0];
cell.storyAuthor.font = [UIFont fontWithName:@"Helvetica-Bold" size:10];
cell.storyDate.textColor = [UIColor colorWithRed:0.14f green:0.18f blue:0.42f alpha:1.0];
cell.storyDate.font = [UIFont fontWithName:@"Helvetica-Bold" size:10];
cell.storyUnreadIndicator.alpha = 1;
} else {
// Read story
cell.storyTitle.textColor = [UIColor colorWithRed:0.15f green:0.25f blue:0.25f alpha:0.9];
cell.storyTitle.font = [UIFont fontWithName:@"Helvetica" size:12];
cell.storyAuthor.textColor = [UIColor colorWithRed:0.58f green:0.58f blue:0.58f alpha:0.5];
cell.storyAuthor.font = [UIFont fontWithName:@"Helvetica-Bold" size:10];
cell.storyDate.textColor = [UIColor colorWithRed:0.14f green:0.18f blue:0.42f alpha:0.5];
cell.storyDate.font = [UIFont fontWithName:@"Helvetica" size:10];
cell.storyUnreadIndicator.alpha = 0.15f;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row < appDelegate.storyCount) {
[appDelegate setActiveStory:[[appDelegate activeFeedStories] objectAtIndex:indexPath.row]];
[appDelegate loadStoryDetailView];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// NSLog(@"Height for row: %d of %d stories. (Finished: %d)", indexPath.row, appDelegate.storyCount, self.pageFinished);
if (indexPath.row >= appDelegate.storyCount) {
if (self.pageFinished) return 16;
else return kTableViewRowHeight;
} else {
NSDictionary *story = [appDelegate.activeFeedStories objectAtIndex:indexPath.row];
int score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]];
int intelligenceLevel = [appDelegate selectedIntelligence];
if (score >= intelligenceLevel) {
// NSLog(@"Score: %d on %d %@ - %@", score, intelligenceLevel-1, score >= (intelligenceLevel-1) ? @"SHOW" : @"HIDE",
// [story objectForKey:@"story_title"]);
return kTableViewRowHeight;
} else {
return 0.0f;
}
}
}
- (void)scrollViewDidScroll: (UIScrollView *)scroll {
NSInteger currentOffset = scroll.contentOffset.y;
NSInteger maximumOffset = scroll.contentSize.height - scroll.frame.size.height;
if (maximumOffset - currentOffset <= 60.0) {
[self fetchFeedDetail:self.feedPage+1];
}
}
- (IBAction)markAllRead {
NSString *urlString = @"http://nb.local.host:8000/reader/mark_feed_as_read";
NSURL *url = [NSURL URLWithString:urlString];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:[appDelegate.activeFeed objectForKey:@"id"] forKey:@"feed_id"];
[request setDelegate:nil];
[request startAsynchronous];
[appDelegate markActiveFeedAllRead];
[appDelegate.navigationController
popToViewController:[appDelegate.navigationController.viewControllers
objectAtIndex:0]
animated:YES];
}
- (IBAction)selectIntelligence {
NSInteger newLevel = [self.intelligenceControl selectedSegmentIndex] - 1;
NSInteger previousLevel = [appDelegate selectedIntelligence];
NSMutableArray *insertIndexPaths = [NSMutableArray array];
NSMutableArray *deleteIndexPaths = [NSMutableArray array];
[appDelegate setSelectedIntelligence:newLevel];
for (int i=0; i < [appDelegate storyCount]; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
NSDictionary *story = [appDelegate.activeFeedStories objectAtIndex:i];
int score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]];
if (previousLevel == -1) {
if (newLevel == 0 && score == -1) {
[deleteIndexPaths addObject:indexPath];
} else if (newLevel == 1 && score < 1) {
[deleteIndexPaths addObject:indexPath];
}
} else if (previousLevel == 0) {
if (newLevel == -1 && score == -1) {
[insertIndexPaths addObject:indexPath];
} else if (newLevel == 1 && score == 0) {
[deleteIndexPaths addObject:indexPath];
}
} else if (previousLevel == 1) {
if (newLevel == 0 && score == 0) {
[insertIndexPaths addObject:indexPath];
} else if (newLevel == -1 && score < 1) {
[insertIndexPaths addObject:indexPath];
}
}
}
NSLog(@"Select: %d deleted, %d inserted. Pre: %d, post: %d", [deleteIndexPaths count], [insertIndexPaths count], previousLevel, newLevel);
[self.storyTitlesTable beginUpdates];
[self.storyTitlesTable deleteRowsAtIndexPaths:deleteIndexPaths
withRowAnimation:UITableViewRowAnimationNone];
[self.storyTitlesTable insertRowsAtIndexPaths:insertIndexPaths
withRowAnimation:UITableViewRowAnimationNone];
[self.storyTitlesTable endUpdates];
}
- (NSDictionary *)getStoryAtRow:(NSInteger)indexPathRow {
int row = 0;
int intelligenceLevel = [appDelegate selectedIntelligence];
for (int i=0; i < [appDelegate storyCount]; i++) {
NSDictionary *story = [appDelegate.activeFeedStories objectAtIndex:i];
int score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]];
if (score >= intelligenceLevel) {
if (row == indexPathRow) {
row = i;
break;
} else {
row++;
}
}
}
NSLog(@"i: %d, %d, %d", row, indexPathRow, intelligenceLevel);
return [appDelegate.activeFeedStories objectAtIndex:row];
}
@end