Merge branch 'dejal'

* dejal:
  iOS: #1236 (offline text)
  iOS: #1215 (wonky behavior)
This commit is contained in:
Samuel Clay 2019-11-14 15:40:35 -05:00
commit 781f9bf5f4
18 changed files with 429 additions and 55 deletions

View file

@ -144,6 +144,10 @@ static UIFont *textFont = nil;
@synthesize cell;
- (void)drawRect:(CGRect)r {
if (!cell) {
return;
}
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor *backgroundColor;

View file

@ -210,11 +210,13 @@
leftBorder.hidden = NO;
}
[self adjustLayout];
[self adjustLayoutCompleted:NO];
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
// leftBorder.frame = CGRectMake(0, 0, 1, CGRectGetHeight(self.view.bounds));
[self adjustLayout];
if (!self.feedDetailIsVisible) {
[self adjustLayoutCompleted:YES];
}
if (self.feedDetailIsVisible) {
// Defer this in the background, to avoid misaligning the detail views
@ -239,8 +241,8 @@
}
}
- (void)adjustLayout {
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
- (void)adjustLayoutCompleted:(BOOL)completed {
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground && !completed) {
return;
}
@ -307,6 +309,8 @@
self.leftBorder.backgroundColor = UIColorFromRGB(0xC2C5BE).CGColor;
self.rightBorder.backgroundColor = UIColorFromRGB(0xC2C5BE).CGColor;
self.view.backgroundColor = UIColor.blackColor;
self.masterNavigationController.navigationBar.tintColor = [UINavigationBar appearance].tintColor;
self.masterNavigationController.navigationBar.barTintColor = [UINavigationBar appearance].barTintColor;

View file

@ -230,9 +230,12 @@ SFSafariViewControllerDelegate> {
@property (readwrite) NSInteger savedStoriesCount;
@property (readwrite) NSInteger totalUnfetchedStoryCount;
@property (readwrite) NSInteger remainingUnfetchedStoryCount;
@property (readwrite) NSInteger totalUncachedTextCount;
@property (readwrite) NSInteger remainingUncachedTextCount;
@property (readwrite) NSInteger totalUncachedImagesCount;
@property (readwrite) NSInteger remainingUncachedImagesCount;
@property (readwrite) NSInteger latestFetchedStoryDate;
@property (readwrite) NSInteger latestCachedTextDate;
@property (readwrite) NSInteger latestCachedImageDate;
@property (readwrite) NSInteger selectedIntelligence;
@property (readwrite) NSMutableDictionary * recentlyReadStories;
@ -447,6 +450,7 @@ SFSafariViewControllerDelegate> {
- (void)cancelOfflineQueue;
- (void)startOfflineQueue;
- (void)startOfflineFetchStories;
- (void)startOfflineFetchText;
- (void)startOfflineFetchImages;
- (BOOL)isReachableForOffline;
- (void)storeUserProfiles:(NSArray *)userProfiles;
@ -456,6 +460,7 @@ SFSafariViewControllerDelegate> {
- (void)flushQueuedReadStories:(BOOL)forceCheck withCallback:(void(^)(void))callback;
- (void)syncQueuedReadStories:(FMDatabase *)db withStories:(NSDictionary *)hashes withCallback:(void(^)(void))callback;
- (void)queueSavedStory:(NSDictionary *)story;
- (void)fetchTextForStory:(NSString *)storyHash inFeed:(NSString *)feedId checkCache:(BOOL)checkCache withCallback:(void(^)(NSString *))callback;
- (void)prepareActiveCachedImages:(FMDatabase *)db;
- (void)cleanImageCache;
- (void)deleteAllCachedImages;

View file

@ -50,6 +50,7 @@
#import "IASKAppSettingsViewController.h"
#import "OfflineSyncUnreads.h"
#import "OfflineFetchStories.h"
#import "OfflineFetchText.h"
#import "OfflineFetchImages.h"
#import "OfflineCleanImages.h"
#import "NBBarButtonItem.h"
@ -80,7 +81,7 @@
@implementation NewsBlurAppDelegate
#define CURRENT_DB_VERSION 36
#define CURRENT_DB_VERSION 37
#define CURRENT_STATE_VERSION 1
@ -3596,6 +3597,10 @@
[db executeUpdate:@"drop table if exists `queued_saved_hashes`"];
}
if (databaseVersion < 37) {
[db executeUpdate:@"drop table if exists `cached_text`"];
}
NSLog(@"Dropped db: %@", [db lastErrorMessage]);
sqlite3_exec(db.sqliteHandle, [[NSString stringWithFormat:@"PRAGMA user_version = %d", CURRENT_DB_VERSION] UTF8String], NULL, NULL, NULL);
}
@ -3675,6 +3680,19 @@
")"];
[db executeUpdate:createSavedTable];
NSString *createTextTable = [NSString stringWithFormat:@"create table if not exists cached_text "
"("
" story_feed_id varchar(20),"
" story_hash varchar(24),"
" story_timestamp number,"
" text_json text"
")"];
[db executeUpdate:createTextTable];
NSString *indexTextFeedId = @"CREATE INDEX IF NOT EXISTS cached_text_story_feed_id ON cached_text (story_feed_id)";
[db executeUpdate:indexTextFeedId];
NSString *indexTextStoryHash = @"CREATE INDEX IF NOT EXISTS cached_text_story_hash ON cached_text (story_hash)";
[db executeUpdate:indexTextStoryHash];
NSString *createImagesTable = [NSString stringWithFormat:@"create table if not exists cached_images "
"("
" story_feed_id varchar(20),"
@ -3747,6 +3765,12 @@
// NSLog(@"Done start offline fetch stories");
}
- (void)startOfflineFetchText {
OfflineFetchText *operationFetchText = [[OfflineFetchText alloc] init];
[offlineQueue addOperation:operationFetchText];
}
- (void)startOfflineFetchImages {
OfflineFetchImages *operationFetchImages = [[OfflineFetchImages alloc] init];
@ -4041,6 +4065,76 @@
}];
}
- (void)fetchTextForStory:(NSString *)storyHash inFeed:(NSString *)feedId checkCache:(BOOL)checkCache withCallback:(void(^)(NSString *))callback {
if (checkCache) {
[self privateGetCachedTextForStory:storyHash inFeed:feedId withCallback:^(NSString *text) {
if (text != nil) {
if (callback) {
callback(text);
}
} else {
[self privateFetchTextForStory:storyHash inFeed:feedId withCallback:callback];
}
}];
} else {
[self privateFetchTextForStory:storyHash inFeed:feedId withCallback:callback];
}
}
- (void)privateGetCachedTextForStory:(NSString *)storyHash inFeed:(NSString *)feedId withCallback:(void(^)(NSString *))callback {
[self.database inDatabase:^(FMDatabase *db) {
NSString *text = nil;
FMResultSet *cursor = [db executeQuery:@"SELECT * FROM cached_text "
"WHERE story_hash = ? AND story_feed_id = ? LIMIT 1",
storyHash, feedId];
while ([cursor next]) {
NSDictionary *textCache = [cursor resultDictionary];
NSString *json = [textCache objectForKey:@"text_json"];
if (json.length > 0) {
NSDictionary *results = [NSJSONSerialization
JSONObjectWithData:[json
dataUsingEncoding:NSUTF8StringEncoding]
options:0 error:nil];
text = results[@"text"];
if (text) {
NSLog(@"Found cached text: %@ bytes", @(text.length));
} else {
NSLog(@"Found cached failure");
}
}
}
[cursor close];
if (callback) {
callback(text);
}
}];
}
- (void)privateFetchTextForStory:(NSString *)storyHash inFeed:(NSString *)feedId withCallback:(void(^)(NSString *))callback {
NSString *urlString = [NSString stringWithFormat:@"%@/rss_feeds/original_text", self.url];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
[params setObject:storyHash forKey:@"story_id"];
[params setObject:feedId forKey:@"feed_id"];
[self POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSString *text = [responseObject objectForKey:@"original_text"];
if ([[responseObject objectForKey:@"failed"] boolValue]) {
text = nil;
}
if (callback) {
callback(text);
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (callback) {
callback(nil);
}
}];
}
- (void)prepareActiveCachedImages:(FMDatabase *)db {
activeCachedImages = [NSMutableDictionary dictionary];

View file

@ -138,8 +138,8 @@ UIGestureRecognizerDelegate, UISearchBarDelegate> {
- (void)showRefreshNotifier;
- (void)showCountingNotifier;
- (void)showSyncingNotifier;
- (void)showSyncingNotifier:(float)progress hoursBack:(NSInteger)days;
- (void)showCachingNotifier:(float)progress hoursBack:(NSInteger)hours;
- (void)showSyncingNotifier:(float)progress hoursBack:(NSInteger)hours;
- (void)showCachingNotifier:(NSString *)prefix progress:(float)progress hoursBack:(NSInteger)hours;
- (void)showOfflineNotifier;
- (void)showDoneNotifier;
- (void)hideNotifier;

View file

@ -2378,17 +2378,17 @@ heightForHeaderInSection:(NSInteger)section {
[self.notifier show];
}
- (void)showCachingNotifier:(float)progress hoursBack:(NSInteger)hours {
- (void)showCachingNotifier:(NSString *)prefix progress:(float)progress hoursBack:(NSInteger)hours {
// [self.notifier hide];
self.notifier.style = NBSyncingProgressStyle;
if (hours < 2) {
self.notifier.title = @"Images from last hour";
self.notifier.title = [NSString stringWithFormat:@"%@ from last hour", prefix];
} else if (hours < 24) {
self.notifier.title = [NSString stringWithFormat:@"Images from %ld hours ago", (long)hours];
self.notifier.title = [NSString stringWithFormat:@"%@ from %ld hours ago", prefix, (long)hours];
} else if (hours < 48) {
self.notifier.title = @"Images from yesterday";
self.notifier.title = [NSString stringWithFormat:@"%@ from yesterday", prefix];
} else {
self.notifier.title = [NSString stringWithFormat:@"Images from %d days ago", (int)round(hours / 24.f)];
self.notifier.title = [NSString stringWithFormat:@"%@ from %d days ago", prefix, (int)round(hours / 24.f)];
}
[self.notifier setProgress:progress];
[self.notifier show];

View file

@ -2489,25 +2489,20 @@ shouldStartLoadWithRequest:(NSURLRequest *)request
self.inTextView = YES;
// NSLog(@"Fetching Text: %@", [self.activeStory objectForKey:@"story_title"]);
if (self.activeStory == appDelegate.storyPageControl.currentPage.activeStory) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.appDelegate.storyPageControl showFetchingTextNotifier];
});
[self.appDelegate.storyPageControl showFetchingTextNotifier];
}
NSString *urlString = [NSString stringWithFormat:@"%@/rss_feeds/original_text",
self.appDelegate.url];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
[params setObject:[self.activeStory objectForKey:@"id"] forKey:@"story_id"];
[params setObject:[self.activeStory objectForKey:@"story_feed_id"] forKey:@"feed_id"];
NSString *storyId = [self.activeStory objectForKey:@"id"];
[appDelegate POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[self finishFetchTextView:responseObject storyId:storyId];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[self failedFetchText:error];
[appDelegate fetchTextForStory:[self.activeStory objectForKey:@"story_hash"] inFeed:[self.activeStory objectForKey:@"story_feed_id"] checkCache:YES withCallback:^(NSString *text) {
if (text != nil) {
[self finishFetchText:text storyId:storyId];
} else {
[self failedFetchText];
}
}];
}
- (void)failedFetchText:(NSError *)error {
- (void)failedFetchText {
[self.appDelegate.storyPageControl hideNotifier];
[MBProgressHUD hideHUDForView:self.webView animated:YES];
if (self.activeStory == appDelegate.storyPageControl.currentPage.activeStory) {
@ -2517,12 +2512,7 @@ shouldStartLoadWithRequest:(NSURLRequest *)request
[appDelegate.storyPageControl setTextButton:self];
}
- (void)finishFetchTextView:(NSDictionary *)results storyId:(NSString *)storyId {
if ([[results objectForKey:@"failed"] boolValue]) {
[self failedFetchText:nil];
return;
}
- (void)finishFetchText:(NSString *)text storyId:(NSString *)storyId {
if (![storyId isEqualToString:[self.activeStory objectForKey:@"id"]]) {
[self.appDelegate.storyPageControl hideNotifier];
[MBProgressHUD hideHUDForView:self.webView animated:YES];
@ -2532,7 +2522,7 @@ shouldStartLoadWithRequest:(NSURLRequest *)request
}
NSMutableDictionary *newActiveStory = [self.activeStory mutableCopy];
[newActiveStory setObject:[results objectForKey:@"original_text"] forKey:@"original_text"];
[newActiveStory setObject:text forKey:@"original_text"];
if ([[self.activeStory objectForKey:@"story_hash"] isEqualToString:[appDelegate.activeStory objectForKey:@"story_hash"]]) {
appDelegate.activeStory = newActiveStory;
}
@ -2583,7 +2573,7 @@ shouldStartLoadWithRequest:(NSURLRequest *)request
- (void)finishFetchStoryChanges:(NSDictionary *)results storyId:(NSString *)storyId {
if ([results[@"failed"] boolValue]) {
[self failedFetchText:nil];
[self failedFetchText];
return;
}

View file

@ -93,9 +93,9 @@
NSLog(@"Queue finished: %ld total (%ld remaining)", (long)appDelegate.totalUncachedImagesCount, (long)appDelegate.remainingUncachedImagesCount);
[self updateProgress];
dispatch_sync(dispatch_get_main_queue(), ^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
});
// dispatch_async(dispatch_get_main_queue(), ^{
// [appDelegate.feedsViewController hideNotifier];
// });
@ -153,7 +153,7 @@
(float)appDelegate.totalUncachedImagesCount);
}
dispatch_async(dispatch_get_main_queue(), ^{
[appDelegate.feedsViewController showCachingNotifier:progress hoursBack:hours];
[appDelegate.feedsViewController showCachingNotifier:@"Images" progress:progress hoursBack:hours];
});
}

View file

@ -53,15 +53,17 @@
// NSLog(@"Finished downloading unread stories. %d total", appDelegate.totalUnfetchedStoryCount);
dispatch_async(dispatch_get_main_queue(), ^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if (![[[NSUserDefaults standardUserDefaults]
objectForKey:@"offline_image_download"] boolValue]) {
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"offline_text_download"]) {
[appDelegate.feedsViewController showCachingNotifier:@"Text" progress:0 hoursBack:1];
[appDelegate startOfflineFetchText];
} else if ([[NSUserDefaults standardUserDefaults] boolForKey:@"offline_image_download"]) {
[appDelegate.feedsViewController showCachingNotifier:@"Images" progress:0 hoursBack:1];
[appDelegate startOfflineFetchImages];
} else {
[appDelegate.feedsViewController showDoneNotifier];
[appDelegate.feedsViewController hideNotifier];
[appDelegate finishBackground];
} else {
[appDelegate.feedsViewController showCachingNotifier:0 hoursBack:1];
[appDelegate startOfflineFetchImages];
}
});
return NO;
@ -164,30 +166,42 @@
if (!strongSelf) return;
BOOL anyInserted = NO;
for (NSDictionary *story in [results objectForKey:@"stories"]) {
id storyFeedId = [story objectForKey:@"story_feed_id"];
id storyHash = [story objectForKey:@"story_hash"];
NSString *storyTimestamp = [story objectForKey:@"story_timestamp"];
id imageUrls = [story objectForKey:@"image_urls"];
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"],
storyFeedId,
storyHash,
storyTimestamp,
[story JSONRepresentation]
];
if ([[story objectForKey:@"image_urls"] class] != [NSNull class] &&
[[story objectForKey:@"image_urls"] count]) {
for (NSString *imageUrl in [story objectForKey:@"image_urls"]) {
if ([appDelegate isFeedInTextView:storyFeedId]) {
[db executeUpdate:@"INSERT INTO cached_text "
"(story_feed_id, story_hash, story_timestamp) VALUES "
"(?, ?, ?)",
storyFeedId,
storyHash,
storyTimestamp
];
}
if ([imageUrls class] != [NSNull class] &&
[imageUrls count]) {
for (NSString *imageUrl in imageUrls) {
[db executeUpdate:@"INSERT INTO cached_images "
"(story_feed_id, story_hash, image_url) VALUES "
"(?, ?, ?)",
[story objectForKey:@"story_feed_id"],
[story objectForKey:@"story_hash"],
storyFeedId,
storyHash,
imageUrl
];
}
}
if (inserted) {
anyInserted = YES;
[storyHashes removeObject:[story objectForKey:@"story_hash"]];
[storyHashes removeObject:storyHash];
}
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"default_order"] isEqualToString:@"oldest"]) {
if ([storyTimestamp intValue] > appDelegate.latestFetchedStoryDate) {

View file

@ -0,0 +1,23 @@
//
// OfflineFetchText.h
// NewsBlur
//
// Created by David Sinclair on 2019-10-25.
// Copyright © 2019 NewsBlur. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NewsBlurAppDelegate.h"
#import "FMDatabaseQueue.h"
NS_ASSUME_NONNULL_BEGIN
@interface OfflineFetchText : NSOperation
@property (nonatomic) NewsBlurAppDelegate *appDelegate;
- (BOOL)fetchText;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,213 @@
//
// OfflineFetchText.m
// NewsBlur
//
// Created by David Sinclair on 2019-10-25.
// Copyright © 2019 NewsBlur. All rights reserved.
//
#import "OfflineFetchText.h"
#import "NewsBlurViewController.h"
#import "FMDatabase.h"
#import "FMDatabaseAdditions.h"
#import "Utilities.h"
#import "NSObject+SBJSON.h"
@implementation OfflineFetchText
- (void)main {
dispatch_sync(dispatch_get_main_queue(), ^{
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
});
while (YES) {
BOOL fetched = [self fetchText];
if (!fetched) break;
}
}
- (BOOL)fetchText {
if (self.isCancelled) {
NSLog(@"Text cancelled.");
return NO;
}
NSLog(@"Fetching text...");
NSArray *pendingTextDictionaries = [self uncachedTextDictionaries];
if (![[[NSUserDefaults standardUserDefaults]
objectForKey:@"offline_text_download"] boolValue] ||
![[[NSUserDefaults standardUserDefaults]
objectForKey:@"offline_allowed"] boolValue] ||
[pendingTextDictionaries count] == 0) {
NSLog(@"Finished caching text. %ld total", (long)self.appDelegate.totalUncachedTextCount);
dispatch_async(dispatch_get_main_queue(), ^{
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"offline_image_download"]) {
[self.appDelegate.feedsViewController showCachingNotifier:@"Images" progress:0 hoursBack:1];
[self.appDelegate startOfflineFetchImages];
} else {
[self.appDelegate.feedsViewController showDoneNotifier];
[self.appDelegate.feedsViewController hideNotifier];
[self.appDelegate finishBackground];
}
});
return NO;
}
if (![self.appDelegate isReachableForOffline]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.appDelegate.feedsViewController showDoneNotifier];
[self.appDelegate.feedsViewController hideNotifier];
});
return NO;
}
NSString *urlString = [NSString stringWithFormat:@"%@/rss_feeds/original_text", self.appDelegate.url];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_group_t group = dispatch_group_create();
for (NSDictionary *pendingTextDictionary in pendingTextDictionaries) {
id storyHash = pendingTextDictionary[@"story_hash"];
id feedId = pendingTextDictionary[@"story_feed_id"];
NSInteger storyTimestamp = [pendingTextDictionary[@"story_timestamp"] integerValue];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
[params setObject:storyHash forKey:@"story_id"];
[params setObject:feedId forKey:@"feed_id"];
dispatch_group_enter(group);
// NSLog(@" ---> Fetching offline text: %@", urlString);
[manager POST:urlString parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// NSLog(@" ---> Fetched %@: %@", storyHash, urlString);
NSString *text = [responseObject objectForKey:@"original_text"];
if ([text isKindOfClass:[NSString class]]) {
[self storeText:text forStoryHash:storyHash storyTimestamp:storyTimestamp];
} else {
[self storeFailedTextForStoryHash:storyHash];
}
dispatch_group_leave(group);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// NSLog(@" ---> Failed to fetch text %@: %@", storyHash, urlString);
[self storeFailedTextForStoryHash:storyHash];
dispatch_group_leave(group);
}];
}
dispatch_sync(dispatch_get_main_queue(), ^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"Queue finished: %ld total (%ld remaining)", (long)self.appDelegate.totalUncachedTextCount, (long)self.appDelegate.remainingUncachedTextCount);
[self updateProgress];
dispatch_async(dispatch_get_main_queue(), ^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
});
return YES;
}
- (NSArray *)uncachedTextDictionaries {
NSMutableArray *pendingTextDictionaries = [NSMutableArray array];
[self.appDelegate.database inDatabase:^(FMDatabase *db) {
NSString *commonQuery = @"FROM cached_text c "
"INNER JOIN unread_hashes u ON (c.story_hash = u.story_hash) "
"WHERE c.text_json is null ";
int count = [db intForQuery:[NSString stringWithFormat:@"SELECT COUNT(1) %@", commonQuery]];
if (self.appDelegate.totalUncachedTextCount == 0) {
self.appDelegate.totalUncachedTextCount = count;
self.appDelegate.remainingUncachedTextCount = self.appDelegate.totalUncachedTextCount;
} else {
self.appDelegate.remainingUncachedTextCount = count;
}
int limit = 120;
NSString *order;
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"default_order"] isEqualToString:@"oldest"]) {
order = @"ASC";
} else {
order = @"DESC";
}
NSString *sql = [NSString stringWithFormat:@"SELECT * %@ ORDER BY u.story_timestamp %@ LIMIT %d", commonQuery, order, limit];
FMResultSet *cursor = [db executeQuery:sql];
while ([cursor next]) {
[pendingTextDictionaries addObject:[cursor resultDictionary]];
}
[cursor close];
}];
return pendingTextDictionaries;
}
- (void)updateProgress {
if (self.isCancelled) return;
NSInteger start = (NSInteger)[[NSDate date] timeIntervalSince1970];
NSInteger end = self.appDelegate.latestCachedTextDate;
NSInteger seconds = start - (end ? end : start);
__block int hours = (int)round(seconds / 60.f / 60.f);
__block float progress = 0.f;
if (self.appDelegate.totalUncachedTextCount) {
progress = 1.f - ((float)self.appDelegate.remainingUncachedTextCount /
(float)self.appDelegate.totalUncachedTextCount);
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.appDelegate.feedsViewController showCachingNotifier:@"Text" progress:progress hoursBack:hours];
});
}
- (void)storeText:(NSString *)text forStoryHash:(NSString *)storyHash storyTimestamp:(NSInteger)storyTimestamp {
if (self.isCancelled) {
NSLog(@"Text cancelled.");
return;
}
NSDictionary *textDictionary = @{@"text" : text};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,
(unsigned long)NULL), ^{
[self storeTextDictionary:textDictionary forStoryHash:storyHash];
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"default_order"] isEqualToString:@"oldest"]) {
if (storyTimestamp > self.appDelegate.latestCachedTextDate) {
self.appDelegate.latestCachedTextDate = storyTimestamp;
}
} else {
if (!self.appDelegate.latestCachedTextDate || storyTimestamp < self.appDelegate.latestCachedTextDate) {
self.appDelegate.latestCachedTextDate = storyTimestamp;
}
}
@synchronized (self) {
self.appDelegate.remainingUncachedTextCount--;
if (self.appDelegate.remainingUncachedTextCount % 10 == 0) {
[self updateProgress];
}
}
});
}
- (void)storeFailedTextForStoryHash:(NSString *)storyHash {
NSDictionary *textDictionary = @{@"failed" : @YES};
[self storeTextDictionary:textDictionary forStoryHash:storyHash];
}
- (void)storeTextDictionary:(NSDictionary *)textDictionary forStoryHash:(NSString *)storyHash {
[self.appDelegate.database inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"UPDATE cached_text SET text_json = ? WHERE story_hash = ?",
[textDictionary JSONRepresentation],
storyHash];
}];
}
@end

View file

@ -104,6 +104,7 @@
// NSLog(@"Deleting stories over limit: %ld - %d", (long)offlineLimit, offlineLimitTimestamp);
[db executeUpdate:[NSString stringWithFormat:@"DELETE FROM unread_hashes WHERE story_timestamp %@ %d", orderComp, offlineLimitTimestamp]];
[db executeUpdate:[NSString stringWithFormat:@"DELETE FROM stories WHERE story_timestamp %@ %d", orderComp, offlineLimitTimestamp]];
[db executeUpdate:[NSString stringWithFormat:@"DELETE FROM text WHERE story_timestamp %@ %d", orderComp, offlineLimitTimestamp]];
// [db executeUpdate:[NSString stringWithFormat:@"DELETE FROM story_scrolls WHERE story_timestamp %@ %d", orderComp, offlineLimitTimestamp]]; // Don't cleanup story scrolls just yet
}
}];

View file

@ -10,6 +10,7 @@
010EDEFA1B2386B7003B79DE /* OnePasswordExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 010EDEF81B2386B7003B79DE /* OnePasswordExtension.m */; };
010EDEFC1B238722003B79DE /* 1Password.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 010EDEFB1B238722003B79DE /* 1Password.xcassets */; };
1715D02B2166B3F900227731 /* PremiumManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1715D02A2166B3F900227731 /* PremiumManager.m */; };
17362ADD23639B4E00A0FCCC /* OfflineFetchText.m in Sources */ = {isa = PBXBuildFile; fileRef = 17362ADC23639B4E00A0FCCC /* OfflineFetchText.m */; };
1740C6881C10FD75005EA453 /* theme_color_dark.png in Resources */ = {isa = PBXBuildFile; fileRef = 1740C6841C10FD75005EA453 /* theme_color_dark.png */; };
1740C6891C10FD75005EA453 /* theme_color_light.png in Resources */ = {isa = PBXBuildFile; fileRef = 1740C6851C10FD75005EA453 /* theme_color_light.png */; };
1740C68A1C10FD75005EA453 /* theme_color_medium.png in Resources */ = {isa = PBXBuildFile; fileRef = 1740C6861C10FD75005EA453 /* theme_color_medium.png */; };
@ -643,6 +644,8 @@
010EDEFB1B238722003B79DE /* 1Password.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = 1Password.xcassets; path = "Other Sources/OnePasswordExtension/1Password.xcassets"; sourceTree = "<group>"; };
1715D0292166B3F900227731 /* PremiumManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PremiumManager.h; sourceTree = "<group>"; };
1715D02A2166B3F900227731 /* PremiumManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PremiumManager.m; sourceTree = "<group>"; };
17362ADB23639B4E00A0FCCC /* OfflineFetchText.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OfflineFetchText.h; path = offline/OfflineFetchText.h; sourceTree = "<group>"; };
17362ADC23639B4E00A0FCCC /* OfflineFetchText.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = OfflineFetchText.m; path = offline/OfflineFetchText.m; sourceTree = "<group>"; };
1740C6841C10FD75005EA453 /* theme_color_dark.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = theme_color_dark.png; sourceTree = "<group>"; };
1740C6851C10FD75005EA453 /* theme_color_light.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = theme_color_light.png; sourceTree = "<group>"; };
1740C6861C10FD75005EA453 /* theme_color_medium.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = theme_color_medium.png; sourceTree = "<group>"; };
@ -2470,6 +2473,8 @@
FF855B5A1794B0670098D48A /* OfflineSyncUnreads.m */,
FF855B5C1794B0760098D48A /* OfflineFetchStories.h */,
FF855B5D1794B0760098D48A /* OfflineFetchStories.m */,
17362ADB23639B4E00A0FCCC /* OfflineFetchText.h */,
17362ADC23639B4E00A0FCCC /* OfflineFetchText.m */,
FF855B5F1794B0830098D48A /* OfflineFetchImages.h */,
FF855B601794B0830098D48A /* OfflineFetchImages.m */,
FF0FAEAF17B0846C008651F9 /* OfflineCleanImages.h */,
@ -2724,7 +2729,7 @@
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1100;
LastUpgradeCheck = 1110;
ORGANIZATIONNAME = NewsBlur;
TargetAttributes = {
1749390F1C251BFE003D98AA = {
@ -3391,6 +3396,7 @@
FF855B5B1794B0670098D48A /* OfflineSyncUnreads.m in Sources */,
FF34FD6A1E9D93CB0062F8ED /* IASKPSSliderSpecifierViewCell.m in Sources */,
FF855B5E1794B0760098D48A /* OfflineFetchStories.m in Sources */,
17362ADD23639B4E00A0FCCC /* OfflineFetchText.m in Sources */,
FF855B611794B0830098D48A /* OfflineFetchImages.m in Sources */,
FF8D1EA71BAA304E00725D8A /* Reachability.m in Sources */,
FFCDD90117F65A71000C6483 /* NBSwipeableCell.m in Sources */,

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1100"
LastUpgradeVersion = "1110"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1100"
LastUpgradeVersion = "1110"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1100"
LastUpgradeVersion = "1110"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction

View file

@ -436,6 +436,16 @@
<key>DefaultValue</key>
<true/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Download text</string>
<key>Key</key>
<string>offline_text_download</string>
<key>DefaultValue</key>
<true/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>

View file

@ -456,6 +456,16 @@
<key>DefaultValue</key>
<true/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Download text</string>
<key>Key</key>
<string>offline_text_download</string>
<key>DefaultValue</key>
<true/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>