Reading cached images and using in stories. A single iamge is nowhere near enough. Need a refactor to account for all images.

This commit is contained in:
Samuel Clay 2013-06-23 22:19:08 -07:00
parent 7625251c22
commit 8bcfc5d565
7 changed files with 127 additions and 28 deletions

View file

@ -425,7 +425,8 @@
[self showLoadingNotifier];
} else if (!self.isOffline) {
[self showLoadingNotifier];
}
}
[appDelegate prepareActiveCachedImages:db];
}];
self.pageFinished = YES;
@ -644,12 +645,11 @@
[appDelegate.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
for (NSDictionary *story in confirmedNewStories) {
[db executeUpdate:@"INSERT into stories"
"(story_feed_id, story_hash, story_timestamp, image_url, story_json) VALUES "
"(?, ?, ?, ?, ?)",
"(story_feed_id, story_hash, story_timestamp, story_json) VALUES "
"(?, ?, ?, ?)",
[story objectForKey:@"story_feed_id"],
[story objectForKey:@"story_hash"],
[story objectForKey:@"story_timestamp"],
[story objectForKey:@"image_url"],
[story JSONRepresentation]
];
}
@ -862,7 +862,12 @@
cell.storyScore = score;
if (self.isOffline) {
BOOL read = ![[self.unreadStoryHashes objectForKey:[story objectForKey:@"story_hash"]] boolValue];
BOOL read;
if ([appDelegate.activeReadFilter isEqualToString:@"all"]) {
read = ![[self.unreadStoryHashes objectForKey:[story objectForKey:@"story_hash"]] boolValue];
} else {
read = NO;
}
cell.isRead = read || ([[story objectForKey:@"read_status"] intValue] == 1);
} else {
cell.isRead = [[story objectForKey:@"read_status"] intValue] == 1;

View file

@ -141,6 +141,7 @@
NSDictionary *categoryFeeds;
UIImageView *splashView;
ASINetworkQueue *operationQueue;
NSMutableDictionary *activeCachedImages;
}
@property (nonatomic) IBOutlet UIWindow *window;
@ -236,6 +237,7 @@
@property (nonatomic) NSDictionary *categoryFeeds;
@property (readwrite) FMDatabaseQueue *database;
@property (readwrite) ASINetworkQueue *operationQueue;
@property (nonatomic) NSMutableDictionary *activeCachedImages;
+ (NewsBlurAppDelegate*) sharedAppDelegate;
- (void)startupAnimationDone:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context;
@ -334,7 +336,6 @@
- (int)databaseSchemaVersion:(FMDatabase *)db;
- (void)createDatabaseConnection;
- (void)setupDatabase:(FMDatabase *)db;
- (NSURL *)applicationDocumentsDirectory;
- (void)fetchUnreadHashes;
- (void)storeUnreadHashes:(ASIHTTPRequest *)request;
- (void)fetchAllUnreadStories;
@ -342,6 +343,8 @@
- (void)flushQueuedReadStories:(BOOL)forceCheck withCallback:(void(^)())callback;
- (void)syncQueuedReadStories:(FMDatabase *)db withStories:(NSDictionary *)hashes withCallback:(void(^)())callback;
- (void)cachedImageQueueFinished:(ASINetworkQueue *)queue;
- (void)flushOldCachedImages;
- (void)prepareActiveCachedImages:(FMDatabase *)db;
@end

View file

@ -47,7 +47,7 @@
@implementation NewsBlurAppDelegate
#define CURRENT_DB_VERSION 17
#define CURRENT_DB_VERSION 18
#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
@synthesize window;
@ -146,6 +146,7 @@
@synthesize categories;
@synthesize categoryFeeds;
@synthesize operationQueue;
@synthesize activeCachedImages;
+ (NewsBlurAppDelegate*) sharedAppDelegate {
return (NewsBlurAppDelegate*) [UIApplication sharedApplication].delegate;
@ -2139,8 +2140,6 @@
" story_feed_id number,"
" story_hash varchar(24),"
" story_timestamp number,"
" image_url varchar(1024),"
" image_cached boolean,"
" story_json text,"
" UNIQUE(story_hash) ON CONFLICT REPLACE"
")"];
@ -2173,19 +2172,30 @@
")"];
[db executeUpdate:createImagesTable];
NSLog(@"Create db %d: %@", [db lastErrorCode], [db lastErrorMessage]);
}
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *storyImagesDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"story_images"];
NSString *faviconsDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"favicons"];
NSString *avatarsDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"avatars"];
// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
if (![[NSFileManager defaultManager] fileExistsAtPath:storyImagesDirectory]) {
[[NSFileManager defaultManager] createDirectoryAtPath:storyImagesDirectory withIntermediateDirectories:NO attributes:nil error:&error];
}
if (![[NSFileManager defaultManager] fileExistsAtPath:faviconsDirectory]) {
[[NSFileManager defaultManager] createDirectoryAtPath:faviconsDirectory withIntermediateDirectories:NO attributes:nil error:&error];
}
if (![[NSFileManager defaultManager] fileExistsAtPath:avatarsDirectory]) {
[[NSFileManager defaultManager] createDirectoryAtPath:avatarsDirectory withIntermediateDirectories:NO attributes:nil error:&error];
}
NSLog(@"Create db %d: %@", [db lastErrorCode], [db lastErrorMessage]);
}
- (void)flushQueuedReadStories:(BOOL)forceCheck withCallback:(void(^)())callback {
if (hasQueuedReadStories || forceCheck) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
[self flushOldCachedImages];
[self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
NSMutableDictionary *hashes = [NSMutableDictionary dictionary];
FMResultSet *stories = [db executeQuery:@"SELECT * FROM queued_read_hashes"];
@ -2387,12 +2397,11 @@
[_self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
for (NSDictionary *story in [results objectForKey:@"stories"]) {
BOOL inserted = [db executeUpdate:@"INSERT into stories "
"(story_feed_id, story_hash, story_timestamp, image_url, story_json) VALUES "
"(?, ?, ?, ?, ?)",
"(story_feed_id, story_hash, story_timestamp, story_json) VALUES "
"(?, ?, ?, ?)",
[story objectForKey:@"story_feed_id"],
[story objectForKey:@"story_hash"],
[story objectForKey:@"story_timestamp"],
[story objectForKey:@"image_url"],
[story JSONRepresentation]
];
if ([[story objectForKey:@"image_url"] class] != [NSNull class]) {
@ -2519,7 +2528,7 @@
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [paths objectAtIndex:0];
NSString *cacheDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"story_images"];
NSString *fullPath = [cacheDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@", md5Url]];
[fileManager createFileAtPath:fullPath contents:responseData attributes:nil];
@ -2542,6 +2551,54 @@
// });
}
- (void)flushOldCachedImages {
int deleted = 0;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"story_images"];
NSDirectoryEnumerator* en = [fileManager enumeratorAtPath:cacheDirectory];
NSString* file;
while (file = [en nextObject])
{
NSError *error = nil;
NSString *filepath = [NSString stringWithFormat:[cacheDirectory stringByAppendingString:@"/%@"],file];
NSDate *creationDate = [[fileManager attributesOfItemAtPath:filepath error:nil] fileCreationDate];
NSDate *d = [[NSDate date] dateByAddingTimeInterval:-14*24*60*60];
NSDateFormatter *df = [NSDateFormatter alloc]; // = [NSDateFormatter initWithDateFormat:@"yyyy-MM-dd"];
[df setDateFormat:@"EEEE d"];
if ([creationDate compare:d] == NSOrderedAscending) {
[[NSFileManager defaultManager]
removeItemAtPath:[cacheDirectory stringByAppendingPathComponent:file]
error:&error];
deleted += 1;
}
}
NSLog(@"Deleted %d old cached images", deleted);
}
- (void)prepareActiveCachedImages:(FMDatabase *)db {
activeCachedImages = [NSMutableDictionary dictionary];
NSArray *feedIds;
if (isRiverView) {
feedIds = activeFolderFeeds;
} else if (activeFeed) {
feedIds = @[[activeFeed objectForKey:@"id"]];
}
NSString *sql = [NSString stringWithFormat:@"SELECT c.image_url, c.story_hash FROM cached_images c "
"INNER JOIN unread_hashes u ON (c.story_hash = u.story_hash) "
"WHERE c.image_cached = 1 AND c.story_feed_id in (%@)",
[feedIds componentsJoinedByString:@","]];
FMResultSet *cursor = [db executeQuery:sql];
while ([cursor next]) {
[activeCachedImages setObject:[cursor objectForColumnName:@"image_url"] forKey:[cursor objectForColumnName:@"story_hash"]];
}
}
@end
#pragma mark -

View file

@ -117,6 +117,7 @@ IASKSettingsDelegate> {
- (void)settingsViewControllerDidEnd:(IASKAppSettingsViewController*)sender;
- (void)settingDidChange:(NSNotification*)notification;
- (void)showRefreshNotifier;
- (void)showSyncingNotifier;
- (void)showSyncingNotifier:(float)progress hoursBack:(int)days;
- (void)showCachingNotifier:(float)progress hoursBack:(int)hours;

View file

@ -497,8 +497,8 @@ static const CGFloat kFolderTitleHeight = 28;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
[appDelegate.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"DELETE FROM feeds WHERE username = ?", appDelegate.activeUsername];
[db executeUpdate:@"INSERT into feeds"
[db executeUpdate:@"DELETE FROM accounts WHERE username = ?", appDelegate.activeUsername];
[db executeUpdate:@"INSERT INTO accounts"
"(username, download_date, feeds_json) VALUES "
"(?, ?, ?)",
appDelegate.activeUsername,
@ -767,7 +767,7 @@ static const CGFloat kFolderTitleHeight = 28;
if (!appDelegate.activeUsername) return;
}
FMResultSet *cursor = [db executeQuery:@"SELECT * FROM feeds WHERE username = ? LIMIT 1",
FMResultSet *cursor = [db executeQuery:@"SELECT * FROM accounts WHERE username = ? LIMIT 1",
appDelegate.activeUsername];
while ([cursor next]) {
@ -1104,6 +1104,8 @@ static const CGFloat kFolderTitleHeight = 28;
- (CGFloat)tableView:(UITableView *)tableView
heightForHeaderInSection:(NSInteger)section {
if ([appDelegate.dictFoldersArray count] == 0) return 0;
NSString *folderName = [appDelegate.dictFoldersArray objectAtIndex:section];
BOOL visibleFeeds = [[self.visibleFolders objectForKey:folderName] boolValue];
@ -1493,6 +1495,10 @@ heightForHeaderInSection:(NSInteger)section {
[request setDidFailSelector:@selector(requestFailed:)];
[request setTimeOutSeconds:30];
[request startAsynchronous];
dispatch_async(dispatch_get_main_queue(), ^{
[self showRefreshNotifier];
});
}
- (void)finishRefreshingFeedList:(ASIHTTPRequest *)request {
@ -1703,8 +1709,15 @@ heightForHeaderInSection:(NSInteger)section {
self.navigationItem.titleView = userInfoView;
}
- (void)showSyncingNotifier {
- (void)showRefreshNotifier {
[self.notifier hide];
self.notifier.style = NBSyncingStyle;
self.notifier.title = @"Counting is difficult...";
[self.notifier setProgress:0];
[self.notifier show];
}
- (void)showSyncingNotifier {
self.notifier.style = NBSyncingStyle;
self.notifier.title = @"Syncing stories...";
[self.notifier setProgress:0];
@ -1715,13 +1728,13 @@ heightForHeaderInSection:(NSInteger)section {
// [self.notifier hide];
self.notifier.style = NBSyncingProgressStyle;
if (hours < 2) {
self.notifier.title = @"Storing last hour";
self.notifier.title = @"Storing past hour";
} else if (hours < 24) {
self.notifier.title = [NSString stringWithFormat:@"Storing %d hours", hours];
self.notifier.title = [NSString stringWithFormat:@"Storing past %d hours", hours];
} else if (hours < 48) {
self.notifier.title = @"Storing yesterday";
} else {
self.notifier.title = [NSString stringWithFormat:@"Storing %d days ago", (int)round(hours / 24.f)];
self.notifier.title = [NSString stringWithFormat:@"Storing past %d days", (int)round(hours / 24.f)];
}
[self.notifier setProgress:progress];
[self.notifier setNeedsDisplay];
@ -1734,7 +1747,7 @@ heightForHeaderInSection:(NSInteger)section {
if (hours < 2) {
self.notifier.title = @"Images from last hour";
} else if (hours < 24) {
self.notifier.title = [NSString stringWithFormat:@"Images from %d hours", hours];
self.notifier.title = [NSString stringWithFormat:@"Images from %d hours ago", hours];
} else if (hours < 48) {
self.notifier.title = @"Images from yesterday";
} else {

View file

@ -127,6 +127,7 @@
NSString *footerString;
NSString *fontStyleClass = @"";
NSString *fontSizeClass = @"";
NSString *storyContent = [self.activeStory objectForKey:@"story_content"];
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
if ([userPreferences stringForKey:@"fontStyle"]){
@ -151,6 +152,15 @@
contentWidthClass = @"NB-iphone";
}
if (appDelegate.feedDetailViewController.isOffline) {
NSString *storyHash = [self.activeStory objectForKey:@"story_hash"];
NSString *imageUrl = [appDelegate.activeCachedImages objectForKey:storyHash];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *storyImagesDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"story_images"];
NSString *cachedImage = [storyImagesDirectory stringByAppendingPathComponent:[Utilities md5:imageUrl]];
storyContent = [[self.activeStory objectForKey:@"story_content"] stringByReplacingOccurrencesOfString:imageUrl withString:cachedImage];
}
NSString *riverClass = (appDelegate.isRiverView || appDelegate.isSocialView) ?
@"NB-river" : @"NB-non-river";
@ -207,7 +217,7 @@
fontSizeClass,
storyHeader,
shareBarString,
[self.activeStory objectForKey:@"story_content"],
storyContent,
sharingHtmlString,
commentString,
footerString

View file

@ -64,6 +64,11 @@ static NSMutableDictionary *imageCache;
// Image not in cache, search on disk.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [paths objectAtIndex:0];
if (isSocial) {
cacheDirectory = [cacheDirectory stringByAppendingPathComponent:@"avatars"];
} else {
cacheDirectory = [cacheDirectory stringByAppendingPathComponent:@"favicons"];
}
NSString *path = [cacheDirectory stringByAppendingPathComponent:filename];
image = [UIImage imageWithContentsOfFile:path];
@ -112,6 +117,11 @@ static NSMutableDictionary *imageCache;
for (NSString *filename in [imageCache allKeys]) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [paths objectAtIndex:0];
if ([filename hasPrefix:@"social"]) {
cacheDirectory = [cacheDirectory stringByAppendingPathComponent:@"avatars"];
} else {
cacheDirectory = [cacheDirectory stringByAppendingPathComponent:@"favicons"];
}
NSString *path = [cacheDirectory stringByAppendingPathComponent:filename];
// Save image to disk