2013-07-15 18:25:09 -07:00
//
// OfflineFetchStories . m
// NewsBlur
//
// Created by Samuel Clay on 7 / 15 / 13.
// Copyright ( c ) 2013 NewsBlur . All rights reserved .
//
# import "OfflineFetchStories.h"
# import "NewsBlurAppDelegate.h"
# import "NewsBlurViewController.h"
# import "FMDatabase.h"
# import "FMDatabaseAdditions.h"
# import "AFJSONRequestOperation.h"
# import "JSON.h"
@ implementation OfflineFetchStories
@ synthesize appDelegate ;
- ( void ) main {
2013-07-16 18:06:36 -07:00
appDelegate = [ NewsBlurAppDelegate sharedAppDelegate ] ;
2013-07-16 16:38:51 -07:00
while ( YES ) {
BOOL fetched = [ self fetchStories ] ;
if ( ! fetched ) break ;
}
2013-07-15 18:25:09 -07:00
}
2013-07-16 16:38:51 -07:00
- ( BOOL ) fetchStories {
if ( self . isCancelled ) {
NSLog ( @ "FetchStories is canceled." ) ;
return NO ;
}
2013-07-16 18:06:36 -07:00
2013-07-15 18:25:09 -07:00
2013-09-04 15:56:37 -07:00
BOOL offlineAllowed = [ [ [ NSUserDefaults standardUserDefaults ]
objectForKey : @ "offline_allowed" ] boolValue ] ;
if ( ! offlineAllowed ||
! [ appDelegate isReachabileForOffline ] ) {
2013-07-15 18:25:09 -07:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2013-07-16 18:06:36 -07:00
[ appDelegate . feedsViewController showDoneNotifier ] ;
2013-07-15 18:25:09 -07:00
[ appDelegate . feedsViewController hideNotifier ] ;
} ) ;
2013-07-16 16:38:51 -07:00
return NO ;
2013-07-16 18:06:36 -07:00
}
NSArray * hashes = [ self unfetchedStoryHashes ] ;
if ( [ hashes count ] = = 0 ) {
2013-07-15 18:25:09 -07:00
NSLog ( @ "Finished downloading unread stories. %d total" , appDelegate . totalUnfetchedStoryCount ) ;
2013-07-19 12:20:51 -07:00
[ UIApplication sharedApplication ] . networkActivityIndicatorVisible = NO ;
2013-07-15 18:25:09 -07:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
if ( ! [ [ [ NSUserDefaults standardUserDefaults ]
objectForKey : @ "offline_image_download" ] boolValue ] ) {
[ appDelegate . feedsViewController showDoneNotifier ] ;
[ appDelegate . feedsViewController hideNotifier ] ;
} else {
[ appDelegate . feedsViewController showCachingNotifier : 0 hoursBack : 1 ] ;
[ appDelegate startOfflineFetchImages ] ;
}
} ) ;
2013-07-16 16:38:51 -07:00
return NO ;
2013-09-04 15:31:12 -07:00
} else {
NSLog ( @ "Fetching Stories: %@" , [ hashes objectAtIndex : 0 ] ) ;
2013-07-15 18:25:09 -07:00
}
2013-09-04 15:31:12 -07:00
__block NSCondition * lock = [ NSCondition new ] ;
[ lock lock ] ;
2013-07-15 18:25:09 -07:00
NSURL * url = [ NSURL URLWithString : [ NSString stringWithFormat : @ "%@/reader/river_stories?page=0&h=%@" ,
NEWSBLUR_URL , [ hashes componentsJoinedByString : @ "&h=" ] ] ] ;
AFJSONRequestOperation * request = [ AFJSONRequestOperation
JSONRequestOperationWithRequest : [ NSURLRequest requestWithURL : url ]
success : ^ ( NSURLRequest * request , NSHTTPURLResponse * response , id JSON ) {
2013-07-17 18:14:04 -07:00
[ self storeAllUnreadStories : JSON withHashes : hashes ] ;
2013-09-04 15:31:12 -07:00
NSLog ( @ "Done Storing Stories: %@" , [ hashes objectAtIndex : 0 ] ) ;
[ lock signal ] ;
2013-07-15 18:25:09 -07:00
} failure : ^ ( NSURLRequest * request , NSHTTPURLResponse * response , NSError * error , id JSON ) {
NSLog ( @ "Failed fetch all unreads." ) ;
2013-09-04 15:31:12 -07:00
[ lock signal ] ;
2013-07-15 18:25:09 -07:00
} ] ;
request . successCallbackQueue = dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _DEFAULT ,
( unsigned long ) NULL ) ;
2013-07-19 12:20:51 -07:00
[ UIApplication sharedApplication ] . networkActivityIndicatorVisible = YES ;
2013-07-15 18:25:09 -07:00
[ request start ] ;
[ request waitUntilFinished ] ;
2013-07-16 16:38:51 -07:00
2013-09-04 15:31:12 -07:00
[ lock waitUntilDate : [ NSDate dateWithTimeIntervalSinceNow : 30 ] ] ;
[ lock unlock ] ;
NSLog ( @ "Completed Fetching Stories: %@" , [ hashes objectAtIndex : 0 ] ) ;
2013-07-16 16:38:51 -07:00
return YES ;
2013-07-15 18:25:09 -07:00
}
- ( NSArray * ) unfetchedStoryHashes {
NSMutableArray * hashes = [ NSMutableArray array ] ;
2013-08-12 11:59:07 -07:00
[ appDelegate . database inTransaction : ^ ( FMDatabase * db , BOOL * rollback ) {
2013-07-15 18:25:09 -07:00
NSString * commonQuery = @ "FROM unread_hashes u "
"LEFT OUTER JOIN stories s ON (s.story_hash = u.story_hash) "
"WHERE s.story_hash IS NULL" ;
int count = [ db intForQuery : [ NSString stringWithFormat : @ "SELECT COUNT(1) %@" , commonQuery ] ] ;
if ( appDelegate . totalUnfetchedStoryCount = = 0 ) {
appDelegate . totalUnfetchedStoryCount = count ;
appDelegate . remainingUnfetchedStoryCount = appDelegate . totalUnfetchedStoryCount ;
} else {
appDelegate . remainingUnfetchedStoryCount = count ;
}
int limit = 100 ;
NSString * order ;
if ( [ [ [ NSUserDefaults standardUserDefaults ] objectForKey : @ "default_order" ] isEqualToString : @ "oldest" ] ) {
order = @ "ASC" ;
} else {
order = @ "DESC" ;
}
FMResultSet * cursor = [ db executeQuery : [ NSString stringWithFormat : @ "SELECT u.story_hash %@ ORDER BY u.story_timestamp %@ LIMIT %d" , commonQuery , order , limit ] ] ;
while ( [ cursor next ] ) {
[ hashes addObject : [ cursor objectForColumnName : @ "story_hash" ] ] ;
}
2013-07-18 18:24:38 -07:00
[ self updateProgress ] ;
2013-07-15 18:25:09 -07:00
} ] ;
return hashes ;
}
2013-07-18 18:24:38 -07:00
- ( void ) updateProgress {
if ( self . isCancelled ) return ;
int start = ( int ) [ [ NSDate date ] timeIntervalSince1970 ] ;
int end = appDelegate . latestFetchedStoryDate ;
int seconds = start - ( end ? end : start ) ;
__block int hours = ( int ) round ( seconds / 60. f / 60. f ) ;
__block float progress = 0. f ;
if ( appDelegate . totalUnfetchedStoryCount ) {
progress = 1. f - ( ( float ) appDelegate . remainingUnfetchedStoryCount /
( float ) appDelegate . totalUnfetchedStoryCount ) ;
}
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2013-08-12 11:59:07 -07:00
NSLog ( @ "appDelegate.remainingUnfetchedStoryCount %d (%f)" , appDelegate . remainingUnfetchedStoryCount , progress ) ;
2013-07-18 18:24:38 -07:00
[ appDelegate . feedsViewController showSyncingNotifier : progress hoursBack : hours ] ;
} ) ;
}
2013-07-17 18:14:04 -07:00
- ( void ) storeAllUnreadStories : ( NSDictionary * ) results withHashes : ( NSArray * ) hashes {
NSMutableArray * storyHashes = [ hashes mutableCopy ] ;
2013-08-12 11:59:07 -07:00
NSLog ( @ "storing hashes: %@" , [ storyHashes objectAtIndex : 0 ] ) ;
2013-09-04 15:31:12 -07:00
[ appDelegate . database inDatabase : ^ ( FMDatabase * db ) {
2013-07-16 16:38:51 -07:00
BOOL anyInserted = NO ;
2013-08-12 11:59:07 -07:00
NSLog ( @ "First storing: %@" , [ [ [ results objectForKey : @ "stories" ] objectAtIndex : 0 ] objectForKey : @ "story_hash" ] ) ;
2013-07-15 18:25:09 -07:00
for ( NSDictionary * story in [ results objectForKey : @ "stories" ] ) {
2013-07-18 18:24:38 -07:00
NSString * storyTimestamp = [ story objectForKey : @ "story_timestamp" ] ;
2013-07-15 18:25:09 -07:00
BOOL inserted = [ db executeUpdate : @ "INSERT into stories "
"(story_feed_id, story_hash, story_timestamp, story_json) VALUES "
"(?, ?, ?, ?)" ,
[ story objectForKey : @ "story_feed_id" ] ,
[ story objectForKey : @ "story_hash" ] ,
2013-07-18 18:24:38 -07:00
storyTimestamp ,
2013-07-15 18:25:09 -07:00
[ story JSONRepresentation ]
] ;
if ( [ [ story objectForKey : @ "image_urls" ] class ] ! = [ NSNull class ] &&
[ [ story objectForKey : @ "image_urls" ] count ] ) {
for ( NSString * imageUrl in [ story objectForKey : @ "image_urls" ] ) {
[ db executeUpdate : @ "INSERT INTO cached_images "
"(story_feed_id, story_hash, image_url) VALUES "
"(?, ?, ?)" ,
[ story objectForKey : @ "story_feed_id" ] ,
[ story objectForKey : @ "story_hash" ] ,
imageUrl
] ;
}
}
2013-07-17 18:14:04 -07:00
if ( inserted ) {
anyInserted = YES ;
[ storyHashes removeObject : [ story objectForKey : @ "story_hash" ] ] ;
}
2013-07-18 18:24:38 -07:00
if ( [ [ [ NSUserDefaults standardUserDefaults ] objectForKey : @ "default_order" ] isEqualToString : @ "oldest" ] ) {
if ( [ storyTimestamp intValue ] > appDelegate . latestFetchedStoryDate ) {
appDelegate . latestFetchedStoryDate = [ storyTimestamp intValue ] ;
}
} else {
2013-07-18 18:44:40 -07:00
if ( ! appDelegate . latestFetchedStoryDate ||
[ storyTimestamp intValue ] < appDelegate . latestFetchedStoryDate ) {
2013-07-18 18:24:38 -07:00
appDelegate . latestFetchedStoryDate = [ storyTimestamp intValue ] ;
}
}
appDelegate . remainingUnfetchedStoryCount - - ;
if ( appDelegate . remainingUnfetchedStoryCount % 10 = = 0 ) {
[ self updateProgress ] ;
}
2013-07-15 18:25:09 -07:00
}
2013-07-16 16:38:51 -07:00
if ( anyInserted ) {
2013-07-15 18:25:09 -07:00
appDelegate . latestFetchedStoryDate = [ [ [ [ results objectForKey : @ "stories" ] lastObject ]
objectForKey : @ "story_timestamp" ] intValue ] ;
}
2013-07-17 18:14:04 -07:00
if ( [ storyHashes count ] ) {
NSLog ( @ "Failed to fetch stories: %@" , storyHashes ) ;
2013-07-18 18:24:38 -07:00
[ db executeUpdate : [ NSString stringWithFormat : @ "DELETE FROM unread_hashes WHERE story_hash IN (\" % @ \ ")" ,
[ storyHashes componentsJoinedByString : @ "\" , \ " " ] ] ] ;
2013-07-17 18:14:04 -07:00
}
2013-08-12 11:59:07 -07:00
NSLog ( @ "Done inserting stories (in transaction)" ) ;
2013-07-15 18:25:09 -07:00
} ] ;
2013-08-12 11:59:07 -07:00
NSLog ( @ "Done inserting stories" ) ;
2013-07-15 18:25:09 -07:00
}
@ end