2010-06-17 13:10:45 -04:00
|
|
|
|
// NewsBlurAppDelegate.m
|
|
|
|
|
// NewsBlur
|
|
|
|
|
//
|
|
|
|
|
// Created by Samuel Clay on 6/16/10.
|
2010-06-21 17:17:26 -04:00
|
|
|
|
// Copyright NewsBlur 2010. All rights reserved.
|
2010-06-17 13:10:45 -04:00
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#import "NewsBlurAppDelegate.h"
|
2012-06-19 11:48:51 -07:00
|
|
|
|
#import "NewsBlurViewController.h"
|
2012-07-24 11:44:16 -07:00
|
|
|
|
#import "NBContainerViewController.h"
|
2010-06-21 17:17:26 -04:00
|
|
|
|
#import "FeedDetailViewController.h"
|
2012-07-15 15:06:06 -07:00
|
|
|
|
#import "DashboardViewController.h"
|
2015-11-13 21:54:32 -08:00
|
|
|
|
#import "MarkReadMenuViewController.h"
|
2012-06-19 10:55:46 -07:00
|
|
|
|
#import "FeedsMenuViewController.h"
|
2010-06-25 18:36:01 -04:00
|
|
|
|
#import "StoryDetailViewController.h"
|
2012-11-05 15:12:42 -08:00
|
|
|
|
#import "StoryPageControl.h"
|
2012-06-13 18:07:24 -07:00
|
|
|
|
#import "FirstTimeUserViewController.h"
|
2012-08-12 18:50:32 -07:00
|
|
|
|
#import "FriendsListViewController.h"
|
2010-10-31 23:02:13 -04:00
|
|
|
|
#import "LoginViewController.h"
|
2011-12-02 16:23:00 -08:00
|
|
|
|
#import "AddSiteViewController.h"
|
|
|
|
|
#import "MoveSiteViewController.h"
|
2012-12-24 23:01:25 -08:00
|
|
|
|
#import "TrainerViewController.h"
|
2016-11-23 08:09:19 -08:00
|
|
|
|
#import "NotificationsViewController.h"
|
2014-11-10 16:00:59 -08:00
|
|
|
|
#import "UserTagsViewController.h"
|
2010-11-13 18:32:43 -05:00
|
|
|
|
#import "OriginalStoryViewController.h"
|
2012-06-21 11:53:48 -07:00
|
|
|
|
#import "ShareViewController.h"
|
2015-10-31 16:35:32 -07:00
|
|
|
|
#import "FontSettingsViewController.h"
|
2016-01-27 20:52:09 -08:00
|
|
|
|
#import "FeedChooserViewController.h"
|
2012-07-01 18:26:39 -07:00
|
|
|
|
#import "UserProfileViewController.h"
|
2017-11-10 17:37:31 -08:00
|
|
|
|
#import "PremiumViewController.h"
|
2012-08-14 17:20:45 -07:00
|
|
|
|
#import "InteractionsModule.h"
|
|
|
|
|
#import "ActivityModule.h"
|
2012-08-15 13:04:05 -07:00
|
|
|
|
#import "FirstTimeUserViewController.h"
|
|
|
|
|
#import "FirstTimeUserAddSitesViewController.h"
|
|
|
|
|
#import "FirstTimeUserAddFriendsViewController.h"
|
|
|
|
|
#import "FirstTimeUserAddNewsBlurViewController.h"
|
2014-10-22 17:22:16 -07:00
|
|
|
|
#import "TUSafariActivity.h"
|
|
|
|
|
#import "ARChromeActivity.h"
|
2015-09-22 22:11:48 -07:00
|
|
|
|
#import "NBCopyLinkActivity.h"
|
2011-10-27 09:44:58 -07:00
|
|
|
|
#import "MBProgressHUD.h"
|
2011-10-28 18:29:33 -07:00
|
|
|
|
#import "Utilities.h"
|
2011-12-04 21:09:16 -08:00
|
|
|
|
#import "StringHelper.h"
|
2013-03-04 20:21:29 -08:00
|
|
|
|
#import "AuthorizeServicesViewController.h"
|
2013-05-30 18:45:51 -07:00
|
|
|
|
#import "Reachability.h"
|
2013-06-05 17:11:01 -07:00
|
|
|
|
#import "FMDatabase.h"
|
2013-06-14 19:21:30 -07:00
|
|
|
|
#import "FMDatabaseQueue.h"
|
2013-06-16 21:39:38 -07:00
|
|
|
|
#import "FMDatabaseAdditions.h"
|
2015-09-16 16:53:07 -07:00
|
|
|
|
#import "SBJson4.h"
|
|
|
|
|
#import "NSObject+SBJSON.h"
|
2013-06-21 17:48:06 -07:00
|
|
|
|
#import "IASKAppSettingsViewController.h"
|
2013-07-15 18:25:09 -07:00
|
|
|
|
#import "OfflineSyncUnreads.h"
|
|
|
|
|
#import "OfflineFetchStories.h"
|
2019-10-25 20:52:51 -07:00
|
|
|
|
#import "OfflineFetchText.h"
|
2013-07-15 18:25:09 -07:00
|
|
|
|
#import "OfflineFetchImages.h"
|
2013-08-05 18:32:43 -07:00
|
|
|
|
#import "OfflineCleanImages.h"
|
2014-01-06 18:37:57 -08:00
|
|
|
|
#import "NBBarButtonItem.h"
|
2017-04-03 18:52:23 -07:00
|
|
|
|
#import "PINCache.h"
|
2014-02-12 20:09:37 -08:00
|
|
|
|
#import "StoriesCollection.h"
|
2014-02-18 16:44:41 -08:00
|
|
|
|
#import "NSString+HTML.h"
|
2014-09-26 14:38:28 -07:00
|
|
|
|
#import "UIView+ViewController.h"
|
2014-09-26 17:38:35 -07:00
|
|
|
|
#import "NBURLCache.h"
|
2016-06-04 09:31:09 -04:00
|
|
|
|
#import "NBActivityItemSource.h"
|
2015-11-17 21:10:57 -08:00
|
|
|
|
#import "NSNull+JSON.h"
|
2015-12-07 16:09:49 -08:00
|
|
|
|
#import "UISearchBar+Field.h"
|
2016-03-19 13:19:06 -04:00
|
|
|
|
#import "UIViewController+HidePopover.h"
|
2017-04-04 15:33:08 -07:00
|
|
|
|
#import "PINCache.h"
|
2014-01-06 14:51:35 -08:00
|
|
|
|
#import <float.h>
|
2016-11-17 19:53:46 -08:00
|
|
|
|
#import <UserNotifications/UserNotifications.h>
|
2019-04-24 20:32:04 -07:00
|
|
|
|
#import <Intents/Intents.h>
|
|
|
|
|
#import <CoreSpotlight/CoreSpotlight.h>
|
|
|
|
|
#import <CoreServices/CoreServices.h>
|
2010-06-17 13:10:45 -04:00
|
|
|
|
|
2016-11-17 19:53:46 -08:00
|
|
|
|
@interface NewsBlurAppDelegate () <UIViewControllerTransitioningDelegate, UNUserNotificationCenterDelegate>
|
2015-10-24 22:22:16 -07:00
|
|
|
|
|
2016-01-21 22:11:37 -08:00
|
|
|
|
@property (nonatomic, strong) NSString *cachedURL;
|
2015-11-05 16:43:43 -08:00
|
|
|
|
@property (nonatomic, strong) UIApplicationShortcutItem *launchedShortcutItem;
|
2015-11-11 11:02:16 -08:00
|
|
|
|
@property (nonatomic, strong) SFSafariViewController *safariViewController;
|
2019-08-22 12:03:39 -07:00
|
|
|
|
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *networkBackgroundTasks;
|
2015-10-24 22:22:16 -07:00
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
2010-06-17 13:10:45 -04:00
|
|
|
|
@implementation NewsBlurAppDelegate
|
|
|
|
|
|
2019-10-25 20:52:51 -07:00
|
|
|
|
#define CURRENT_DB_VERSION 37
|
2012-11-01 15:26:50 -07:00
|
|
|
|
|
2019-03-22 20:55:22 -07:00
|
|
|
|
#define CURRENT_STATE_VERSION 1
|
|
|
|
|
|
2010-06-17 13:10:45 -04:00
|
|
|
|
@synthesize window;
|
2012-06-09 12:54:32 -07:00
|
|
|
|
|
2012-07-22 14:23:50 -07:00
|
|
|
|
@synthesize ftuxNavigationController;
|
2010-06-21 17:17:26 -04:00
|
|
|
|
@synthesize navigationController;
|
2012-07-31 17:17:02 -07:00
|
|
|
|
@synthesize modalNavigationController;
|
2012-08-13 18:45:06 -07:00
|
|
|
|
@synthesize shareNavigationController;
|
2013-03-02 19:15:08 -08:00
|
|
|
|
@synthesize trainNavigationController;
|
2016-11-23 08:09:19 -08:00
|
|
|
|
@synthesize notificationsNavigationController;
|
2017-11-09 18:43:37 -08:00
|
|
|
|
@synthesize premiumNavigationController;
|
2012-07-28 12:56:51 -07:00
|
|
|
|
@synthesize userProfileNavigationController;
|
2012-07-24 11:44:16 -07:00
|
|
|
|
@synthesize masterContainerViewController;
|
2012-07-10 12:34:58 -07:00
|
|
|
|
@synthesize dashboardViewController;
|
2010-06-21 17:17:26 -04:00
|
|
|
|
@synthesize feedsViewController;
|
2012-06-19 10:55:46 -07:00
|
|
|
|
@synthesize feedsMenuViewController;
|
2010-06-24 12:53:50 -04:00
|
|
|
|
@synthesize feedDetailViewController;
|
2012-07-01 18:26:39 -07:00
|
|
|
|
@synthesize friendsListViewController;
|
2012-06-18 14:31:42 -07:00
|
|
|
|
@synthesize fontSettingsViewController;
|
2010-06-25 18:36:01 -04:00
|
|
|
|
@synthesize storyDetailViewController;
|
2012-11-05 15:12:42 -08:00
|
|
|
|
@synthesize storyPageControl;
|
2012-06-22 18:01:08 -07:00
|
|
|
|
@synthesize shareViewController;
|
2010-10-31 23:02:13 -04:00
|
|
|
|
@synthesize loginViewController;
|
2011-12-02 16:23:00 -08:00
|
|
|
|
@synthesize addSiteViewController;
|
|
|
|
|
@synthesize moveSiteViewController;
|
2012-12-24 23:01:25 -08:00
|
|
|
|
@synthesize trainerViewController;
|
2016-11-23 08:09:19 -08:00
|
|
|
|
@synthesize notificationsViewController;
|
2014-11-10 16:00:59 -08:00
|
|
|
|
@synthesize userTagsViewController;
|
2010-11-13 18:32:43 -05:00
|
|
|
|
@synthesize originalStoryViewController;
|
2013-09-25 17:43:00 -07:00
|
|
|
|
@synthesize originalStoryViewNavController;
|
2012-07-01 18:26:39 -07:00
|
|
|
|
@synthesize userProfileViewController;
|
2013-06-21 17:48:06 -07:00
|
|
|
|
@synthesize preferencesViewController;
|
2017-11-09 18:43:37 -08:00
|
|
|
|
@synthesize premiumViewController;
|
2010-06-17 13:10:45 -04:00
|
|
|
|
|
2012-07-22 14:23:50 -07:00
|
|
|
|
@synthesize firstTimeUserViewController;
|
|
|
|
|
@synthesize firstTimeUserAddSitesViewController;
|
|
|
|
|
@synthesize firstTimeUserAddFriendsViewController;
|
|
|
|
|
@synthesize firstTimeUserAddNewsBlurViewController;
|
|
|
|
|
|
2017-03-19 18:15:36 -07:00
|
|
|
|
@synthesize networkManager;
|
2012-06-20 08:33:16 -07:00
|
|
|
|
@synthesize feedDetailPortraitYCoordinate;
|
2014-02-24 18:56:51 -08:00
|
|
|
|
@synthesize cachedFavicons;
|
|
|
|
|
@synthesize cachedStoryImages;
|
2010-11-13 18:32:43 -05:00
|
|
|
|
@synthesize activeUsername;
|
2012-07-03 17:54:36 -07:00
|
|
|
|
@synthesize activeUserProfileId;
|
2012-07-28 14:15:20 -07:00
|
|
|
|
@synthesize activeUserProfileName;
|
2012-10-02 15:39:18 -07:00
|
|
|
|
@synthesize hasNoSites;
|
2012-08-06 19:21:39 -07:00
|
|
|
|
@synthesize isTryFeedView;
|
2012-08-02 18:00:48 -07:00
|
|
|
|
|
2012-07-16 22:35:28 -07:00
|
|
|
|
@synthesize inFindingStoryMode;
|
2013-07-17 19:22:41 -07:00
|
|
|
|
@synthesize hasLoadedFeedDetail;
|
2012-07-16 19:45:14 -07:00
|
|
|
|
@synthesize tryFeedStoryId;
|
2016-12-06 16:03:40 -08:00
|
|
|
|
@synthesize tryFeedFeedId;
|
2012-07-31 23:49:51 -07:00
|
|
|
|
@synthesize tryFeedCategory;
|
2012-06-19 15:47:51 -07:00
|
|
|
|
@synthesize popoverHasFeedView;
|
2012-06-29 23:25:56 -07:00
|
|
|
|
@synthesize inFeedDetail;
|
2012-08-02 18:00:48 -07:00
|
|
|
|
@synthesize inStoryDetail;
|
2014-10-30 17:26:34 -07:00
|
|
|
|
@synthesize isPresentingActivities;
|
2012-06-25 18:05:25 -07:00
|
|
|
|
@synthesize activeComment;
|
2012-07-27 12:27:13 -07:00
|
|
|
|
@synthesize activeShareType;
|
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
@synthesize storiesCollection;
|
2014-02-11 16:55:15 -08:00
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
@synthesize activeStory;
|
2012-10-17 15:07:53 -07:00
|
|
|
|
@synthesize savedStoriesCount;
|
2011-08-09 17:58:43 -07:00
|
|
|
|
@synthesize originalStoryCount;
|
2011-07-29 09:06:17 -07:00
|
|
|
|
@synthesize selectedIntelligence;
|
2010-11-13 18:32:43 -05:00
|
|
|
|
@synthesize activeOriginalStoryURL;
|
2011-07-24 21:47:58 -07:00
|
|
|
|
@synthesize recentlyReadStories;
|
2011-11-03 18:08:24 -07:00
|
|
|
|
@synthesize recentlyReadFeeds;
|
2011-08-22 18:25:33 -07:00
|
|
|
|
@synthesize readStories;
|
2013-09-05 12:38:06 -07:00
|
|
|
|
@synthesize unreadStoryHashes;
|
2018-09-13 10:44:00 -07:00
|
|
|
|
@synthesize unsavedStoryHashes;
|
2012-10-14 17:30:03 -07:00
|
|
|
|
@synthesize folderCountCache;
|
2014-09-26 18:35:40 -07:00
|
|
|
|
@synthesize collapsedFolders;
|
2014-10-01 14:23:57 -07:00
|
|
|
|
@synthesize fontDescriptorTitleSize;
|
2010-06-17 13:10:45 -04:00
|
|
|
|
|
2011-10-04 18:01:35 -07:00
|
|
|
|
@synthesize dictFolders;
|
|
|
|
|
@synthesize dictFeeds;
|
2012-06-26 16:24:19 -07:00
|
|
|
|
@synthesize dictActiveFeeds;
|
2012-06-25 15:02:20 -07:00
|
|
|
|
@synthesize dictSocialFeeds;
|
2014-05-20 12:21:17 -07:00
|
|
|
|
@synthesize dictSavedStoryTags;
|
2013-03-04 20:21:29 -08:00
|
|
|
|
@synthesize dictSocialProfile;
|
2012-07-01 18:26:39 -07:00
|
|
|
|
@synthesize dictUserProfile;
|
2013-03-04 20:21:29 -08:00
|
|
|
|
@synthesize dictSocialServices;
|
2013-06-29 17:28:41 -07:00
|
|
|
|
@synthesize dictUnreadCounts;
|
2015-10-05 11:45:33 -07:00
|
|
|
|
@synthesize dictTextFeeds;
|
2017-11-09 18:43:37 -08:00
|
|
|
|
@synthesize isPremium;
|
2017-11-10 17:37:31 -08:00
|
|
|
|
@synthesize premiumExpire;
|
2012-07-26 23:07:47 -07:00
|
|
|
|
@synthesize userInteractionsArray;
|
|
|
|
|
@synthesize userActivitiesArray;
|
2011-10-04 18:01:35 -07:00
|
|
|
|
@synthesize dictFoldersArray;
|
2016-11-23 09:29:29 -08:00
|
|
|
|
@synthesize notificationFeedIds;
|
2011-10-04 18:01:35 -07:00
|
|
|
|
|
2013-06-05 17:11:01 -07:00
|
|
|
|
@synthesize database;
|
2012-08-14 17:20:45 -07:00
|
|
|
|
@synthesize categories;
|
|
|
|
|
@synthesize categoryFeeds;
|
2013-06-23 22:19:08 -07:00
|
|
|
|
@synthesize activeCachedImages;
|
2013-07-15 18:25:09 -07:00
|
|
|
|
@synthesize hasQueuedReadStories;
|
|
|
|
|
@synthesize offlineQueue;
|
2013-08-05 18:32:43 -07:00
|
|
|
|
@synthesize offlineCleaningQueue;
|
2013-10-17 12:32:19 -07:00
|
|
|
|
@synthesize backgroundCompletionHandler;
|
2014-02-11 15:20:41 -08:00
|
|
|
|
@synthesize cacheImagesOperationQueue;
|
2012-08-14 17:20:45 -07:00
|
|
|
|
|
2014-02-11 16:55:15 -08:00
|
|
|
|
@synthesize totalUnfetchedStoryCount;
|
|
|
|
|
@synthesize remainingUnfetchedStoryCount;
|
|
|
|
|
@synthesize latestFetchedStoryDate;
|
|
|
|
|
@synthesize latestCachedImageDate;
|
|
|
|
|
@synthesize totalUncachedImagesCount;
|
|
|
|
|
@synthesize remainingUncachedImagesCount;
|
|
|
|
|
|
2011-12-03 18:22:14 -08:00
|
|
|
|
+ (NewsBlurAppDelegate*) sharedAppDelegate {
|
|
|
|
|
return (NewsBlurAppDelegate*) [UIApplication sharedApplication].delegate;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-22 20:55:22 -07:00
|
|
|
|
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
2013-07-16 18:06:36 -07:00
|
|
|
|
[self registerDefaultsFromSettingsBundle];
|
2012-06-30 00:02:08 -07:00
|
|
|
|
|
2013-03-03 14:03:56 -08:00
|
|
|
|
self.navigationController.delegate = self;
|
2012-07-24 16:03:16 -07:00
|
|
|
|
self.navigationController.viewControllers = [NSArray arrayWithObject:self.feedsViewController];
|
2014-02-13 17:18:29 -08:00
|
|
|
|
self.storiesCollection = [StoriesCollection new];
|
2015-03-12 19:43:39 -07:00
|
|
|
|
|
2012-06-30 00:02:08 -07:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2012-07-24 11:44:16 -07:00
|
|
|
|
self.window.rootViewController = self.masterContainerViewController;
|
2012-06-09 12:54:32 -07:00
|
|
|
|
} else {
|
2012-07-24 16:03:16 -07:00
|
|
|
|
self.window.rootViewController = self.navigationController;
|
2012-06-09 12:54:32 -07:00
|
|
|
|
}
|
2013-04-12 10:11:21 -07:00
|
|
|
|
|
2017-04-03 18:52:23 -07:00
|
|
|
|
[self clearNetworkManager];
|
2013-04-12 10:11:21 -07:00
|
|
|
|
|
2010-06-17 13:10:45 -04:00
|
|
|
|
[window makeKeyAndVisible];
|
2013-10-08 16:00:55 -07:00
|
|
|
|
|
2015-12-07 16:09:49 -08:00
|
|
|
|
[[ThemeManager themeManager] prepareForWindow:self.window];
|
2013-05-30 16:31:57 -07:00
|
|
|
|
|
2013-06-14 19:21:30 -07:00
|
|
|
|
[self createDatabaseConnection];
|
2017-04-03 18:52:23 -07:00
|
|
|
|
[self.cachedStoryImages removeAllObjects:nil];
|
2016-12-06 16:03:40 -08:00
|
|
|
|
[feedsViewController loadOfflineFeeds:NO];
|
2013-10-17 17:23:52 -07:00
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
|
|
|
|
|
(unsigned long)NULL), ^(void) {
|
2015-09-16 16:53:07 -07:00
|
|
|
|
[self setupReachability];
|
2014-02-11 15:20:41 -08:00
|
|
|
|
cacheImagesOperationQueue = [NSOperationQueue new];
|
2014-02-12 20:41:29 -08:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
cacheImagesOperationQueue.maxConcurrentOperationCount = 2;
|
|
|
|
|
} else {
|
|
|
|
|
cacheImagesOperationQueue.maxConcurrentOperationCount = 1;
|
|
|
|
|
}
|
2013-10-17 17:23:52 -07:00
|
|
|
|
});
|
2013-10-08 16:00:55 -07:00
|
|
|
|
|
2013-05-30 16:31:57 -07:00
|
|
|
|
// [self showFirstTimeUser];
|
2014-02-24 18:56:51 -08:00
|
|
|
|
|
2017-04-03 18:52:23 -07:00
|
|
|
|
cachedFavicons = [[PINCache alloc] initWithName:@"NBFavicons"];
|
2018-02-20 11:17:51 -08:00
|
|
|
|
cachedFavicons.memoryCache.removeAllObjectsOnEnteringBackground = NO;
|
2017-04-03 18:52:23 -07:00
|
|
|
|
cachedStoryImages = [[PINCache alloc] initWithName:@"NBStoryImages"];
|
2018-02-20 11:17:51 -08:00
|
|
|
|
cachedStoryImages.memoryCache.removeAllObjectsOnEnteringBackground = NO;
|
2017-11-09 18:43:37 -08:00
|
|
|
|
isPremium = NO;
|
2017-11-15 11:14:05 -08:00
|
|
|
|
premiumExpire = 0;
|
2014-09-26 17:38:35 -07:00
|
|
|
|
|
|
|
|
|
NBURLCache *urlCache = [[NBURLCache alloc] init];
|
|
|
|
|
[NSURLCache setSharedURLCache:urlCache];
|
2014-09-26 17:51:01 -07:00
|
|
|
|
// Uncomment below line to test image caching
|
|
|
|
|
// [[NSURLCache sharedURLCache] removeAllCachedResponses];
|
2014-09-26 17:38:35 -07:00
|
|
|
|
|
2019-03-22 20:55:22 -07:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
2015-11-18 15:20:03 -08:00
|
|
|
|
if ([UIApplicationShortcutItem class] && launchOptions[UIApplicationLaunchOptionsShortcutItemKey]) {
|
2015-11-05 16:43:43 -08:00
|
|
|
|
self.launchedShortcutItem = launchOptions[UIApplicationLaunchOptionsShortcutItemKey];
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-06 16:03:40 -08:00
|
|
|
|
if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
|
|
|
|
|
NSDictionary *notification = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
|
|
|
|
|
[self processNotification:notification
|
|
|
|
|
action:@"com.apple.UNNotificationDefaultActionIdentifier"
|
|
|
|
|
withCompletionHandler:nil];
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-30 16:31:57 -07:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-05 16:43:43 -08:00
|
|
|
|
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
|
|
|
|
if (self.launchedShortcutItem) {
|
|
|
|
|
[self handleShortcutItem:self.launchedShortcutItem];
|
|
|
|
|
self.launchedShortcutItem = nil;
|
|
|
|
|
}
|
2019-03-22 20:55:22 -07:00
|
|
|
|
|
|
|
|
|
if (storyPageControl.temporarilyMarkedUnread && [storiesCollection isStoryUnread:activeStory]) {
|
|
|
|
|
[storiesCollection markStoryRead:activeStory];
|
|
|
|
|
[storiesCollection syncStoryAsRead:activeStory];
|
|
|
|
|
storyPageControl.temporarilyMarkedUnread = NO;
|
2019-07-17 20:32:14 -07:00
|
|
|
|
|
|
|
|
|
[self.feedDetailViewController reloadData];
|
|
|
|
|
[self.storyPageControl refreshHeaders];
|
2019-03-22 20:55:22 -07:00
|
|
|
|
}
|
2015-11-05 16:43:43 -08:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-06 12:42:38 -07:00
|
|
|
|
- (void)applicationWillResignActive:(UIApplication *)application {
|
|
|
|
|
[self.feedsViewController refreshHeaderCounts];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)applicationWillTerminate:(UIApplication *)application {
|
|
|
|
|
[self.feedsViewController refreshHeaderCounts];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
|
|
|
|
[self.feedsViewController refreshHeaderCounts];
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-22 20:55:22 -07:00
|
|
|
|
- (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder {
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)application:(UIApplication *)application willEncodeRestorableStateWithCoder:(NSCoder *)coder {
|
|
|
|
|
[coder encodeInteger:CURRENT_STATE_VERSION forKey:@"version"];
|
|
|
|
|
[coder encodeObject:[NSDate date] forKey:@"last_saved_state_date"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder {
|
|
|
|
|
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
NSString *option = [preferences stringForKey:@"restore_state"];
|
|
|
|
|
|
|
|
|
|
if ([option isEqualToString:@"never"]) {
|
|
|
|
|
return NO;
|
|
|
|
|
} else if ([option isEqualToString:@"always"]) {
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSTimeInterval daysInterval = 60 * 60;
|
|
|
|
|
NSTimeInterval limitInterval = option.doubleValue * daysInterval;
|
|
|
|
|
NSInteger version = [coder decodeIntegerForKey:@"version"];
|
|
|
|
|
NSDate *lastSavedDate = [coder decodeObjectOfClass:[NSDate class] forKey:@"last_saved_state_date"];
|
|
|
|
|
|
|
|
|
|
if (limitInterval == 0) {
|
|
|
|
|
limitInterval = 24 * daysInterval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (version > CURRENT_STATE_VERSION || lastSavedDate == nil) {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSTimeInterval savedInterval = -[lastSavedDate timeIntervalSinceNow];
|
|
|
|
|
|
|
|
|
|
return savedInterval < limitInterval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray<NSString *> *)identifierComponents coder:(NSCoder *)coder {
|
|
|
|
|
NSString *identifier = identifierComponents.lastObject;
|
|
|
|
|
|
|
|
|
|
if ([identifier isEqualToString:@"MainNavigation"]) {
|
|
|
|
|
return self.navigationController;
|
|
|
|
|
} else if ([identifier isEqualToString:@"FeedsView"]) {
|
|
|
|
|
return self.feedsViewController;
|
|
|
|
|
} else if ([identifier isEqualToString:@"FeedDetailView"]) {
|
|
|
|
|
return self.feedDetailViewController;
|
|
|
|
|
} else if ([identifier isEqualToString:@"StoryPageControl"]) {
|
|
|
|
|
return self.storyPageControl;
|
|
|
|
|
} else if ([identifier isEqualToString:@"ContainerView"]) {
|
|
|
|
|
return self.masterContainerViewController;
|
|
|
|
|
} else {
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)application:(UIApplication *)application didDecodeRestorableStateWithCoder:(NSCoder *)coder {
|
|
|
|
|
// All done; could do any cleanup here
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-24 20:32:04 -07:00
|
|
|
|
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler {
|
|
|
|
|
[self handleUserActivity:userActivity];
|
|
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-05 16:43:43 -08:00
|
|
|
|
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
|
|
|
|
|
completionHandler([self handleShortcutItem:shortcutItem]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)handleShortcutItem:(UIApplicationShortcutItem *)shortcutItem {
|
|
|
|
|
NSString *type = shortcutItem.type;
|
2015-12-16 21:57:05 -05:00
|
|
|
|
NSString *prefix = [[NSBundle mainBundle].bundleIdentifier stringByAppendingString:@"."];
|
2015-11-05 16:43:43 -08:00
|
|
|
|
BOOL handled = YES;
|
|
|
|
|
|
2015-12-16 21:57:05 -05:00
|
|
|
|
if (!self.activeUsername) {
|
2015-11-05 17:36:39 -08:00
|
|
|
|
handled = NO;
|
2015-12-16 21:57:05 -05:00
|
|
|
|
} else if ([type startsWith:prefix]) {
|
|
|
|
|
type = [type substringFromIndex:[prefix length]];
|
|
|
|
|
if ([type isEqualToString:@"AddFeed"]) {
|
|
|
|
|
[self.navigationController popToRootViewControllerAnimated:NO];
|
|
|
|
|
[self performSelector:@selector(delayedAddSite) withObject:nil afterDelay:0.0];
|
|
|
|
|
} else if ([type isEqualToString:@"AllStories"]) {
|
|
|
|
|
[self.navigationController popToRootViewControllerAnimated:NO];
|
|
|
|
|
[self.feedsViewController didSelectSectionHeaderWithTag:2];
|
|
|
|
|
} else if ([type isEqualToString:@"Search"]) {
|
|
|
|
|
[self.navigationController popToRootViewControllerAnimated:NO];
|
|
|
|
|
[self.feedsViewController didSelectSectionHeaderWithTag:2];
|
|
|
|
|
self.feedDetailViewController.storiesCollection.searchQuery = @"";
|
2020-06-11 21:02:22 -07:00
|
|
|
|
self.feedDetailViewController.storiesCollection.savedSearchQuery = nil;
|
2015-12-16 21:57:05 -05:00
|
|
|
|
self.feedDetailViewController.storiesCollection.inSearch = YES;
|
|
|
|
|
} else {
|
|
|
|
|
handled = NO;
|
|
|
|
|
}
|
2015-11-05 16:43:43 -08:00
|
|
|
|
} else {
|
|
|
|
|
handled = NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return handled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)delayedAddSite {
|
|
|
|
|
[self.feedsViewController tapAddSite:self];
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-03 12:12:06 -08:00
|
|
|
|
- (void)viewWillAppear:(BOOL)animated {
|
2014-09-18 11:25:51 -07:00
|
|
|
|
[super viewWillAppear:animated];
|
|
|
|
|
|
2014-03-03 12:12:06 -08:00
|
|
|
|
self.title = @"All";
|
|
|
|
|
}
|
2013-10-17 12:32:19 -07:00
|
|
|
|
|
|
|
|
|
- (void)application:(UIApplication *)application
|
|
|
|
|
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
|
|
|
|
|
[self createDatabaseConnection];
|
|
|
|
|
[self.feedsViewController fetchFeedList:NO];
|
|
|
|
|
backgroundCompletionHandler = completionHandler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)finishBackground {
|
|
|
|
|
if (!backgroundCompletionHandler) return;
|
|
|
|
|
|
2017-04-03 18:52:23 -07:00
|
|
|
|
NSLog(@"Background fetch complete. Found data: %ld/%ld = %d",
|
|
|
|
|
(long)self.totalUnfetchedStoryCount, (long)self.totalUncachedImagesCount,
|
2013-10-17 12:32:19 -07:00
|
|
|
|
self.totalUnfetchedStoryCount || self.totalUncachedImagesCount);
|
|
|
|
|
if (self.totalUnfetchedStoryCount || self.totalUncachedImagesCount) {
|
|
|
|
|
backgroundCompletionHandler(UIBackgroundFetchResultNewData);
|
|
|
|
|
} else {
|
|
|
|
|
backgroundCompletionHandler(UIBackgroundFetchResultNoData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-16 18:06:36 -07:00
|
|
|
|
- (void)registerDefaultsFromSettingsBundle {
|
|
|
|
|
NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
|
|
|
|
|
if(!settingsBundle) {
|
|
|
|
|
NSLog(@"Could not find Settings.bundle");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:@"Root.plist"]];
|
|
|
|
|
NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];
|
|
|
|
|
|
|
|
|
|
NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
|
|
|
|
|
for(NSDictionary *prefSpecification in preferences) {
|
|
|
|
|
NSString *key = [prefSpecification objectForKey:@"Key"];
|
|
|
|
|
if (key && [[prefSpecification allKeys] containsObject:@"DefaultValue"]) {
|
|
|
|
|
[defaultsToRegister setObject:[prefSpecification objectForKey:@"DefaultValue"] forKey:key];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];
|
2017-11-28 16:22:46 -08:00
|
|
|
|
|
|
|
|
|
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
|
|
|
|
|
[[NSUserDefaults standardUserDefaults] setObject:version forKey:@"version"];
|
2013-07-16 18:06:36 -07:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-17 19:53:46 -08:00
|
|
|
|
- (void)registerForRemoteNotifications {
|
|
|
|
|
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
|
|
|
|
center.delegate = self;
|
|
|
|
|
[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error){
|
|
|
|
|
if(!error){
|
2017-09-26 10:48:02 -07:00
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
|
|
|
|
});
|
2016-11-17 19:53:46 -08:00
|
|
|
|
}
|
|
|
|
|
}];
|
2016-11-21 15:31:14 -08:00
|
|
|
|
|
2016-11-21 16:55:58 -08:00
|
|
|
|
// UNNotificationAction *viewAction = [UNNotificationAction actionWithIdentifier:@"VIEW_STORY_IDENTIFIER"
|
|
|
|
|
// title:@"View story"
|
|
|
|
|
// options:UNNotificationActionOptionForeground];
|
2016-11-21 15:31:14 -08:00
|
|
|
|
UNNotificationAction *readAction = [UNNotificationAction actionWithIdentifier:@"MARK_READ_IDENTIFIER"
|
|
|
|
|
title:@"Mark read"
|
|
|
|
|
options:UNNotificationActionOptionNone];
|
|
|
|
|
UNNotificationAction *starAction = [UNNotificationAction actionWithIdentifier:@"STAR_IDENTIFIER"
|
|
|
|
|
title:@"Save story"
|
|
|
|
|
options:UNNotificationActionOptionNone];
|
|
|
|
|
UNNotificationAction *dismissAction = [UNNotificationAction actionWithIdentifier:@"DISMISS_IDENTIFIER"
|
|
|
|
|
title:@"Dismiss"
|
|
|
|
|
options:UNNotificationActionOptionDestructive];
|
|
|
|
|
UNNotificationCategory *storyCategory = [UNNotificationCategory categoryWithIdentifier:@"STORY_CATEGORY"
|
2016-11-21 16:55:58 -08:00
|
|
|
|
actions:@[readAction, starAction, dismissAction]
|
2016-11-21 15:31:14 -08:00
|
|
|
|
intentIdentifiers:@[]
|
2016-11-21 18:10:17 -08:00
|
|
|
|
options:UNNotificationCategoryOptionNone];
|
2016-11-21 15:31:14 -08:00
|
|
|
|
[center setNotificationCategories:[NSSet setWithObject:storyCategory]];
|
2016-11-17 19:53:46 -08:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-04 14:55:09 -07:00
|
|
|
|
|
|
|
|
|
- (void)registerForBadgeNotifications {
|
|
|
|
|
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
|
|
|
|
center.delegate = self;
|
|
|
|
|
[center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error){
|
|
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-17 19:53:46 -08:00
|
|
|
|
//Called when a notification is delivered to a foreground app.
|
|
|
|
|
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
|
|
|
|
|
NSLog(@"User Info : %@",notification.request.content.userInfo);
|
|
|
|
|
completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Called to let your app know which action was selected by the user for a given notification.
|
2017-09-26 10:48:02 -07:00
|
|
|
|
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
|
2016-12-06 16:03:40 -08:00
|
|
|
|
[self processNotification:response.notification.request.content.userInfo
|
|
|
|
|
action:response.actionIdentifier
|
|
|
|
|
withCompletionHandler:completionHandler];
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-26 10:48:02 -07:00
|
|
|
|
- (void)processNotification:(NSDictionary *)content action:(NSString *)action withCompletionHandler:(void(^)(void))completionHandler {
|
2016-12-06 16:03:40 -08:00
|
|
|
|
NSLog(@"User Info : %@ / %@", content, action);
|
|
|
|
|
NSString *storyHash = [content objectForKey:@"story_hash"];
|
|
|
|
|
NSNumber *storyFeedId = [content objectForKey:@"story_feed_id"];
|
2016-11-21 15:31:14 -08:00
|
|
|
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@", storyFeedId];
|
2016-11-18 17:37:34 -08:00
|
|
|
|
|
|
|
|
|
if (!self.activeUsername) {
|
|
|
|
|
return;
|
2016-12-06 16:03:40 -08:00
|
|
|
|
} else if ([action isEqualToString:@"MARK_READ_IDENTIFIER"]) {
|
2016-11-21 15:31:14 -08:00
|
|
|
|
[self markStoryAsRead:storyHash inFeed:feedIdStr withCallback:^{
|
2016-12-06 16:03:40 -08:00
|
|
|
|
if (completionHandler) completionHandler();
|
2016-11-21 15:31:14 -08:00
|
|
|
|
}];
|
2016-12-06 16:03:40 -08:00
|
|
|
|
} else if ([action isEqualToString:@"STAR_IDENTIFIER"]) {
|
2016-11-21 16:33:24 -08:00
|
|
|
|
[self markStoryAsStarred:storyHash withCallback:^{
|
2016-12-06 16:03:40 -08:00
|
|
|
|
if (completionHandler) completionHandler();
|
2016-11-21 15:31:14 -08:00
|
|
|
|
}];
|
2016-12-06 16:03:40 -08:00
|
|
|
|
} else if ([action isEqualToString:@"VIEW_STORY_IDENTIFIER"] ||
|
|
|
|
|
[action isEqualToString:@"com.apple.UNNotificationDefaultActionIdentifier"]) {
|
2020-01-23 15:20:28 -08:00
|
|
|
|
[self popToRootWithCompletion:^{
|
|
|
|
|
[self loadFeed:feedIdStr withStory:storyHash animated:NO];
|
|
|
|
|
}];
|
2016-12-06 16:03:40 -08:00
|
|
|
|
if (completionHandler) completionHandler();
|
|
|
|
|
} else if ([action isEqualToString:@"DISMISS_IDENTIFIER"]) {
|
|
|
|
|
if (completionHandler) completionHandler();
|
2016-11-18 17:37:34 -08:00
|
|
|
|
}
|
2016-11-21 15:31:14 -08:00
|
|
|
|
|
2016-11-17 19:53:46 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
|
|
|
|
|
const char *data = [deviceToken bytes];
|
|
|
|
|
NSMutableString *token = [NSMutableString string];
|
|
|
|
|
|
|
|
|
|
for (NSUInteger i = 0; i < [deviceToken length]; i++) {
|
|
|
|
|
[token appendFormat:@"%02.2hhX", data[i]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSLog(@" -> APNS token: %@", token);
|
2017-04-03 16:07:01 -07:00
|
|
|
|
NSString *url = [NSString stringWithFormat:@"%@/notifications/apns_token/", self.url];
|
|
|
|
|
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
|
|
|
|
[params setObject:token forKey:@"apns_token"];
|
2019-08-22 12:03:39 -07:00
|
|
|
|
[self POST:url parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
2017-04-03 16:07:01 -07:00
|
|
|
|
NSLog(@" -> APNS: %@", responseObject);
|
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
2016-11-17 19:53:46 -08:00
|
|
|
|
NSLog(@"Failed to set APNS token");
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-19 15:04:03 -07:00
|
|
|
|
- (BOOL)application:(UIApplication *)application
|
|
|
|
|
openURL:(NSURL *)url
|
|
|
|
|
sourceApplication:(NSString *)sourceApplication
|
|
|
|
|
annotation:(id)annotation {
|
2019-12-20 19:09:20 -08:00
|
|
|
|
if (self.activeUsername && [url.scheme isEqualToString:@"newsblurwidget"]) {
|
|
|
|
|
NSMutableDictionary *query = [NSMutableDictionary dictionary];
|
|
|
|
|
|
|
|
|
|
for (NSString *component in [url.query componentsSeparatedByString:@"&"]) {
|
|
|
|
|
NSArray *keyAndValue = [component componentsSeparatedByString:@"="];
|
|
|
|
|
|
|
|
|
|
[query setObject:keyAndValue.lastObject forKey:keyAndValue.firstObject];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSString *feedId = query[@"feedId"];
|
|
|
|
|
NSString *storyHash = query[@"storyHash"];
|
|
|
|
|
NSString *error = query[@"error"];
|
|
|
|
|
|
|
|
|
|
if (error.length) {
|
2020-01-23 15:20:28 -08:00
|
|
|
|
[self popToRootWithCompletion:^{
|
|
|
|
|
[self showWidgetSites];
|
|
|
|
|
}];
|
2019-12-20 19:09:20 -08:00
|
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!feedId.length || !storyHash.length) {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-23 15:20:28 -08:00
|
|
|
|
[self popToRootWithCompletion:^{
|
|
|
|
|
self.inFindingStoryMode = YES;
|
|
|
|
|
[storiesCollection reset];
|
|
|
|
|
storiesCollection.isRiverView = YES;
|
|
|
|
|
|
|
|
|
|
self.tryFeedStoryId = storyHash;
|
|
|
|
|
storiesCollection.activeFolder = @"everything";
|
|
|
|
|
|
|
|
|
|
[self loadRiverFeedDetailView:self.feedDetailViewController withFolder:storiesCollection.activeFolder];
|
|
|
|
|
}];
|
2019-12-20 19:09:20 -08:00
|
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-14 17:40:18 -07:00
|
|
|
|
return NO;
|
2013-07-19 15:04:03 -07:00
|
|
|
|
}
|
2013-03-06 16:23:17 -08:00
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
- (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.
|
2014-02-24 18:56:51 -08:00
|
|
|
|
[cachedStoryImages removeAllObjects];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-05-30 18:45:51 -07:00
|
|
|
|
- (void)setupReachability {
|
2016-01-21 22:11:37 -08:00
|
|
|
|
Reachability* reach = [Reachability reachabilityWithHostname:self.host];
|
2013-05-30 18:45:51 -07:00
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
|
|
selector:@selector(reachabilityChanged:)
|
|
|
|
|
name:kReachabilityChangedNotification
|
|
|
|
|
object:nil];
|
2015-09-22 17:15:50 -07:00
|
|
|
|
reach.reachableBlock = ^(Reachability *reach) {
|
|
|
|
|
NSLog(@"Reachable: %@", reach);
|
|
|
|
|
};
|
|
|
|
|
reach.unreachableBlock = ^(Reachability *reach) {
|
|
|
|
|
NSLog(@"Un-Reachable: %@", reach);
|
2018-07-16 11:16:57 -04:00
|
|
|
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
|
|
|
[feedsViewController loadOfflineFeeds:NO];
|
|
|
|
|
});
|
2015-09-22 17:15:50 -07:00
|
|
|
|
};
|
2013-05-30 18:45:51 -07:00
|
|
|
|
[reach startNotifier];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)reachabilityChanged:(id)something {
|
2015-09-22 17:15:50 -07:00
|
|
|
|
NSLog(@"Reachability changed: %@", something);
|
2016-01-21 22:11:37 -08:00
|
|
|
|
// Reachability* reach = [Reachability reachabilityWithHostname:self.host];
|
2014-02-12 21:07:18 -08:00
|
|
|
|
|
2015-09-22 17:15:50 -07:00
|
|
|
|
// if (reach.isReachable && feedsViewController.isOffline) {
|
|
|
|
|
// [feedsViewController loadOfflineFeeds:NO];
|
|
|
|
|
//// } else {
|
|
|
|
|
//// [feedsViewController loadOfflineFeeds:NO];
|
|
|
|
|
// }
|
2013-05-30 18:45:51 -07:00
|
|
|
|
}
|
2013-03-06 16:23:17 -08:00
|
|
|
|
|
2016-01-21 22:11:37 -08:00
|
|
|
|
- (NSString *)url {
|
|
|
|
|
if (!self.cachedURL) {
|
|
|
|
|
NSString *url = [[NSUserDefaults standardUserDefaults] objectForKey:@"custom_domain"];
|
|
|
|
|
|
|
|
|
|
if (url.length) {
|
|
|
|
|
if ([url rangeOfString:@"://"].location == NSNotFound) {
|
|
|
|
|
url = [@"http://" stringByAppendingString:url];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
url = DEFAULT_NEWSBLUR_URL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.cachedURL = url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return self.cachedURL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)host {
|
|
|
|
|
NSString *url = self.url;
|
|
|
|
|
NSString *host = nil;
|
|
|
|
|
NSRange range = [url rangeOfString:@"://"];
|
|
|
|
|
|
|
|
|
|
if (url.length && range.location != NSNotFound) {
|
|
|
|
|
host = [url substringFromIndex:range.location + range.length];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return host;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-29 23:48:47 -07:00
|
|
|
|
#pragma mark -
|
2012-07-01 12:08:30 -07:00
|
|
|
|
#pragma mark Social Views
|
|
|
|
|
|
2013-09-25 17:43:00 -07:00
|
|
|
|
- (NSDictionary *)getUser:(NSInteger)userId {
|
2014-02-12 20:09:37 -08:00
|
|
|
|
for (int i = 0; i < storiesCollection.activeFeedUserProfiles.count; i++) {
|
|
|
|
|
if ([[[storiesCollection.activeFeedUserProfiles objectAtIndex:i] objectForKey:@"user_id"] intValue] == userId) {
|
|
|
|
|
return [storiesCollection.activeFeedUserProfiles objectAtIndex:i];
|
2013-09-05 16:34:39 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check DB if not found in active feed
|
|
|
|
|
__block NSDictionary *user;
|
|
|
|
|
[self.database inDatabase:^(FMDatabase *db) {
|
2013-09-25 17:43:00 -07:00
|
|
|
|
NSString *userSql = [NSString stringWithFormat:@"SELECT * FROM users WHERE user_id = %ld", (long)userId];
|
2013-09-05 16:34:39 -07:00
|
|
|
|
FMResultSet *cursor = [db executeQuery:userSql];
|
|
|
|
|
while ([cursor next]) {
|
|
|
|
|
user = [NSJSONSerialization
|
|
|
|
|
JSONObjectWithData:[[cursor stringForColumn:@"user_json"]
|
|
|
|
|
dataUsingEncoding:NSUTF8StringEncoding]
|
2016-10-05 21:03:32 -07:00
|
|
|
|
options:0 error:nil];
|
2013-09-05 16:34:39 -07:00
|
|
|
|
if (user) break;
|
|
|
|
|
}
|
2013-10-03 18:07:39 -07:00
|
|
|
|
[cursor close];
|
2013-09-05 16:34:39 -07:00
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
return user;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-28 12:56:51 -07:00
|
|
|
|
- (void)showUserProfileModal:(id)sender {
|
2016-01-07 21:12:58 -08:00
|
|
|
|
[self hidePopoverAnimated:NO];
|
2012-08-02 18:00:48 -07:00
|
|
|
|
UserProfileViewController *newUserProfile = [[UserProfileViewController alloc] init];
|
|
|
|
|
self.userProfileViewController = newUserProfile;
|
2012-07-12 22:05:23 -07:00
|
|
|
|
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:self.userProfileViewController];
|
2012-07-28 12:56:51 -07:00
|
|
|
|
self.userProfileNavigationController = navController;
|
2013-09-25 12:05:17 -07:00
|
|
|
|
self.userProfileNavigationController.navigationBar.translucent = NO;
|
2012-07-31 00:53:16 -07:00
|
|
|
|
|
2012-07-28 12:56:51 -07:00
|
|
|
|
|
|
|
|
|
// adding Done button
|
|
|
|
|
UIBarButtonItem *donebutton = [[UIBarButtonItem alloc]
|
2012-07-28 14:56:40 -07:00
|
|
|
|
initWithTitle:@"Close"
|
2012-07-28 12:56:51 -07:00
|
|
|
|
style:UIBarButtonItemStyleDone
|
|
|
|
|
target:self
|
|
|
|
|
action:@selector(hideUserProfileModal)];
|
|
|
|
|
|
2012-08-02 18:00:48 -07:00
|
|
|
|
newUserProfile.navigationItem.rightBarButtonItem = donebutton;
|
|
|
|
|
newUserProfile.navigationItem.title = self.activeUserProfileName;
|
|
|
|
|
newUserProfile.navigationItem.backBarButtonItem.title = self.activeUserProfileName;
|
|
|
|
|
[newUserProfile getUserProfile];
|
2012-07-25 17:29:29 -07:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2012-07-28 12:56:51 -07:00
|
|
|
|
[self.masterContainerViewController showUserProfilePopover:sender];
|
2012-07-25 17:29:29 -07:00
|
|
|
|
} else {
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[self.navigationController presentViewController:navController animated:YES completion:nil];
|
2012-07-25 17:29:29 -07:00
|
|
|
|
}
|
2012-07-31 23:49:51 -07:00
|
|
|
|
|
2012-07-12 22:05:23 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-28 14:15:20 -07:00
|
|
|
|
- (void)pushUserProfile {
|
|
|
|
|
UserProfileViewController *userProfileView = [[UserProfileViewController alloc] init];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// adding Done button
|
|
|
|
|
UIBarButtonItem *donebutton = [[UIBarButtonItem alloc]
|
2012-07-28 14:56:40 -07:00
|
|
|
|
initWithTitle:@"Close"
|
2012-07-28 14:15:20 -07:00
|
|
|
|
style:UIBarButtonItemStyleDone
|
|
|
|
|
target:self
|
|
|
|
|
action:@selector(hideUserProfileModal)];
|
|
|
|
|
|
|
|
|
|
userProfileView.navigationItem.rightBarButtonItem = donebutton;
|
|
|
|
|
userProfileView.navigationItem.title = self.activeUserProfileName;
|
|
|
|
|
userProfileView.navigationItem.backBarButtonItem.title = self.activeUserProfileName;
|
2012-07-31 10:24:38 -07:00
|
|
|
|
[userProfileView getUserProfile];
|
2012-08-13 23:35:11 -07:00
|
|
|
|
if (self.modalNavigationController.view.window == nil) {
|
|
|
|
|
[self.userProfileNavigationController pushViewController:userProfileView animated:YES];
|
|
|
|
|
} else {
|
|
|
|
|
[self.modalNavigationController pushViewController:userProfileView animated:YES];
|
|
|
|
|
};
|
|
|
|
|
|
2012-07-28 14:15:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-12 22:05:23 -07:00
|
|
|
|
- (void)hideUserProfileModal {
|
2012-07-28 12:56:51 -07:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2015-12-15 12:37:18 -08:00
|
|
|
|
[self hidePopover];
|
2012-07-28 12:56:51 -07:00
|
|
|
|
} else {
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
2012-07-28 12:56:51 -07:00
|
|
|
|
}
|
2012-07-12 22:05:23 -07:00
|
|
|
|
}
|
|
|
|
|
|
2019-02-05 21:05:34 -08:00
|
|
|
|
- (void)resizePreviewSize {
|
|
|
|
|
[feedsViewController resizePreviewSize];
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-04 16:34:30 -07:00
|
|
|
|
- (void)resizeFontSize {
|
|
|
|
|
[feedsViewController resizeFontSize];
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-23 15:20:28 -08:00
|
|
|
|
- (void)popToRootWithCompletion:(void (^)(void))completion {
|
2019-12-20 19:09:20 -08:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2020-01-23 15:20:28 -08:00
|
|
|
|
if (completion) {
|
|
|
|
|
[CATransaction begin];
|
|
|
|
|
[CATransaction setCompletionBlock:completion];
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 19:09:20 -08:00
|
|
|
|
[masterContainerViewController dismissViewControllerAnimated:NO completion:nil];
|
2020-01-23 15:20:28 -08:00
|
|
|
|
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:0] animated:YES];
|
|
|
|
|
|
|
|
|
|
if (completion) {
|
|
|
|
|
[CATransaction commit];
|
|
|
|
|
}
|
2019-12-20 19:09:20 -08:00
|
|
|
|
} else {
|
|
|
|
|
[self.navigationController popToRootViewControllerAnimated:NO];
|
2020-01-23 15:20:28 -08:00
|
|
|
|
|
|
|
|
|
if (completion) {
|
|
|
|
|
completion();
|
|
|
|
|
}
|
2019-12-20 19:09:20 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-09 18:43:37 -08:00
|
|
|
|
- (void)showPremiumDialog {
|
|
|
|
|
UINavigationController *navController = self.navigationController;
|
|
|
|
|
if (self.premiumNavigationController == nil) {
|
|
|
|
|
self.premiumNavigationController = [[UINavigationController alloc]
|
|
|
|
|
initWithRootViewController:self.premiumViewController];
|
|
|
|
|
}
|
|
|
|
|
self.premiumNavigationController.navigationBar.translucent = NO;
|
|
|
|
|
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
[masterContainerViewController dismissViewControllerAnimated:NO completion:nil];
|
2017-11-16 16:47:01 -08:00
|
|
|
|
premiumNavigationController.modalPresentationStyle = UIModalPresentationFormSheet;
|
2017-11-09 18:43:37 -08:00
|
|
|
|
[masterContainerViewController presentViewController:premiumNavigationController animated:YES completion:nil];
|
2017-11-16 16:47:01 -08:00
|
|
|
|
[self.premiumViewController.view setNeedsLayout];
|
2017-11-09 18:43:37 -08:00
|
|
|
|
} else {
|
|
|
|
|
[navController presentViewController:self.premiumNavigationController animated:YES completion:nil];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-21 17:48:06 -07:00
|
|
|
|
- (void)showPreferences {
|
|
|
|
|
if (!preferencesViewController) {
|
|
|
|
|
preferencesViewController = [[IASKAppSettingsViewController alloc] init];
|
2015-12-07 16:09:49 -08:00
|
|
|
|
[[ThemeManager themeManager] addThemeGestureRecognizerToView:self.preferencesViewController.view];
|
2013-06-21 17:48:06 -07:00
|
|
|
|
}
|
2015-12-16 11:21:47 -08:00
|
|
|
|
|
|
|
|
|
[self hidePopover];
|
2013-06-21 17:48:06 -07:00
|
|
|
|
|
|
|
|
|
preferencesViewController.delegate = self.feedsViewController;
|
|
|
|
|
preferencesViewController.showDoneButton = YES;
|
|
|
|
|
preferencesViewController.showCreditsFooter = NO;
|
|
|
|
|
preferencesViewController.title = @"Preferences";
|
2019-09-21 14:51:55 -07:00
|
|
|
|
|
|
|
|
|
[self setHiddenPreferencesAnimated:NO];
|
|
|
|
|
|
|
|
|
|
[[NSUserDefaults standardUserDefaults] setObject:@"Delete offline stories..."
|
|
|
|
|
forKey:@"offline_cache_empty_stories"];
|
|
|
|
|
|
|
|
|
|
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:preferencesViewController];
|
|
|
|
|
self.modalNavigationController = navController;
|
|
|
|
|
self.modalNavigationController.navigationBar.translucent = NO;
|
|
|
|
|
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
[masterContainerViewController dismissViewControllerAnimated:NO completion:nil];
|
|
|
|
|
self.modalNavigationController.modalPresentationStyle = UIModalPresentationFormSheet;
|
|
|
|
|
[masterContainerViewController presentViewController:modalNavigationController animated:YES completion:nil];
|
|
|
|
|
} else {
|
|
|
|
|
[navigationController presentViewController:modalNavigationController animated:YES completion:nil];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setHiddenPreferencesAnimated:(BOOL)animated {
|
2013-11-23 13:14:30 -08:00
|
|
|
|
NSMutableSet *hiddenSet = [NSMutableSet set];
|
2019-09-21 14:51:55 -07:00
|
|
|
|
|
2013-11-23 13:14:30 -08:00
|
|
|
|
BOOL offline_enabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"offline_allowed"];
|
2013-12-13 15:15:17 -08:00
|
|
|
|
if (!offline_enabled) {
|
2013-11-23 13:14:30 -08:00
|
|
|
|
[hiddenSet addObjectsFromArray:@[@"offline_image_download",
|
|
|
|
|
@"offline_download_connection",
|
|
|
|
|
@"offline_store_limit"]];
|
|
|
|
|
}
|
|
|
|
|
BOOL system_font_enabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"use_system_font_size"];
|
|
|
|
|
if (system_font_enabled) {
|
|
|
|
|
[hiddenSet addObjectsFromArray:@[@"feed_list_font_size"]];
|
|
|
|
|
}
|
2019-09-21 14:51:55 -07:00
|
|
|
|
if (@available(iOS 13.0, *)) {
|
|
|
|
|
BOOL theme_follow_system = [[NSUserDefaults standardUserDefaults] boolForKey:@"theme_follow_system"];
|
|
|
|
|
if (theme_follow_system) {
|
|
|
|
|
[hiddenSet addObjectsFromArray:@[@"theme_auto_toggle", @"theme_auto_brightness", @"theme_style", @"theme_gesture"]];
|
2019-12-21 13:34:29 -08:00
|
|
|
|
[[ThemeManager themeManager] updateForSystemAppearance];
|
2019-09-21 14:51:55 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-12-07 16:09:49 -08:00
|
|
|
|
BOOL theme_auto_toggle = [[NSUserDefaults standardUserDefaults] boolForKey:@"theme_auto_toggle"];
|
|
|
|
|
if (theme_auto_toggle) {
|
|
|
|
|
[hiddenSet addObjectsFromArray:@[@"theme_style", @"theme_gesture"]];
|
|
|
|
|
} else {
|
|
|
|
|
[hiddenSet addObjectsFromArray:@[@"theme_auto_brightness"]];
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 19:31:08 -07:00
|
|
|
|
BOOL story_full_screen = [[NSUserDefaults standardUserDefaults] boolForKey:@"story_full_screen"];
|
|
|
|
|
if (!story_full_screen) {
|
|
|
|
|
[hiddenSet addObjectsFromArray:@[@"story_hide_status_bar"]];
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-21 14:51:55 -07:00
|
|
|
|
[preferencesViewController setHiddenKeys:hiddenSet animated:animated];
|
2013-06-21 17:48:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-27 20:52:09 -08:00
|
|
|
|
- (void)showFeedChooserForOperation:(FeedChooserOperation)operation {
|
|
|
|
|
[self hidePopover];
|
|
|
|
|
|
|
|
|
|
self.feedChooserViewController = [FeedChooserViewController new];
|
|
|
|
|
self.feedChooserViewController.operation = operation;
|
|
|
|
|
|
|
|
|
|
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:self.feedChooserViewController];
|
|
|
|
|
|
|
|
|
|
self.modalNavigationController = nav;
|
|
|
|
|
self.modalNavigationController.navigationBar.translucent = NO;
|
|
|
|
|
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
self.modalNavigationController.modalPresentationStyle = UIModalPresentationFormSheet;
|
|
|
|
|
[masterContainerViewController presentViewController:modalNavigationController animated:YES completion:nil];
|
|
|
|
|
} else {
|
|
|
|
|
[navigationController presentViewController:modalNavigationController animated:YES completion:nil];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showMuteSites {
|
|
|
|
|
[self showFeedChooserForOperation:FeedChooserOperationMuteSites];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showOrganizeSites {
|
|
|
|
|
[self showFeedChooserForOperation:FeedChooserOperationOrganizeSites];
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 19:09:20 -08:00
|
|
|
|
- (void)showWidgetSites {
|
|
|
|
|
[self showFeedChooserForOperation:FeedChooserOperationWidgetSites];
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-01 12:08:30 -07:00
|
|
|
|
- (void)showFindFriends {
|
2015-12-16 11:21:47 -08:00
|
|
|
|
[self hidePopover];
|
|
|
|
|
|
2012-07-31 15:17:51 -07:00
|
|
|
|
FriendsListViewController *friendsBVC = [[FriendsListViewController alloc] init];
|
2012-07-31 13:10:26 -07:00
|
|
|
|
UINavigationController *friendsNav = [[UINavigationController alloc] initWithRootViewController:friendsListViewController];
|
|
|
|
|
|
2013-06-21 17:48:06 -07:00
|
|
|
|
self.friendsListViewController = friendsBVC;
|
2012-07-31 17:17:02 -07:00
|
|
|
|
self.modalNavigationController = friendsNav;
|
2013-09-25 12:05:17 -07:00
|
|
|
|
self.modalNavigationController.navigationBar.translucent = NO;
|
2012-07-01 12:08:30 -07:00
|
|
|
|
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2014-09-22 12:35:38 -07:00
|
|
|
|
[masterContainerViewController dismissViewControllerAnimated:NO completion:nil];
|
2012-07-31 17:17:02 -07:00
|
|
|
|
self.modalNavigationController.modalPresentationStyle = UIModalPresentationFormSheet;
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[masterContainerViewController presentViewController:modalNavigationController animated:YES completion:nil];
|
2012-07-01 12:08:30 -07:00
|
|
|
|
} else {
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[navigationController presentViewController:modalNavigationController animated:YES completion:nil];
|
2012-07-01 12:08:30 -07:00
|
|
|
|
}
|
2012-07-31 00:53:16 -07:00
|
|
|
|
[self.friendsListViewController loadSuggestedFriendsList];
|
2012-07-01 12:08:30 -07:00
|
|
|
|
}
|
2012-06-29 23:48:47 -07:00
|
|
|
|
|
2014-01-06 18:37:57 -08:00
|
|
|
|
- (void)showSendTo:(UIViewController *)vc sender:(id)sender {
|
2014-01-06 17:54:18 -08:00
|
|
|
|
NSString *authorName = [activeStory objectForKey:@"story_authors"];
|
|
|
|
|
NSString *text = [activeStory objectForKey:@"story_content"];
|
2014-02-18 16:44:41 -08:00
|
|
|
|
NSString *title = [[activeStory objectForKey:@"story_title"] stringByDecodingHTMLEntities];
|
2014-01-06 18:37:57 -08:00
|
|
|
|
NSArray *images = [activeStory objectForKey:@"image_urls"];
|
2014-01-06 17:54:18 -08:00
|
|
|
|
NSURL *url = [NSURL URLWithString:[activeStory objectForKey:@"story_permalink"]];
|
|
|
|
|
NSString *feedId = [NSString stringWithFormat:@"%@", [activeStory objectForKey:@"story_feed_id"]];
|
|
|
|
|
NSDictionary *feed = [self getFeed:feedId];
|
|
|
|
|
NSString *feedTitle = [feed objectForKey:@"feed_title"];
|
|
|
|
|
|
2014-05-29 12:48:25 -07:00
|
|
|
|
if ([activeStory objectForKey:@"original_text"]) {
|
|
|
|
|
text = [activeStory objectForKey:@"original_text"];
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-06 17:54:18 -08:00
|
|
|
|
return [self showSendTo:vc
|
2014-01-06 18:37:57 -08:00
|
|
|
|
sender:sender
|
2014-01-06 17:54:18 -08:00
|
|
|
|
withUrl:url
|
|
|
|
|
authorName:authorName
|
|
|
|
|
text:text
|
|
|
|
|
title:title
|
|
|
|
|
feedTitle:feedTitle
|
|
|
|
|
images:images];
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-06 18:37:57 -08:00
|
|
|
|
- (void)showSendTo:(UIViewController *)vc sender:(id)sender
|
|
|
|
|
withUrl:(NSURL *)url
|
2014-01-06 17:54:18 -08:00
|
|
|
|
authorName:(NSString *)authorName
|
|
|
|
|
text:(NSString *)text
|
|
|
|
|
title:(NSString *)title
|
|
|
|
|
feedTitle:(NSString *)feedTitle
|
|
|
|
|
images:(NSArray *)images {
|
2014-10-22 17:03:48 -07:00
|
|
|
|
|
2015-09-14 17:40:18 -07:00
|
|
|
|
// iOS 8+
|
2015-09-17 17:59:54 -07:00
|
|
|
|
if (text) {
|
|
|
|
|
NSString *maybeFeedTitle = feedTitle ? [NSString stringWithFormat:@" via %@", feedTitle] : @"";
|
|
|
|
|
text = [NSString stringWithFormat:@"<html><body><br><br><hr style=\"border: none; overflow: hidden; height: 1px;width: 100%%;background-color: #C0C0C0;\"><br><a href=\"%@\">%@</a>%@<br>%@</body></html>", [url absoluteString], title, maybeFeedTitle, text];
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-04 09:31:09 -04:00
|
|
|
|
NBActivityItemSource *activityItemSource = [[NBActivityItemSource alloc] initWithUrl:url authorName:authorName text:text title:title feedTitle:feedTitle];
|
|
|
|
|
NSArray *activityItems = @[activityItemSource, url];
|
2015-09-30 12:59:50 -07:00
|
|
|
|
|
2015-09-14 17:40:18 -07:00
|
|
|
|
NSMutableArray *appActivities = [[NSMutableArray alloc] init];
|
|
|
|
|
if (url) [appActivities addObject:[[TUSafariActivity alloc] init]];
|
|
|
|
|
if (url) [appActivities addObject:[[ARChromeActivity alloc]
|
|
|
|
|
initWithCallbackURL:[NSURL URLWithString:@"newsblur://"]]];
|
2015-09-22 22:11:48 -07:00
|
|
|
|
if (url) [appActivities addObject:[[NBCopyLinkActivity alloc] init]];
|
2015-09-14 17:40:18 -07:00
|
|
|
|
|
|
|
|
|
UIActivityViewController *activityViewController = [[UIActivityViewController alloc]
|
2015-09-17 17:59:54 -07:00
|
|
|
|
initWithActivityItems:activityItems
|
2015-09-14 17:40:18 -07:00
|
|
|
|
applicationActivities:appActivities];
|
|
|
|
|
[activityViewController setTitle:title];
|
2015-09-17 17:01:32 -07:00
|
|
|
|
[activityViewController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
2015-09-14 17:40:18 -07:00
|
|
|
|
self.isPresentingActivities = NO;
|
2015-09-17 17:01:32 -07:00
|
|
|
|
|
2015-09-14 17:40:18 -07:00
|
|
|
|
NSString *_completedString;
|
|
|
|
|
NSLog(@"activityType: %@", activityType);
|
|
|
|
|
if (!activityType) return;
|
2014-10-22 17:03:48 -07:00
|
|
|
|
|
2015-09-14 17:40:18 -07:00
|
|
|
|
if ([activityType isEqualToString:UIActivityTypePostToTwitter]) {
|
|
|
|
|
_completedString = @"Posted";
|
|
|
|
|
} else if ([activityType isEqualToString:UIActivityTypePostToFacebook]) {
|
|
|
|
|
_completedString = @"Posted";
|
|
|
|
|
} else if ([activityType isEqualToString:UIActivityTypeMail]) {
|
|
|
|
|
_completedString = @"Sent";
|
|
|
|
|
} else if ([activityType isEqualToString:UIActivityTypeMessage]) {
|
|
|
|
|
_completedString = @"Sent";
|
|
|
|
|
} else if ([activityType isEqualToString:UIActivityTypeCopyToPasteboard]) {
|
|
|
|
|
_completedString = @"Copied";
|
|
|
|
|
} else if ([activityType isEqualToString:UIActivityTypeAirDrop]) {
|
|
|
|
|
_completedString = @"Airdropped";
|
|
|
|
|
} else if ([activityType isEqualToString:@"com.ideashower.ReadItLaterPro.AddToPocketExtension"]) {
|
|
|
|
|
return;
|
|
|
|
|
} else if ([activityType isEqualToString:@"TUSafariActivity"]) {
|
|
|
|
|
return;
|
|
|
|
|
} else if ([activityType isEqualToString:@"ARChromeActivity"]) {
|
|
|
|
|
return;
|
2015-09-22 22:11:48 -07:00
|
|
|
|
} else if ([activityType isEqualToString:@"NBCopyLinkActivity"]) {
|
2015-10-20 11:54:00 -07:00
|
|
|
|
_completedString = @"Copied Link";
|
2014-10-22 17:03:48 -07:00
|
|
|
|
} else {
|
2015-09-14 17:40:18 -07:00
|
|
|
|
_completedString = @"Saved";
|
2014-10-22 17:03:48 -07:00
|
|
|
|
}
|
2015-09-14 17:40:18 -07:00
|
|
|
|
[MBProgressHUD hideHUDForView:vc.view animated:NO];
|
|
|
|
|
if (completed) {
|
2014-10-22 17:03:48 -07:00
|
|
|
|
MBProgressHUD *storyHUD = [MBProgressHUD showHUDAddedTo:vc.view animated:YES];
|
|
|
|
|
storyHUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"37x-Checkmark.png"]];
|
|
|
|
|
storyHUD.mode = MBProgressHUDModeCustomView;
|
|
|
|
|
storyHUD.removeFromSuperViewOnHide = YES;
|
|
|
|
|
storyHUD.labelText = _completedString;
|
|
|
|
|
[storyHUD hide:YES afterDelay:1];
|
|
|
|
|
}
|
2015-09-17 17:01:32 -07:00
|
|
|
|
}];
|
2015-09-14 17:40:18 -07:00
|
|
|
|
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2015-12-15 12:37:18 -08:00
|
|
|
|
BOOL fromPopover = [self hidePopoverAnimated:NO];
|
2015-12-05 14:42:02 -05:00
|
|
|
|
[self.masterContainerViewController presentViewController:activityViewController animated:!fromPopover completion:nil];
|
2015-09-14 17:40:18 -07:00
|
|
|
|
activityViewController.modalPresentationStyle = UIModalPresentationPopover;
|
2014-10-22 17:03:48 -07:00
|
|
|
|
// iOS 8+
|
2015-09-14 17:40:18 -07:00
|
|
|
|
UIPopoverPresentationController *popPC = activityViewController.popoverPresentationController;
|
|
|
|
|
popPC.permittedArrowDirections = UIPopoverArrowDirectionAny;
|
2017-05-10 17:26:18 -07:00
|
|
|
|
popPC.backgroundColor = UIColorFromLightDarkRGB(NEWSBLUR_WHITE_COLOR, 0x707070);
|
2014-12-09 16:15:37 -08:00
|
|
|
|
|
2015-09-14 17:40:18 -07:00
|
|
|
|
if ([sender isKindOfClass:[UIBarButtonItem class]]) {
|
|
|
|
|
popPC.barButtonItem = sender;
|
|
|
|
|
} else if ([sender isKindOfClass:[NSValue class]]) {
|
|
|
|
|
// // Uncomment below to show share popover from linked text. Problem is
|
|
|
|
|
// // that on finger up the link will open.
|
|
|
|
|
CGPoint pt = [(NSValue *)sender CGPointValue];
|
|
|
|
|
CGRect rect = CGRectMake(pt.x, pt.y, 1, 1);
|
|
|
|
|
//// [[OSKPresentationManager sharedInstance] presentActivitySheetForContent:content presentingViewController:vc popoverFromRect:rect inView:self.storyPageControl.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES options:options];
|
2014-10-22 17:03:48 -07:00
|
|
|
|
|
2015-09-14 17:40:18 -07:00
|
|
|
|
// [[OSKPresentationManager sharedInstance] presentActivitySheetForContent:content
|
|
|
|
|
// presentingViewController:vc options:options];
|
|
|
|
|
popPC.sourceRect = rect;
|
|
|
|
|
popPC.sourceView = self.storyPageControl.view;
|
2014-10-22 17:03:48 -07:00
|
|
|
|
} else {
|
2015-09-14 17:40:18 -07:00
|
|
|
|
popPC.sourceRect = [sender frame];
|
|
|
|
|
popPC.sourceView = [sender superview];
|
|
|
|
|
|
|
|
|
|
// [[OSKPresentationManager sharedInstance] presentActivitySheetForContent:content presentingViewController:vc popoverFromRect:[sender frame] inView:[sender superview] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES options:options];
|
2014-01-06 18:37:57 -08:00
|
|
|
|
}
|
2015-09-14 17:40:18 -07:00
|
|
|
|
} else {
|
2015-09-17 17:01:32 -07:00
|
|
|
|
[self.navigationController presentViewController:activityViewController animated:YES completion:^{}];
|
2014-10-30 17:26:34 -07:00
|
|
|
|
}
|
|
|
|
|
self.isPresentingActivities = YES;
|
2014-01-06 17:54:18 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showShareView:(NSString *)type
|
2012-07-20 15:54:10 -07:00
|
|
|
|
setUserId:(NSString *)userId
|
|
|
|
|
setUsername:(NSString *)username
|
2012-07-30 14:58:57 -07:00
|
|
|
|
setReplyId:(NSString *)replyId {
|
2012-07-23 16:17:49 -07:00
|
|
|
|
|
2013-03-02 21:27:29 -08:00
|
|
|
|
[self.shareViewController setCommentType:type];
|
2012-07-12 23:44:14 -07:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2012-07-25 20:38:44 -07:00
|
|
|
|
[self.masterContainerViewController transitionToShareView];
|
2017-05-10 14:57:14 -07:00
|
|
|
|
[self.shareViewController setSiteInfo:type setUserId:userId setUsername:username setReplyId:replyId];
|
2012-07-12 23:44:14 -07:00
|
|
|
|
} else {
|
2012-08-13 18:45:06 -07:00
|
|
|
|
if (self.shareNavigationController == nil) {
|
2012-11-28 15:21:44 -08:00
|
|
|
|
UINavigationController *shareNav = [[UINavigationController alloc]
|
|
|
|
|
initWithRootViewController:self.shareViewController];
|
2012-08-13 18:45:06 -07:00
|
|
|
|
self.shareNavigationController = shareNav;
|
2013-09-25 12:05:17 -07:00
|
|
|
|
self.shareNavigationController.navigationBar.translucent = NO;
|
2012-08-13 18:45:06 -07:00
|
|
|
|
}
|
2017-05-10 14:57:14 -07:00
|
|
|
|
[self.navigationController presentViewController:self.shareNavigationController animated:YES completion:^{
|
|
|
|
|
[self.shareViewController setSiteInfo:type setUserId:userId setUsername:username setReplyId:replyId];
|
|
|
|
|
}];
|
2012-07-12 23:44:14 -07:00
|
|
|
|
}
|
2012-07-12 13:34:41 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)hideShareView:(BOOL)resetComment {
|
|
|
|
|
if (resetComment) {
|
|
|
|
|
self.shareViewController.commentField.text = @"";
|
2012-08-13 23:54:10 -07:00
|
|
|
|
self.shareViewController.currentType = nil;
|
2012-07-12 13:34:41 -07:00
|
|
|
|
}
|
2012-07-26 11:11:43 -07:00
|
|
|
|
|
2012-07-23 16:17:49 -07:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2012-07-25 20:38:44 -07:00
|
|
|
|
[self.masterContainerViewController transitionFromShareView];
|
2015-12-09 11:42:02 -05:00
|
|
|
|
[self.storyPageControl becomeFirstResponder];
|
2016-10-05 21:03:32 -07:00
|
|
|
|
} else if (!self.showingSafariViewController) {
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
2012-07-26 11:11:43 -07:00
|
|
|
|
[self.shareViewController.commentField resignFirstResponder];
|
2012-07-12 23:44:14 -07:00
|
|
|
|
}
|
2012-06-29 23:48:47 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)resetShareComments {
|
|
|
|
|
[shareViewController clearComments];
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-24 00:22:26 -04:00
|
|
|
|
#pragma mark -
|
2013-06-29 17:28:41 -07:00
|
|
|
|
#pragma mark View Management
|
2010-06-24 00:22:26 -04:00
|
|
|
|
|
2010-11-11 23:48:27 -05:00
|
|
|
|
- (void)showLogin {
|
2012-07-01 23:48:43 -07:00
|
|
|
|
self.dictFeeds = nil;
|
|
|
|
|
self.dictSocialFeeds = nil;
|
2014-05-20 12:21:17 -07:00
|
|
|
|
self.dictSavedStoryTags = nil;
|
2016-03-26 21:27:25 -07:00
|
|
|
|
self.dictSavedStoryFeedCounts = nil;
|
2012-07-01 23:48:43 -07:00
|
|
|
|
self.dictFolders = nil;
|
|
|
|
|
self.dictFoldersArray = nil;
|
2016-11-23 09:29:29 -08:00
|
|
|
|
self.notificationFeedIds = nil;
|
2012-08-14 17:20:45 -07:00
|
|
|
|
self.userActivitiesArray = nil;
|
|
|
|
|
self.userInteractionsArray = nil;
|
2013-06-29 17:28:41 -07:00
|
|
|
|
self.dictUnreadCounts = nil;
|
2015-10-05 11:45:33 -07:00
|
|
|
|
self.dictTextFeeds = nil;
|
2012-07-01 23:48:43 -07:00
|
|
|
|
|
|
|
|
|
[self.feedsViewController.feedTitlesTable reloadData];
|
2012-08-14 17:20:45 -07:00
|
|
|
|
[self.feedsViewController resetToolbar];
|
|
|
|
|
|
|
|
|
|
[self.dashboardViewController.interactionsModule.interactionsTable reloadData];
|
|
|
|
|
[self.dashboardViewController.activitiesModule.activitiesTable reloadData];
|
|
|
|
|
|
|
|
|
|
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
[userPreferences setInteger:-1 forKey:@"selectedIntelligence"];
|
|
|
|
|
[userPreferences synchronize];
|
|
|
|
|
|
2012-06-13 12:19:35 -07:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2018-04-06 09:36:08 -04:00
|
|
|
|
if (self.masterContainerViewController.presentedViewController == loginViewController) {
|
|
|
|
|
NSLog(@"Already showing login!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-06-27 21:03:03 -07:00
|
|
|
|
loginViewController.modalPresentationStyle = UIModalPresentationFullScreen;
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[self.masterContainerViewController presentViewController:loginViewController animated:NO completion:nil];
|
2012-06-13 12:19:35 -07:00
|
|
|
|
} else {
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[feedsMenuViewController dismissViewControllerAnimated:NO completion:nil];
|
2015-09-16 18:16:23 -07:00
|
|
|
|
if (navigationController.isViewLoaded && navigationController.view.window) {
|
2016-01-27 21:50:44 -08:00
|
|
|
|
if ([self.navigationController visibleViewController] == loginViewController) {
|
|
|
|
|
NSLog(@"Already showing login!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-09-16 18:16:23 -07:00
|
|
|
|
[self.navigationController presentViewController:loginViewController animated:NO completion:nil];
|
|
|
|
|
}
|
2012-06-13 12:19:35 -07:00
|
|
|
|
}
|
2010-11-11 23:48:27 -05:00
|
|
|
|
}
|
|
|
|
|
|
2012-06-13 18:07:24 -07:00
|
|
|
|
- (void)showFirstTimeUser {
|
2012-08-15 13:04:05 -07:00
|
|
|
|
// [self.feedsViewController changeToAllMode];
|
|
|
|
|
|
2012-07-22 14:23:50 -07:00
|
|
|
|
UINavigationController *ftux = [[UINavigationController alloc] initWithRootViewController:self.firstTimeUserViewController];
|
|
|
|
|
|
|
|
|
|
self.ftuxNavigationController = ftux;
|
2013-09-25 12:05:17 -07:00
|
|
|
|
self.ftuxNavigationController.navigationBar.translucent = NO;
|
2012-07-22 14:23:50 -07:00
|
|
|
|
|
2012-06-13 18:07:24 -07:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2014-09-22 12:35:38 -07:00
|
|
|
|
[masterContainerViewController dismissViewControllerAnimated:NO completion:nil];
|
2019-06-27 21:03:03 -07:00
|
|
|
|
self.ftuxNavigationController.modalPresentationStyle = UIModalPresentationFullScreen;
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[self.masterContainerViewController presentViewController:self.ftuxNavigationController animated:YES completion:nil];
|
2012-08-14 17:20:45 -07:00
|
|
|
|
|
|
|
|
|
self.ftuxNavigationController.view.superview.frame = CGRectMake(0, 0, 540, 540);//it's important to do this after
|
|
|
|
|
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
|
|
|
|
|
if (UIInterfaceOrientationIsPortrait(orientation)) {
|
|
|
|
|
self.ftuxNavigationController.view.superview.center = self.view.center;
|
|
|
|
|
} else {
|
|
|
|
|
self.ftuxNavigationController.view.superview.center = CGPointMake(self.view.center.y, self.view.center.x);
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-13 18:07:24 -07:00
|
|
|
|
} else {
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[self.navigationController presentViewController:self.ftuxNavigationController animated:YES completion:nil];
|
2012-06-13 18:07:24 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-02 16:23:00 -08:00
|
|
|
|
- (void)showMoveSite {
|
|
|
|
|
UINavigationController *navController = self.navigationController;
|
2012-06-08 19:36:23 -07:00
|
|
|
|
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2014-09-22 12:35:38 -07:00
|
|
|
|
[masterContainerViewController dismissViewControllerAnimated:NO completion:nil];
|
2012-06-08 19:36:23 -07:00
|
|
|
|
moveSiteViewController.modalPresentationStyle=UIModalPresentationFormSheet;
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[navController presentViewController:moveSiteViewController animated:YES completion:nil];
|
2012-06-08 19:36:23 -07:00
|
|
|
|
} else {
|
2016-01-21 20:04:43 -08:00
|
|
|
|
[self hidePopover];
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[navController presentViewController:moveSiteViewController animated:YES completion:nil];
|
2012-06-08 19:36:23 -07:00
|
|
|
|
}
|
2011-10-04 18:01:35 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-12-24 23:01:25 -08:00
|
|
|
|
- (void)openTrainSite {
|
2016-01-21 20:04:43 -08:00
|
|
|
|
[self hidePopover];
|
2013-10-02 18:06:31 -07:00
|
|
|
|
// Needs a delay because the menu will close the popover.
|
|
|
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC),
|
|
|
|
|
dispatch_get_main_queue(), ^{
|
|
|
|
|
[self
|
|
|
|
|
openTrainSiteWithFeedLoaded:YES
|
|
|
|
|
from:self.feedDetailViewController.settingsBarButton];
|
|
|
|
|
});
|
2013-10-02 16:05:22 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-10-02 16:28:09 -07:00
|
|
|
|
- (void)openTrainSiteWithFeedLoaded:(BOOL)feedLoaded from:(id)sender {
|
2012-12-24 23:01:25 -08:00
|
|
|
|
UINavigationController *navController = self.navigationController;
|
2012-12-27 00:15:26 -08:00
|
|
|
|
trainerViewController.feedTrainer = YES;
|
|
|
|
|
trainerViewController.storyTrainer = NO;
|
2013-10-02 16:05:22 -07:00
|
|
|
|
trainerViewController.feedLoaded = feedLoaded;
|
2012-12-24 23:01:25 -08:00
|
|
|
|
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2012-12-27 19:34:05 -08:00
|
|
|
|
// trainerViewController.modalPresentationStyle=UIModalPresentationFormSheet;
|
2013-06-17 11:50:13 -07:00
|
|
|
|
// [navController presentViewController:trainerViewController animated:YES completion:nil];
|
2013-10-02 16:28:09 -07:00
|
|
|
|
[self.masterContainerViewController showTrainingPopover:sender];
|
2012-12-27 00:15:26 -08:00
|
|
|
|
} else {
|
2013-03-02 19:15:08 -08:00
|
|
|
|
if (self.trainNavigationController == nil) {
|
|
|
|
|
self.trainNavigationController = [[UINavigationController alloc]
|
|
|
|
|
initWithRootViewController:self.trainerViewController];
|
|
|
|
|
}
|
2013-09-25 12:05:17 -07:00
|
|
|
|
self.trainNavigationController.navigationBar.translucent = NO;
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[navController presentViewController:self.trainNavigationController animated:YES completion:nil];
|
2012-12-27 00:15:26 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-27 23:04:25 -08:00
|
|
|
|
- (void)openTrainStory:(id)sender {
|
2012-12-27 00:15:26 -08:00
|
|
|
|
UINavigationController *navController = self.navigationController;
|
|
|
|
|
trainerViewController.feedTrainer = NO;
|
|
|
|
|
trainerViewController.storyTrainer = YES;
|
2014-05-15 18:49:48 -07:00
|
|
|
|
trainerViewController.feedLoaded = YES;
|
2012-12-27 00:15:26 -08:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2012-12-27 23:04:25 -08:00
|
|
|
|
[self.masterContainerViewController showTrainingPopover:sender];
|
2012-12-24 23:01:25 -08:00
|
|
|
|
} else {
|
2013-03-02 19:15:08 -08:00
|
|
|
|
if (self.trainNavigationController == nil) {
|
|
|
|
|
self.trainNavigationController = [[UINavigationController alloc]
|
|
|
|
|
initWithRootViewController:self.trainerViewController];
|
|
|
|
|
}
|
2013-09-25 12:05:17 -07:00
|
|
|
|
self.trainNavigationController.navigationBar.translucent = NO;
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[navController presentViewController:self.trainNavigationController animated:YES completion:nil];
|
2012-12-24 23:01:25 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-23 10:13:22 -08:00
|
|
|
|
- (void)openNotificationsWithFeed:(NSString *)feedId {
|
2016-11-23 08:09:19 -08:00
|
|
|
|
[self hidePopover];
|
|
|
|
|
// Needs a delay because the menu will close the popover.
|
|
|
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
2016-11-23 10:13:22 -08:00
|
|
|
|
[self openNotificationsWithFeed:feedId sender:self.feedDetailViewController.settingsBarButton];
|
2016-11-23 08:09:19 -08:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-23 10:13:22 -08:00
|
|
|
|
- (void)openNotificationsWithFeed:(NSString *)feedId sender:(id)sender {
|
2016-11-23 08:09:19 -08:00
|
|
|
|
UINavigationController *navController = self.navigationController;
|
|
|
|
|
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2016-11-23 10:13:22 -08:00
|
|
|
|
[self.masterContainerViewController showNotificationsPopoverWithFeed:feedId sender:sender];
|
2016-11-23 08:09:19 -08:00
|
|
|
|
} else {
|
|
|
|
|
if (self.notificationsNavigationController == nil) {
|
|
|
|
|
self.notificationsNavigationController = [[UINavigationController alloc]
|
|
|
|
|
initWithRootViewController:self.notificationsViewController];
|
|
|
|
|
}
|
|
|
|
|
self.notificationsNavigationController.navigationBar.translucent = NO;
|
2016-11-23 10:13:22 -08:00
|
|
|
|
self.notificationsViewController.feedId = feedId;
|
2016-11-23 08:09:19 -08:00
|
|
|
|
[navController presentViewController:self.notificationsNavigationController animated:YES completion:nil];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-05 18:46:39 -07:00
|
|
|
|
- (void)updateNotifications:(NSDictionary *)params feed:(NSString *)feedId {
|
2017-06-01 22:32:35 -07:00
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@/notifications/feed/",
|
|
|
|
|
self.url];
|
2017-04-05 18:46:39 -07:00
|
|
|
|
NSMutableDictionary *feed = [[self.dictFeeds objectForKey:feedId] mutableCopy];
|
|
|
|
|
|
|
|
|
|
[feed setObject:params[@"notification_types"] forKey:@"notification_types"];
|
|
|
|
|
[feed setObject:params[@"notification_filter"] forKey:@"notification_filter"];
|
|
|
|
|
|
|
|
|
|
[self.dictFeeds setObject:feed forKey:feedId];
|
2017-06-01 22:32:35 -07:00
|
|
|
|
|
2019-08-22 12:03:39 -07:00
|
|
|
|
[self POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
2017-06-01 22:32:35 -07:00
|
|
|
|
NSLog(@"Saved notifications %@: %@", feedId, params);
|
|
|
|
|
[self checkForFeedNotifications];
|
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
|
|
|
NSLog(@"Failed to save notifications: %@", params);
|
|
|
|
|
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
|
|
|
|
|
[self.notificationsViewController informError:error statusCode:httpResponse.statusCode];
|
|
|
|
|
}];
|
2017-04-05 18:46:39 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)checkForFeedNotifications {
|
|
|
|
|
NSMutableArray *foundNotificationFeedIds = [NSMutableArray array];
|
|
|
|
|
|
|
|
|
|
for (NSDictionary *feed in self.dictFeeds.allValues) {
|
2020-06-15 21:26:05 -07:00
|
|
|
|
if (![feed isKindOfClass:[NSDictionary class]]) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-05 18:46:39 -07:00
|
|
|
|
NSArray *types = [feed objectForKey:@"notification_types"];
|
|
|
|
|
if (types) {
|
|
|
|
|
for (NSString *notificationType in types) {
|
|
|
|
|
if ([notificationType isEqualToString:@"ios"]) {
|
|
|
|
|
[self registerForRemoteNotifications];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ([types count]) {
|
|
|
|
|
[foundNotificationFeedIds addObject:[feed objectForKey:@"id"]];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.notificationFeedIds = [foundNotificationFeedIds sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
|
|
|
|
|
NSString *feed1Title = [[[self.dictFeeds objectForKey:[NSString stringWithFormat:@"%@", obj1]] objectForKey:@"feed_title"] lowercaseString];
|
|
|
|
|
NSString *feed2Title = [[[self.dictFeeds objectForKey:[NSString stringWithFormat:@"%@", obj2]] objectForKey:@"feed_title"] lowercaseString];
|
|
|
|
|
|
|
|
|
|
return [feed1Title compare:feed2Title];
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-31 15:50:03 -08:00
|
|
|
|
- (void)openStatisticsWithFeed:(NSString *)feedId sender:(id)sender {
|
2020-06-11 21:02:22 -07:00
|
|
|
|
feedId = [self feedIdWithoutSearchQuery:feedId];
|
2020-01-31 15:50:03 -08:00
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@/rss_feeds/statistics_embedded/%@", self.url, feedId];
|
|
|
|
|
NSURL *url = [NSURL URLWithString:urlString];
|
|
|
|
|
NSDictionary *feed = self.dictFeeds[feedId];
|
|
|
|
|
NSString *title = feed[@"feed_title"];
|
|
|
|
|
|
|
|
|
|
[self showInAppBrowser:url withCustomTitle:title fromSender:sender];
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-10 16:00:59 -08:00
|
|
|
|
- (void)openUserTagsStory:(id)sender {
|
2014-11-10 18:25:31 -08:00
|
|
|
|
if (!self.userTagsViewController) {
|
|
|
|
|
self.userTagsViewController = [[UserTagsViewController alloc] init];
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-15 12:37:18 -08:00
|
|
|
|
[self.userTagsViewController view]; // Force viewDidLoad
|
|
|
|
|
CGRect frame = [sender CGRectValue];
|
|
|
|
|
[self showPopoverWithViewController:self.userTagsViewController contentSize:CGSizeMake(220, 382) sourceView:self.storyPageControl.view sourceRect:frame];
|
2014-11-10 16:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
2015-11-13 21:54:32 -08:00
|
|
|
|
#pragma mark - UIPopoverPresentationControllerDelegate
|
|
|
|
|
|
|
|
|
|
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
|
|
|
|
|
return UIModalPresentationNone;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-18 23:01:48 -04:00
|
|
|
|
- (void)popoverPresentationControllerDidDismissPopover:(UIPopoverPresentationController *)popoverPresentationController {
|
|
|
|
|
[self.navigationController.topViewController becomeFirstResponder];
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-19 18:15:36 -07:00
|
|
|
|
#pragma mark - Network
|
|
|
|
|
|
|
|
|
|
- (void)cancelRequests {
|
|
|
|
|
[self clearNetworkManager];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)clearNetworkManager {
|
2019-08-22 12:03:39 -07:00
|
|
|
|
for (NSString *networkOperationIdentifier in self.networkBackgroundTasks) {
|
|
|
|
|
[self endNetworkOperation:networkOperationIdentifier];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.networkBackgroundTasks = [NSMutableDictionary new];
|
|
|
|
|
|
2017-03-19 18:15:36 -07:00
|
|
|
|
[networkManager invalidateSessionCancelingTasks:YES];
|
|
|
|
|
networkManager = [AFHTTPSessionManager manager];
|
|
|
|
|
networkManager.responseSerializer = [AFJSONResponseSerializer serializer];
|
2017-04-04 15:33:08 -07:00
|
|
|
|
[networkManager.requestSerializer setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
|
2017-03-19 18:15:36 -07:00
|
|
|
|
|
|
|
|
|
NSString *currentiPhoneVersion = [[[NSBundle mainBundle] infoDictionary]
|
|
|
|
|
objectForKey:@"CFBundleVersion"];
|
|
|
|
|
NSString *UA;
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
UA = [NSString stringWithFormat:@"NewsBlur iPad App v%@", currentiPhoneVersion];
|
|
|
|
|
} else {
|
|
|
|
|
UA = [NSString stringWithFormat:@"NewsBlur iPhone App v%@", currentiPhoneVersion];
|
|
|
|
|
}
|
|
|
|
|
[networkManager.requestSerializer setValue:UA forHTTPHeaderField:@"User-Agent"];
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-22 12:03:39 -07:00
|
|
|
|
- (NSString *)beginNetworkOperation {
|
|
|
|
|
NSString *networkOperationIdentifier = [NSUUID UUID].UUIDString;
|
|
|
|
|
|
|
|
|
|
UIBackgroundTaskIdentifier backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
|
|
|
|
|
[self endNetworkOperation:networkOperationIdentifier];
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
if (backgroundTaskIdentifier != UIBackgroundTaskInvalid) {
|
|
|
|
|
self.networkBackgroundTasks[networkOperationIdentifier] = @(backgroundTaskIdentifier);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return networkOperationIdentifier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)endNetworkOperation:(NSString *)networkOperationIdentifier {
|
|
|
|
|
UIBackgroundTaskIdentifier identifier = self.networkBackgroundTasks[networkOperationIdentifier].integerValue;
|
|
|
|
|
|
|
|
|
|
if (identifier != UIBackgroundTaskInvalid) {
|
|
|
|
|
[[UIApplication sharedApplication] endBackgroundTask:identifier];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self.networkBackgroundTasks removeObjectForKey:networkOperationIdentifier];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)safelyInvokeTarget:(id _Nonnull)target withSelector:(SEL _Nullable)selector passingObject:(id _Nullable)object {
|
|
|
|
|
if (selector == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IMP imp = [target methodForSelector:selector];
|
|
|
|
|
void (*func)(id, SEL, id _Nullable) = (void *)imp;
|
|
|
|
|
func(target, selector, object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)GET:(NSString *)urlString
|
|
|
|
|
parameters:(id)parameters
|
|
|
|
|
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
|
|
|
|
|
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure {
|
|
|
|
|
NSString *networkOperationIdentifier = [self beginNetworkOperation];
|
|
|
|
|
|
|
|
|
|
[networkManager GET:urlString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
|
|
|
|
if (success) {
|
|
|
|
|
success(task, responseObject);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self endNetworkOperation:networkOperationIdentifier];
|
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
|
|
|
if (failure) {
|
|
|
|
|
failure(task, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self endNetworkOperation:networkOperationIdentifier];
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)GET:(NSString *)urlString
|
|
|
|
|
parameters:(id)parameters
|
|
|
|
|
target:(id)target
|
|
|
|
|
success:(SEL)success
|
|
|
|
|
failure:(SEL)failure {
|
|
|
|
|
[self GET:urlString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
|
|
|
|
[self safelyInvokeTarget:target withSelector:success passingObject:responseObject];
|
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
|
|
|
[self safelyInvokeTarget:target withSelector:failure passingObject:error];
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)POST:(NSString *)urlString
|
|
|
|
|
parameters:(id)parameters
|
|
|
|
|
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
|
|
|
|
|
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure {
|
|
|
|
|
NSString *networkOperationIdentifier = [self beginNetworkOperation];
|
|
|
|
|
|
|
|
|
|
[networkManager POST:urlString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
|
|
|
|
if (success) {
|
|
|
|
|
success(task, responseObject);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self endNetworkOperation:networkOperationIdentifier];
|
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
|
|
|
if (failure) {
|
|
|
|
|
failure(task, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self endNetworkOperation:networkOperationIdentifier];
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)POST:(NSString *)urlString
|
|
|
|
|
parameters:(id)parameters
|
|
|
|
|
target:(id)target
|
|
|
|
|
success:(SEL)success
|
|
|
|
|
failure:(SEL)failure {
|
|
|
|
|
[self POST:urlString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
|
|
|
|
[self safelyInvokeTarget:target withSelector:success passingObject:responseObject];
|
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
|
|
|
[self safelyInvokeTarget:target withSelector:failure passingObject:error];
|
|
|
|
|
}];
|
|
|
|
|
}
|
2017-03-19 18:15:36 -07:00
|
|
|
|
|
2020-02-23 15:21:32 -08:00
|
|
|
|
- (NSHTTPCookie *)sessionIdCookie {
|
|
|
|
|
NSURL *url = [NSURL URLWithString:self.url];
|
|
|
|
|
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL: url];
|
|
|
|
|
|
|
|
|
|
for (NSHTTPCookie *cookie in cookies) {
|
|
|
|
|
if ([cookie.name isEqualToString:@"newsblur_sessionid"]) {
|
|
|
|
|
return cookie;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)prepareWebView:(WKWebView *)webView completionHandler:(void (^)(void))completion {
|
|
|
|
|
NSHTTPCookie *cookie = self.sessionIdCookie;
|
|
|
|
|
|
|
|
|
|
if (cookie != nil) {
|
|
|
|
|
[webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:completion];
|
|
|
|
|
} else {
|
|
|
|
|
completion();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-10 16:00:59 -08:00
|
|
|
|
#pragma mark -
|
|
|
|
|
|
2019-04-24 20:32:04 -07:00
|
|
|
|
- (void)loadFolder:(NSString *)folder feedID:(NSString *)feedIdStr {
|
2020-06-11 21:02:22 -07:00
|
|
|
|
feedIdStr = [self feedIdWithoutSearchQuery:feedIdStr];
|
2019-04-24 20:32:04 -07:00
|
|
|
|
NSDictionary *feed;
|
|
|
|
|
storiesCollection.isReadView = NO;
|
|
|
|
|
if ([self isSocialFeed:feedIdStr]) {
|
|
|
|
|
feed = [dictSocialFeeds objectForKey:feedIdStr];
|
|
|
|
|
storiesCollection.isSocialView = YES;
|
|
|
|
|
storiesCollection.isSavedView = NO;
|
|
|
|
|
} else if ([self isSavedFeed:feedIdStr]) {
|
|
|
|
|
feed = [dictSavedStoryTags objectForKey:feedIdStr];
|
|
|
|
|
storiesCollection.isSocialView = NO;
|
|
|
|
|
storiesCollection.isSavedView = YES;
|
|
|
|
|
storiesCollection.activeSavedStoryTag = [feed objectForKey:@"tag"];
|
|
|
|
|
} else {
|
|
|
|
|
feed = [dictFeeds objectForKey:feedIdStr];
|
|
|
|
|
storiesCollection.isSocialView = NO;
|
|
|
|
|
storiesCollection.isSavedView = NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[storiesCollection setActiveFeed:feed];
|
|
|
|
|
[storiesCollection setActiveFolder:folder];
|
|
|
|
|
readStories = [NSMutableArray array];
|
|
|
|
|
[folderCountCache removeObjectForKey:folder];
|
|
|
|
|
storiesCollection.activeClassifiers = [NSMutableDictionary dictionary];
|
|
|
|
|
|
|
|
|
|
[self loadFeedDetailView];
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-03 18:22:14 -08:00
|
|
|
|
- (void)reloadFeedsView:(BOOL)showLoader {
|
2012-08-09 10:18:15 -07:00
|
|
|
|
[feedsViewController fetchFeedList:showLoader];
|
2010-11-11 20:05:53 -05:00
|
|
|
|
}
|
2011-10-06 10:27:37 -07:00
|
|
|
|
|
2010-06-25 18:36:01 -04:00
|
|
|
|
- (void)loadFeedDetailView {
|
2014-02-10 19:21:53 -08:00
|
|
|
|
[self loadFeedDetailView:YES];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)loadFeedDetailView:(BOOL)transition {
|
2015-03-10 18:58:23 -07:00
|
|
|
|
self.inFeedDetail = YES;
|
2012-06-27 21:28:04 -07:00
|
|
|
|
popoverHasFeedView = YES;
|
2014-02-19 18:59:14 -08:00
|
|
|
|
|
2013-07-19 17:20:39 -07:00
|
|
|
|
[feedDetailViewController resetFeedDetail];
|
2014-02-19 18:59:14 -08:00
|
|
|
|
if (feedDetailViewController == dashboardViewController.storiesModule) {
|
|
|
|
|
feedDetailViewController.storiesCollection = dashboardViewController.storiesModule.storiesCollection;
|
2014-03-12 19:43:20 -07:00
|
|
|
|
} else {
|
2014-02-19 18:59:14 -08:00
|
|
|
|
feedDetailViewController.storiesCollection = storiesCollection;
|
|
|
|
|
}
|
2013-08-06 18:08:55 -07:00
|
|
|
|
|
2014-02-10 19:21:53 -08:00
|
|
|
|
if (transition) {
|
|
|
|
|
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc]
|
|
|
|
|
initWithTitle: @"All"
|
2015-09-16 18:16:23 -07:00
|
|
|
|
style: UIBarButtonItemStylePlain
|
2014-02-10 19:21:53 -08:00
|
|
|
|
target: nil
|
|
|
|
|
action: nil];
|
|
|
|
|
[feedsViewController.navigationItem setBackBarButtonItem:newBackButton];
|
|
|
|
|
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
[self.masterContainerViewController transitionToFeedDetail];
|
|
|
|
|
} else {
|
|
|
|
|
[navigationController pushViewController:feedDetailViewController
|
|
|
|
|
animated:YES];
|
|
|
|
|
}
|
2012-07-20 22:00:30 -07:00
|
|
|
|
}
|
2014-02-12 20:41:29 -08:00
|
|
|
|
|
|
|
|
|
[self flushQueuedReadStories:NO withCallback:^{
|
2018-09-13 10:44:00 -07:00
|
|
|
|
[self flushQueuedSavedStories:NO withCallback:^{
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
[feedDetailViewController fetchFeedDetail:1 withCallback:nil];
|
|
|
|
|
});
|
|
|
|
|
}];
|
2014-02-12 20:41:29 -08:00
|
|
|
|
}];
|
2012-06-19 15:47:51 -07:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-21 15:31:14 -08:00
|
|
|
|
- (void)loadFeed:(NSString *)feedId
|
|
|
|
|
withStory:(NSString *)contentId
|
|
|
|
|
animated:(BOOL)animated {
|
|
|
|
|
NSDictionary *feed = [self getFeed:feedId];
|
2016-12-06 16:03:40 -08:00
|
|
|
|
NSLog(@"loadFeed: %@", feed);
|
|
|
|
|
|
|
|
|
|
if (!feed || [feed isKindOfClass:[NSNull class]]) {
|
|
|
|
|
if (self.tryFeedFeedId) {
|
|
|
|
|
self.tryFeedStoryId = nil;
|
|
|
|
|
self.tryFeedFeedId = nil;
|
|
|
|
|
} else {
|
|
|
|
|
self.tryFeedFeedId = feedId;
|
|
|
|
|
self.tryFeedStoryId = contentId;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2016-11-21 15:31:14 -08:00
|
|
|
|
|
|
|
|
|
self.isTryFeedView = YES;
|
|
|
|
|
self.inFindingStoryMode = YES;
|
|
|
|
|
self.tryFeedStoryId = contentId;
|
2016-12-06 16:03:40 -08:00
|
|
|
|
self.tryFeedFeedId = nil;
|
2016-11-21 15:31:14 -08:00
|
|
|
|
storiesCollection.isSocialView = NO;
|
|
|
|
|
storiesCollection.activeFeed = feed;
|
|
|
|
|
storiesCollection.activeFolder = nil;
|
|
|
|
|
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
[self loadFeedDetailView];
|
|
|
|
|
} else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
|
|
|
|
|
[self.navigationController popToRootViewControllerAnimated:NO];
|
|
|
|
|
[self hidePopoverAnimated:NO completion:^{
|
|
|
|
|
if (self.navigationController.presentedViewController) {
|
|
|
|
|
[self.navigationController dismissViewControllerAnimated:NO completion:^{
|
|
|
|
|
[self loadFeedDetailView];
|
|
|
|
|
}];
|
|
|
|
|
} else {
|
|
|
|
|
[self loadFeedDetailView];
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-09 14:13:44 -08:00
|
|
|
|
- (void)loadTryFeedDetailView:(NSString *)feedId
|
|
|
|
|
withStory:(NSString *)contentId
|
|
|
|
|
isSocial:(BOOL)social
|
|
|
|
|
withUser:(NSDictionary *)user
|
|
|
|
|
showFindingStory:(BOOL)showHUD {
|
2013-10-17 17:23:52 -07:00
|
|
|
|
NSDictionary *feed = [self getFeed:feedId];
|
2012-07-16 22:35:28 -07:00
|
|
|
|
|
|
|
|
|
if (social) {
|
2014-02-12 20:09:37 -08:00
|
|
|
|
storiesCollection.isSocialView = YES;
|
2012-08-01 12:41:02 -07:00
|
|
|
|
self.inFindingStoryMode = YES;
|
2012-07-29 20:55:11 -07:00
|
|
|
|
|
2012-07-28 23:31:12 -07:00
|
|
|
|
if (feed == nil) {
|
|
|
|
|
feed = user;
|
2012-08-06 19:21:39 -07:00
|
|
|
|
self.isTryFeedView = YES;
|
2012-07-28 23:31:12 -07:00
|
|
|
|
}
|
2012-07-16 22:35:28 -07:00
|
|
|
|
} else {
|
2012-07-28 23:31:12 -07:00
|
|
|
|
if (feed == nil) {
|
2012-07-29 20:55:11 -07:00
|
|
|
|
feed = user;
|
2012-08-06 19:21:39 -07:00
|
|
|
|
self.isTryFeedView = YES;
|
2012-07-29 20:55:11 -07:00
|
|
|
|
|
2012-07-28 23:31:12 -07:00
|
|
|
|
}
|
2014-02-12 20:09:37 -08:00
|
|
|
|
storiesCollection.isSocialView = NO;
|
2016-11-21 15:31:14 -08:00
|
|
|
|
// [self setInFindingStoryMode:NO];
|
2012-07-16 22:35:28 -07:00
|
|
|
|
}
|
2012-07-29 20:55:11 -07:00
|
|
|
|
|
2012-11-13 12:28:16 -08:00
|
|
|
|
self.tryFeedStoryId = contentId;
|
2014-02-12 20:09:37 -08:00
|
|
|
|
storiesCollection.activeFeed = feed;
|
|
|
|
|
storiesCollection.activeFolder = nil;
|
2012-07-16 19:45:14 -07:00
|
|
|
|
|
2014-12-15 15:24:47 -08:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
[self loadFeedDetailView];
|
|
|
|
|
} else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
|
|
|
|
|
[self.navigationController popToRootViewControllerAnimated:NO];
|
2016-01-07 21:12:58 -08:00
|
|
|
|
[self hidePopoverAnimated:YES completion:^{
|
|
|
|
|
if (self.navigationController.presentedViewController) {
|
|
|
|
|
[self.navigationController dismissViewControllerAnimated:YES completion:^{
|
|
|
|
|
[self loadFeedDetailView];
|
|
|
|
|
}];
|
|
|
|
|
} else {
|
2014-12-15 15:24:47 -08:00
|
|
|
|
[self loadFeedDetailView];
|
2016-01-07 21:12:58 -08:00
|
|
|
|
}
|
|
|
|
|
}];
|
2013-09-11 17:05:47 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)loadStarredDetailViewWithStory:(NSString *)contentId
|
|
|
|
|
showFindingStory:(BOOL)showHUD {
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
|
|
|
|
|
[self.navigationController popToRootViewControllerAnimated:NO];
|
|
|
|
|
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
2015-12-15 12:37:18 -08:00
|
|
|
|
[self hidePopoverAnimated:NO];
|
2013-09-11 17:05:47 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.inFindingStoryMode = YES;
|
2014-02-21 12:24:53 -08:00
|
|
|
|
[storiesCollection reset];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
storiesCollection.isRiverView = YES;
|
2013-09-11 17:05:47 -07:00
|
|
|
|
|
|
|
|
|
self.tryFeedStoryId = contentId;
|
2014-02-12 20:09:37 -08:00
|
|
|
|
storiesCollection.activeFolder = @"saved_stories";
|
2013-09-11 17:05:47 -07:00
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
[self loadRiverFeedDetailView:feedDetailViewController withFolder:@"saved_stories"];
|
2013-09-11 17:05:47 -07:00
|
|
|
|
|
|
|
|
|
if (showHUD) {
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
[self.storyPageControl showShareHUD:@"Finding story..."];
|
|
|
|
|
} else {
|
|
|
|
|
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.feedDetailViewController.view animated:YES];
|
|
|
|
|
HUD.labelText = @"Finding story...";
|
|
|
|
|
}
|
2012-11-09 14:13:44 -08:00
|
|
|
|
}
|
2012-07-16 19:45:14 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-06-26 12:12:31 -07:00
|
|
|
|
- (BOOL)isSocialFeed:(NSString *)feedIdStr {
|
|
|
|
|
if ([feedIdStr length] > 6) {
|
|
|
|
|
NSString *feedIdSubStr = [feedIdStr substringToIndex:6];
|
|
|
|
|
if ([feedIdSubStr isEqualToString:@"social"]) {
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
2012-06-26 12:29:37 -07:00
|
|
|
|
|
2020-06-11 21:02:22 -07:00
|
|
|
|
- (BOOL)isSavedSearch:(NSString *)feedIdStr {
|
|
|
|
|
return [feedIdStr containsString:@"?"];
|
|
|
|
|
}
|
2020-05-27 21:26:44 -07:00
|
|
|
|
|
2014-05-20 12:21:17 -07:00
|
|
|
|
- (BOOL)isSavedFeed:(NSString *)feedIdStr {
|
|
|
|
|
return [feedIdStr startsWith:@"saved:"];
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-26 21:27:25 -07:00
|
|
|
|
- (NSInteger)savedStoriesCountForFeed:(NSString *)feedIdStr {
|
|
|
|
|
return [self.dictSavedStoryFeedCounts[feedIdStr] integerValue];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)isSavedStoriesIntelligenceMode {
|
|
|
|
|
return self.selectedIntelligence == 2;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-18 13:43:04 -08:00
|
|
|
|
- (NSArray *)allFeedIds {
|
|
|
|
|
NSMutableArray *mutableFeedIds = [NSMutableArray array];
|
|
|
|
|
|
|
|
|
|
for (NSString *folderName in self.dictFoldersArray) {
|
|
|
|
|
for (id feedId in self.dictFolders[folderName]) {
|
|
|
|
|
if (![feedId isKindOfClass:[NSString class]] || ![self isSavedFeed:feedId]) {
|
|
|
|
|
[mutableFeedIds addObject:feedId];
|
2015-11-13 21:54:32 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-11-18 13:43:04 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mutableFeedIds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSArray *)feedIdsForFolderTitle:(NSString *)folderTitle {
|
2017-11-05 22:07:43 -08:00
|
|
|
|
if ([folderTitle isEqualToString:@"everything"] || [folderTitle isEqualToString:@"infrequent"]) {
|
2015-11-18 13:43:04 -08:00
|
|
|
|
return @[folderTitle];
|
2015-11-13 21:54:32 -08:00
|
|
|
|
} else {
|
|
|
|
|
return self.dictFolders[folderTitle];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-02 13:10:00 -07:00
|
|
|
|
- (BOOL)isPortrait {
|
|
|
|
|
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
|
|
|
|
|
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
|
|
|
|
|
return YES;
|
|
|
|
|
} else {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-28 22:07:38 -08:00
|
|
|
|
- (BOOL)isCompactWidth {
|
|
|
|
|
return self.compactWidth > 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-02 11:28:55 -07:00
|
|
|
|
- (void)confirmLogout {
|
2017-01-05 11:18:44 -08:00
|
|
|
|
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Positive?" message:nil preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
|
[alertController addAction:[UIAlertAction actionWithTitle: @"Logout" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
|
|
|
|
|
[alertController dismissViewControllerAnimated:YES completion:nil];
|
|
|
|
|
NSLog(@"Logging out...");
|
2017-03-19 18:15:36 -07:00
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@/reader/logout?api=1",
|
2017-01-05 11:18:44 -08:00
|
|
|
|
self.url];
|
2019-08-22 12:03:39 -07:00
|
|
|
|
[self GET:urlString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
2017-01-05 11:18:44 -08:00
|
|
|
|
[MBProgressHUD hideHUDForView:self.view animated:YES];
|
|
|
|
|
[self showLogin];
|
2017-03-19 18:15:36 -07:00
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
|
|
|
[MBProgressHUD hideHUDForView:self.view animated:YES];
|
2017-01-05 11:18:44 -08:00
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
[MBProgressHUD hideHUDForView:self.view animated:YES];
|
|
|
|
|
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
|
|
|
|
|
HUD.labelText = @"Logging out...";
|
|
|
|
|
}]];
|
|
|
|
|
[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel"
|
|
|
|
|
style:UIAlertActionStyleCancel handler:nil]];
|
|
|
|
|
[self.feedsViewController presentViewController:alertController animated:YES completion:nil];
|
2012-07-02 11:28:55 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-03-04 20:21:29 -08:00
|
|
|
|
- (void)showConnectToService:(NSString *)serviceName {
|
2013-03-05 08:49:37 -08:00
|
|
|
|
AuthorizeServicesViewController *serviceVC = [[AuthorizeServicesViewController alloc] init];
|
|
|
|
|
serviceVC.url = [NSString stringWithFormat:@"/oauth/%@_connect", serviceName];
|
|
|
|
|
serviceVC.type = serviceName;
|
|
|
|
|
serviceVC.fromStory = YES;
|
2013-03-04 20:21:29 -08:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2013-03-05 08:49:37 -08:00
|
|
|
|
UINavigationController *connectNav = [[UINavigationController alloc]
|
|
|
|
|
initWithRootViewController:serviceVC];
|
|
|
|
|
self.modalNavigationController = connectNav;
|
2014-09-22 12:35:38 -07:00
|
|
|
|
[masterContainerViewController dismissViewControllerAnimated:NO completion:nil];
|
2013-03-05 08:49:37 -08:00
|
|
|
|
self.modalNavigationController.modalPresentationStyle = UIModalPresentationFormSheet;
|
2013-09-25 12:05:17 -07:00
|
|
|
|
self.modalNavigationController.navigationBar.translucent = NO;
|
2013-06-17 11:50:13 -07:00
|
|
|
|
[self.masterContainerViewController presentViewController:modalNavigationController
|
|
|
|
|
animated:YES completion:nil];
|
2013-03-04 20:21:29 -08:00
|
|
|
|
} else {
|
2013-03-05 08:49:37 -08:00
|
|
|
|
[self.shareNavigationController pushViewController:serviceVC animated:YES];
|
2013-03-04 20:21:29 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-11 15:43:42 -07:00
|
|
|
|
- (void)showAlert:(UIAlertController *)alert withViewController:(UIViewController *)vc {
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
[self.masterContainerViewController presentViewController:alert animated:YES completion:nil];
|
|
|
|
|
} else {
|
|
|
|
|
[vc presentViewController:alert animated:YES completion:nil];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-26 10:48:02 -07:00
|
|
|
|
- (void)refreshUserProfile:(void(^)(void))callback {
|
2017-03-19 18:15:36 -07:00
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@/social/load_user_profile",
|
|
|
|
|
self.url];
|
2019-08-22 12:03:39 -07:00
|
|
|
|
[self GET:urlString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
2017-03-19 18:15:36 -07:00
|
|
|
|
self.dictUserProfile = [responseObject objectForKey:@"user_profile"];
|
|
|
|
|
self.dictSocialServices = [responseObject objectForKey:@"services"];
|
2013-03-04 20:21:29 -08:00
|
|
|
|
callback();
|
2017-03-19 18:15:36 -07:00
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
|
|
|
NSLog(@"Failed user profile");
|
2013-03-04 20:21:29 -08:00
|
|
|
|
callback();
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-21 16:23:51 -07:00
|
|
|
|
- (void)refreshFeedCount:(id)feedId {
|
2017-12-15 18:10:04 -08:00
|
|
|
|
// [feedsViewController fadeFeed:feedId];
|
|
|
|
|
[feedsViewController redrawFeedCounts:feedId];
|
|
|
|
|
[feedsViewController refreshHeaderCounts];
|
2014-03-21 16:23:51 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
- (void)loadRiverFeedDetailView:(FeedDetailViewController *)feedDetailView withFolder:(NSString *)folder {
|
2014-02-10 19:21:53 -08:00
|
|
|
|
self.readStories = [NSMutableArray array];
|
|
|
|
|
NSMutableArray *feeds = [NSMutableArray array];
|
2014-02-18 18:38:07 -08:00
|
|
|
|
BOOL transferFromDashboard = [folder isEqualToString:@"river_dashboard"];
|
2014-02-10 19:21:53 -08:00
|
|
|
|
|
2014-02-18 18:38:07 -08:00
|
|
|
|
self.inFeedDetail = YES;
|
2014-02-19 18:59:14 -08:00
|
|
|
|
[feedDetailView resetFeedDetail];
|
|
|
|
|
if (feedDetailView == dashboardViewController.storiesModule) {
|
|
|
|
|
feedDetailView.storiesCollection = dashboardViewController.storiesModule.storiesCollection;
|
|
|
|
|
} else if (feedDetailView == feedDetailViewController) {
|
|
|
|
|
feedDetailView.storiesCollection = storiesCollection;
|
|
|
|
|
}
|
2014-02-18 16:44:41 -08:00
|
|
|
|
|
2014-02-21 12:24:53 -08:00
|
|
|
|
[feedDetailView.storiesCollection reset];
|
|
|
|
|
|
2014-02-18 18:38:07 -08:00
|
|
|
|
if (transferFromDashboard) {
|
|
|
|
|
StoriesCollection *dashboardCollection = dashboardViewController.storiesModule.storiesCollection;
|
2014-02-21 12:24:53 -08:00
|
|
|
|
[feedDetailView.storiesCollection transferStoriesFromCollection:dashboardCollection];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
feedDetailView.storiesCollection.isRiverView = YES;
|
2014-02-21 12:24:53 -08:00
|
|
|
|
feedDetailView.storiesCollection.transferredFromDashboard = YES;
|
2014-02-18 18:38:07 -08:00
|
|
|
|
[feedDetailView.storiesCollection setActiveFolder:@"everything"];
|
|
|
|
|
} else {
|
|
|
|
|
if ([folder isEqualToString:@"river_global"]) {
|
|
|
|
|
feedDetailView.storiesCollection.isSocialRiverView = YES;
|
|
|
|
|
feedDetailView.storiesCollection.isRiverView = YES;
|
|
|
|
|
[feedDetailView.storiesCollection setActiveFolder:@"river_global"];
|
|
|
|
|
} else if ([folder isEqualToString:@"river_blurblogs"]) {
|
|
|
|
|
feedDetailView.storiesCollection.isSocialRiverView = YES;
|
|
|
|
|
feedDetailView.storiesCollection.isRiverView = YES;
|
|
|
|
|
// add all the feeds from every NON blurblog folder
|
|
|
|
|
[feedDetailView.storiesCollection setActiveFolder:@"river_blurblogs"];
|
|
|
|
|
for (NSString *folderName in self.feedsViewController.activeFeedLocations) {
|
|
|
|
|
if ([folderName isEqualToString:@"river_blurblogs"]) { // remove all blurblugs which is a blank folder name
|
|
|
|
|
NSArray *originalFolder = [self.dictFolders objectForKey:folderName];
|
|
|
|
|
NSArray *folderFeeds = [self.feedsViewController.activeFeedLocations objectForKey:folderName];
|
|
|
|
|
for (int l=0; l < [folderFeeds count]; l++) {
|
|
|
|
|
[feeds addObject:[originalFolder objectAtIndex:[[folderFeeds objectAtIndex:l] intValue]]];
|
|
|
|
|
}
|
2014-02-10 19:21:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-11-05 22:07:43 -08:00
|
|
|
|
} else if ([folder isEqualToString:@"everything"] || [folder isEqualToString:@"infrequent"]) {
|
2014-02-18 18:38:07 -08:00
|
|
|
|
feedDetailView.storiesCollection.isRiverView = YES;
|
|
|
|
|
// add all the feeds from every NON blurblog folder
|
2017-11-05 22:07:43 -08:00
|
|
|
|
[feedDetailView.storiesCollection setActiveFolder:folder];
|
2014-02-18 18:38:07 -08:00
|
|
|
|
for (NSString *folderName in self.feedsViewController.activeFeedLocations) {
|
2014-05-20 12:21:17 -07:00
|
|
|
|
if ([folderName isEqualToString:@"river_blurblogs"]) continue;
|
2014-10-22 16:39:37 -07:00
|
|
|
|
if ([folderName isEqualToString:@"read_stories"]) continue;
|
2020-05-27 21:26:44 -07:00
|
|
|
|
if ([folderName isEqualToString:@"saved_searches"]) continue;
|
2014-05-20 12:21:17 -07:00
|
|
|
|
if ([folderName isEqualToString:@"saved_stories"]) continue;
|
|
|
|
|
NSArray *originalFolder = [self.dictFolders objectForKey:folderName];
|
|
|
|
|
NSArray *folderFeeds = [self.feedsViewController.activeFeedLocations objectForKey:folderName];
|
|
|
|
|
for (int l=0; l < [folderFeeds count]; l++) {
|
|
|
|
|
[feeds addObject:[originalFolder objectAtIndex:[[folderFeeds objectAtIndex:l] intValue]]];
|
2014-02-10 19:21:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-02-18 18:38:07 -08:00
|
|
|
|
[self.folderCountCache removeAllObjects];
|
|
|
|
|
} else {
|
|
|
|
|
feedDetailView.storiesCollection.isRiverView = YES;
|
|
|
|
|
NSString *folderName = [self.dictFoldersArray objectAtIndex:[folder intValue]];
|
|
|
|
|
|
2014-11-04 14:57:19 -08:00
|
|
|
|
if ([folder isEqualToString:@"saved_stories"] || [folderName isEqualToString:@"saved_stories"]) {
|
2014-05-20 15:43:45 -07:00
|
|
|
|
feedDetailView.storiesCollection.isSavedView = YES;
|
2014-11-04 14:57:19 -08:00
|
|
|
|
[feedDetailView.storiesCollection setActiveFolder:@"saved_stories"];
|
2020-05-27 21:26:44 -07:00
|
|
|
|
} else if ([folder isEqualToString:@"saved_searches"] || [folderName isEqualToString:@"saved_searches"]) {
|
|
|
|
|
feedDetailView.storiesCollection.isSavedView = YES;
|
|
|
|
|
[feedDetailView.storiesCollection setActiveFolder:@"saved_searches"];
|
2014-11-04 14:57:19 -08:00
|
|
|
|
} else if ([folder isEqualToString:@"read_stories"] || [folderName isEqualToString:@"read_stories"]) {
|
2014-10-22 17:03:48 -07:00
|
|
|
|
feedDetailView.storiesCollection.isReadView = YES;
|
2014-11-04 14:57:19 -08:00
|
|
|
|
[feedDetailView.storiesCollection setActiveFolder:@"read_stories"];
|
2014-11-04 17:13:53 -08:00
|
|
|
|
} else {
|
|
|
|
|
[feedDetailView.storiesCollection setActiveFolder:folderName];
|
2014-05-20 15:43:45 -07:00
|
|
|
|
}
|
2014-02-18 18:38:07 -08:00
|
|
|
|
NSArray *originalFolder = [self.dictFolders objectForKey:folderName];
|
|
|
|
|
NSArray *activeFeedLocations = [self.feedsViewController.activeFeedLocations objectForKey:folderName];
|
|
|
|
|
for (int l=0; l < [activeFeedLocations count]; l++) {
|
|
|
|
|
[feeds addObject:[originalFolder objectAtIndex:[[activeFeedLocations objectAtIndex:l] intValue]]];
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-10 19:21:53 -08:00
|
|
|
|
}
|
2014-02-18 18:38:07 -08:00
|
|
|
|
feedDetailView.storiesCollection.activeFolderFeeds = feeds;
|
2014-03-04 18:09:43 -08:00
|
|
|
|
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
if (!self.feedsViewController.viewShowingAllFeeds &&
|
|
|
|
|
[preferences boolForKey:@"show_feeds_after_being_read"]) {
|
2014-02-18 18:38:07 -08:00
|
|
|
|
for (id feedId in feeds) {
|
|
|
|
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@", feedId];
|
|
|
|
|
[self.feedsViewController.stillVisibleFeeds setObject:[NSNumber numberWithBool:YES] forKey:feedIdStr];
|
|
|
|
|
}
|
2014-02-10 19:21:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-02-18 18:38:07 -08:00
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
if (feedDetailView.storiesCollection.activeFolder) {
|
|
|
|
|
[self.folderCountCache removeObjectForKey:feedDetailView.storiesCollection.activeFolder];
|
|
|
|
|
}
|
2013-08-06 18:08:55 -07:00
|
|
|
|
|
2019-03-22 20:55:22 -07:00
|
|
|
|
if (feedDetailView == feedDetailViewController && feedDetailView.navigationController == nil) {
|
2014-03-03 12:12:06 -08:00
|
|
|
|
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle: @"All"
|
2015-09-18 15:02:15 -07:00
|
|
|
|
style: UIBarButtonItemStylePlain
|
2014-03-03 12:12:06 -08:00
|
|
|
|
target: nil
|
|
|
|
|
action: nil];
|
|
|
|
|
[feedsViewController.navigationItem setBackBarButtonItem: newBackButton];
|
2014-02-10 19:21:53 -08:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
[self.masterContainerViewController transitionToFeedDetail];
|
|
|
|
|
} else {
|
|
|
|
|
UINavigationController *navController = self.navigationController;
|
|
|
|
|
[navController pushViewController:feedDetailViewController animated:YES];
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-02-27 17:51:56 -08:00
|
|
|
|
|
|
|
|
|
if (!transferFromDashboard) {
|
|
|
|
|
[self flushQueuedReadStories:NO withCallback:^{
|
2018-09-13 10:44:00 -07:00
|
|
|
|
[self flushQueuedSavedStories:NO withCallback:^{
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
[feedDetailView fetchRiver];
|
|
|
|
|
});
|
|
|
|
|
}];
|
2014-02-27 17:51:56 -08:00
|
|
|
|
}];
|
|
|
|
|
} else {
|
|
|
|
|
[feedDetailView reloadData];
|
|
|
|
|
}
|
2014-02-10 19:21:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-18 18:38:07 -08:00
|
|
|
|
- (void)openDashboardRiverForStory:(NSString *)contentId
|
|
|
|
|
showFindingStory:(BOOL)showHUD {
|
2014-02-10 19:21:53 -08:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
|
|
|
|
|
[self.navigationController popToRootViewControllerAnimated:NO];
|
|
|
|
|
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
2015-12-15 12:37:18 -08:00
|
|
|
|
[self hidePopoverAnimated:NO];
|
2014-02-10 19:21:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.inFindingStoryMode = YES;
|
2014-02-21 12:24:53 -08:00
|
|
|
|
[storiesCollection reset];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
storiesCollection.isRiverView = YES;
|
2014-02-10 19:21:53 -08:00
|
|
|
|
|
|
|
|
|
self.tryFeedStoryId = contentId;
|
2014-02-12 20:09:37 -08:00
|
|
|
|
storiesCollection.activeFolder = @"everything";
|
2014-02-10 19:21:53 -08:00
|
|
|
|
|
2014-02-18 18:38:07 -08:00
|
|
|
|
[self loadRiverFeedDetailView:feedDetailViewController withFolder:@"river_dashboard"];
|
2014-02-10 19:21:53 -08:00
|
|
|
|
|
|
|
|
|
if (showHUD) {
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
[self.storyPageControl showShareHUD:@"Finding story..."];
|
|
|
|
|
} else {
|
|
|
|
|
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.feedDetailViewController.view animated:YES];
|
|
|
|
|
HUD.labelText = @"Finding story...";
|
|
|
|
|
}
|
2012-07-24 16:03:16 -07:00
|
|
|
|
}
|
2012-06-20 07:39:49 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-06-29 21:15:43 -07:00
|
|
|
|
- (void)adjustStoryDetailWebView {
|
2020-02-23 15:21:32 -08:00
|
|
|
|
// change the web view
|
2012-11-07 17:54:16 -08:00
|
|
|
|
[storyPageControl.currentPage changeWebViewWidth];
|
|
|
|
|
[storyPageControl.nextPage changeWebViewWidth];
|
2012-11-14 17:31:52 -08:00
|
|
|
|
[storyPageControl.previousPage changeWebViewWidth];
|
2012-07-23 15:27:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)calibrateStoryTitles {
|
|
|
|
|
[self.feedDetailViewController checkScroll];
|
|
|
|
|
[self.feedDetailViewController changeActiveFeedDetailRow];
|
|
|
|
|
|
|
|
|
|
}
|
2012-07-12 13:34:41 -07:00
|
|
|
|
|
2012-12-12 15:08:24 -08:00
|
|
|
|
- (void)recalculateIntelligenceScores:(id)feedId {
|
2012-12-13 12:12:00 -08:00
|
|
|
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@", feedId];
|
|
|
|
|
NSMutableArray *newFeedStories = [NSMutableArray array];
|
2012-12-12 15:08:24 -08:00
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
for (NSDictionary *story in storiesCollection.activeFeedStories) {
|
2012-12-13 12:12:00 -08:00
|
|
|
|
NSString *storyFeedId = [NSString stringWithFormat:@"%@",
|
|
|
|
|
[story objectForKey:@"story_feed_id"]];
|
|
|
|
|
if (![storyFeedId isEqualToString:feedIdStr]) {
|
|
|
|
|
[newFeedStories addObject:story];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSMutableDictionary *newStory = [story mutableCopy];
|
|
|
|
|
|
2016-03-26 21:27:25 -07:00
|
|
|
|
// If the story is visible, mark it as sticky so it doesn't go away on page loads.
|
2013-09-25 17:43:00 -07:00
|
|
|
|
NSInteger score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]];
|
2012-12-13 12:12:00 -08:00
|
|
|
|
if (score >= self.selectedIntelligence) {
|
|
|
|
|
[newStory setObject:[NSNumber numberWithBool:YES] forKey:@"sticky"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSNumber *zero = [NSNumber numberWithInt:0];
|
|
|
|
|
NSMutableDictionary *intelligence = [NSMutableDictionary
|
|
|
|
|
dictionaryWithObjects:[NSArray arrayWithObjects:
|
|
|
|
|
[zero copy], [zero copy],
|
|
|
|
|
[zero copy], [zero copy], nil]
|
|
|
|
|
forKeys:[NSArray arrayWithObjects:
|
|
|
|
|
@"author", @"feed", @"tags", @"title", nil]];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
NSDictionary *classifiers = [storiesCollection.activeClassifiers objectForKey:feedIdStr];
|
2012-12-13 12:12:00 -08:00
|
|
|
|
|
|
|
|
|
for (NSString *title in [classifiers objectForKey:@"titles"]) {
|
|
|
|
|
if ([[intelligence objectForKey:@"title"] intValue] <= 0 &&
|
|
|
|
|
[[story objectForKey:@"story_title"] containsString:title]) {
|
|
|
|
|
int score = [[[classifiers objectForKey:@"titles"] objectForKey:title] intValue];
|
|
|
|
|
[intelligence setObject:[NSNumber numberWithInt:score] forKey:@"title"];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (NSString *author in [classifiers objectForKey:@"authors"]) {
|
|
|
|
|
if ([[intelligence objectForKey:@"author"] intValue] <= 0 &&
|
2012-12-28 11:34:06 -08:00
|
|
|
|
[[story objectForKey:@"story_authors"] class] != [NSNull class] &&
|
2012-12-13 12:12:00 -08:00
|
|
|
|
[[story objectForKey:@"story_authors"] containsString:author]) {
|
|
|
|
|
int score = [[[classifiers objectForKey:@"authors"] objectForKey:author] intValue];
|
|
|
|
|
[intelligence setObject:[NSNumber numberWithInt:score] forKey:@"author"];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (NSString *tag in [classifiers objectForKey:@"tags"]) {
|
|
|
|
|
if ([[intelligence objectForKey:@"tags"] intValue] <= 0 &&
|
2012-12-28 11:34:06 -08:00
|
|
|
|
[[story objectForKey:@"story_tags"] class] != [NSNull class] &&
|
2012-12-13 12:12:00 -08:00
|
|
|
|
[[story objectForKey:@"story_tags"] containsObject:tag]) {
|
|
|
|
|
int score = [[[classifiers objectForKey:@"tags"] objectForKey:tag] intValue];
|
|
|
|
|
[intelligence setObject:[NSNumber numberWithInt:score] forKey:@"tags"];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (NSString *feed in [classifiers objectForKey:@"feeds"]) {
|
|
|
|
|
if ([[intelligence objectForKey:@"feed"] intValue] <= 0 &&
|
2012-12-27 18:37:05 -08:00
|
|
|
|
[storyFeedId isEqualToString:feed]) {
|
2012-12-13 12:12:00 -08:00
|
|
|
|
int score = [[[classifiers objectForKey:@"feeds"] objectForKey:feed] intValue];
|
|
|
|
|
[intelligence setObject:[NSNumber numberWithInt:score] forKey:@"feed"];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[newStory setObject:intelligence forKey:@"intelligence"];
|
|
|
|
|
[newFeedStories addObject:newStory];
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
storiesCollection.activeFeedStories = newFeedStories;
|
2012-12-12 15:08:24 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-06-20 19:18:29 -07:00
|
|
|
|
- (void)changeActiveFeedDetailRow {
|
|
|
|
|
[feedDetailViewController changeActiveFeedDetailRow];
|
2012-06-18 16:45:36 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-06-25 18:36:01 -04:00
|
|
|
|
- (void)loadStoryDetailView {
|
2015-11-28 22:07:38 -08:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone || self.isCompactWidth) {
|
2015-03-12 19:43:39 -07:00
|
|
|
|
[navigationController pushViewController:storyPageControl animated:YES];
|
|
|
|
|
navigationController.navigationItem.hidesBackButton = YES;
|
2011-10-27 09:44:58 -07:00
|
|
|
|
}
|
2015-03-12 19:43:39 -07:00
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
NSInteger activeStoryLocation = [storiesCollection locationOfActiveStory];
|
2012-12-18 17:29:23 -08:00
|
|
|
|
if (activeStoryLocation >= 0) {
|
|
|
|
|
BOOL animated = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad &&
|
|
|
|
|
!self.tryFeedCategory);
|
2015-04-26 21:22:25 -07:00
|
|
|
|
[self.storyPageControl view];
|
2015-09-28 20:01:17 -07:00
|
|
|
|
[self.storyPageControl.view setNeedsLayout];
|
|
|
|
|
[self.storyPageControl.view layoutIfNeeded];
|
2015-11-28 22:07:38 -08:00
|
|
|
|
|
|
|
|
|
NSDictionary *params = @{@"location" : @(activeStoryLocation), @"animated" : @(animated)};
|
|
|
|
|
|
|
|
|
|
if (self.isCompactWidth) {
|
|
|
|
|
[self performSelector:@selector(deferredChangePage:) withObject:params afterDelay:0.0];
|
|
|
|
|
} else {
|
|
|
|
|
[self deferredChangePage:params];
|
|
|
|
|
}
|
2012-06-09 12:54:32 -07:00
|
|
|
|
}
|
2015-03-12 19:43:39 -07:00
|
|
|
|
|
2013-09-11 17:05:47 -07:00
|
|
|
|
[MBProgressHUD hideHUDForView:self.storyPageControl.view animated:YES];
|
2012-06-11 16:56:38 -07:00
|
|
|
|
}
|
|
|
|
|
|
2015-11-28 22:07:38 -08:00
|
|
|
|
- (void)deferredChangePage:(NSDictionary *)params {
|
|
|
|
|
[self.storyPageControl changePage:[params[@"location"] integerValue] animated:[params[@"animated"] boolValue]];
|
|
|
|
|
[self.storyPageControl animateIntoPlace:YES];
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-11 23:48:27 -05:00
|
|
|
|
- (void)setTitle:(NSString *)title {
|
|
|
|
|
UILabel *label = [[UILabel alloc] init];
|
|
|
|
|
[label setFont:[UIFont boldSystemFontOfSize:16.0]];
|
|
|
|
|
[label setBackgroundColor:[UIColor clearColor]];
|
2013-02-14 15:36:21 -08:00
|
|
|
|
[label setTextColor:UIColorFromRGB(0x404040)];
|
2010-11-11 23:48:27 -05:00
|
|
|
|
[label setText:title];
|
2012-11-13 14:49:53 -08:00
|
|
|
|
[label setShadowOffset:CGSizeMake(0, -1)];
|
2013-02-14 15:36:21 -08:00
|
|
|
|
[label setShadowColor:UIColorFromRGB(0xFAFAFA)];
|
2010-11-11 23:48:27 -05:00
|
|
|
|
[label sizeToFit];
|
|
|
|
|
[navigationController.navigationBar.topItem setTitleView:label];
|
|
|
|
|
}
|
2010-06-24 00:22:26 -04:00
|
|
|
|
|
2012-06-14 10:19:36 -07:00
|
|
|
|
- (void)showOriginalStory:(NSURL *)url {
|
2013-12-12 15:35:22 -08:00
|
|
|
|
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
|
2017-05-05 20:29:44 -07:00
|
|
|
|
if (!url) {
|
|
|
|
|
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Nowhere to go"
|
|
|
|
|
message:@"The story doesn't link anywhere."
|
|
|
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
|
|
|
|
|
|
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"Oh well" style:UIAlertActionStyleDefault
|
|
|
|
|
handler:^(UIAlertAction * action) {}];
|
|
|
|
|
|
|
|
|
|
[alert addAction:defaultAction];
|
|
|
|
|
[navigationController presentViewController:alert animated:YES completion:nil];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-17 16:32:22 -08:00
|
|
|
|
NSString *storyBrowser = [preferences stringForKey:@"story_browser"];
|
|
|
|
|
if ([storyBrowser isEqualToString:@"safari"]) {
|
2017-01-05 11:44:18 -08:00
|
|
|
|
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
|
2017-03-09 17:05:35 -08:00
|
|
|
|
// [[UIApplication sharedApplication] openURL:url];
|
2013-12-12 15:35:22 -08:00
|
|
|
|
return;
|
2017-02-17 16:32:22 -08:00
|
|
|
|
} else if ([storyBrowser isEqualToString:@"chrome"] &&
|
2013-12-12 15:35:22 -08:00
|
|
|
|
[[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"googlechrome-x-callback://"]]) {
|
2017-01-05 11:44:18 -08:00
|
|
|
|
NSString *openingURL = [url.absoluteString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
|
2013-12-12 15:35:22 -08:00
|
|
|
|
NSURL *callbackURL = [NSURL URLWithString:@"newsblur://"];
|
2017-01-05 11:44:18 -08:00
|
|
|
|
NSString *callback = [callbackURL.absoluteString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
|
|
|
|
|
NSString *sourceName = [[[NSBundle mainBundle]objectForInfoDictionaryKey:@"CFBundleName"] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
|
2013-12-12 15:35:22 -08:00
|
|
|
|
|
|
|
|
|
NSURL *activityURL = [NSURL URLWithString:
|
|
|
|
|
[NSString stringWithFormat:@"googlechrome-x-callback://x-callback-url/open/?url=%@&x-success=%@&x-source=%@",
|
|
|
|
|
openingURL,
|
|
|
|
|
callback,
|
|
|
|
|
sourceName]];
|
|
|
|
|
|
2017-01-05 11:44:18 -08:00
|
|
|
|
[[UIApplication sharedApplication] openURL:activityURL options:@{} completionHandler:nil];
|
2015-06-04 21:05:25 -05:00
|
|
|
|
return;
|
2017-02-17 16:32:22 -08:00
|
|
|
|
} else if ([storyBrowser isEqualToString:@"opera_mini"] &&
|
2015-06-04 21:05:25 -05:00
|
|
|
|
[[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"opera-http://"]]) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NSString *operaURL;
|
|
|
|
|
NSRange prefix = [[url absoluteString] rangeOfString: @"http"];
|
|
|
|
|
if (NSNotFound != prefix.location) {
|
|
|
|
|
operaURL = [[url absoluteString]
|
|
|
|
|
stringByReplacingCharactersInRange: prefix
|
|
|
|
|
withString: @"opera-http"];
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-05 11:44:18 -08:00
|
|
|
|
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:operaURL] options:@{} completionHandler:nil];
|
2015-06-04 21:05:25 -05:00
|
|
|
|
return;
|
2017-02-17 16:32:22 -08:00
|
|
|
|
} else if ([storyBrowser isEqualToString:@"firefox"]) {
|
|
|
|
|
NSString *encodedURL = [url.absoluteString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
|
2017-08-11 13:27:12 -07:00
|
|
|
|
NSString *firefoxURL = [NSString stringWithFormat:@"%@%@", @"firefox://open-url?url=", encodedURL];
|
2017-04-06 11:45:43 -07:00
|
|
|
|
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:firefoxURL] options:@{} completionHandler:nil];
|
2020-02-13 20:07:12 -08:00
|
|
|
|
} else if ([storyBrowser isEqualToString:@"edge"]){
|
|
|
|
|
NSString *edgeURL;
|
|
|
|
|
NSRange prefix = [[url absoluteString] rangeOfString: @"http"];
|
|
|
|
|
|
|
|
|
|
if (NSNotFound != prefix.location) {
|
|
|
|
|
edgeURL = [[url absoluteString]
|
|
|
|
|
stringByReplacingCharactersInRange: prefix
|
|
|
|
|
withString: @"microsoft-edge-http"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:edgeURL] options:@{} completionHandler:nil];
|
2020-08-25 15:56:29 -07:00
|
|
|
|
} else if ([storyBrowser isEqualToString:@"brave"]){
|
|
|
|
|
NSString *encodedURL = [url.absoluteString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
|
|
|
|
|
NSString *braveURL = [NSString stringWithFormat:@"%@%@", @"brave://open-url?url=", encodedURL];
|
|
|
|
|
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:braveURL] options:@{} completionHandler:nil];
|
2017-02-17 16:32:22 -08:00
|
|
|
|
} else if ([storyBrowser isEqualToString:@"inappsafari"]) {
|
2020-01-27 20:53:31 -08:00
|
|
|
|
[self showSafariViewControllerWithURL:url useReader:NO];
|
2017-11-05 18:22:58 -08:00
|
|
|
|
} else if ([storyBrowser isEqualToString:@"inappsafarireader"]) {
|
2020-01-27 20:53:31 -08:00
|
|
|
|
[self showSafariViewControllerWithURL:url useReader:YES];
|
2012-06-29 10:46:26 -07:00
|
|
|
|
} else {
|
2020-01-31 15:50:03 -08:00
|
|
|
|
[self showInAppBrowser:url withCustomTitle:nil fromSender:nil];
|
2020-01-28 16:10:16 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-31 15:50:03 -08:00
|
|
|
|
- (void)showInAppBrowser:(NSURL *)url withCustomTitle:(NSString *)customTitle fromSender:(id)sender {
|
2020-01-28 16:10:16 -08:00
|
|
|
|
if (!originalStoryViewController) {
|
|
|
|
|
originalStoryViewController = [[OriginalStoryViewController alloc] init];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.activeOriginalStoryURL = url;
|
|
|
|
|
originalStoryViewController.customPageTitle = customTitle;
|
|
|
|
|
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2020-01-31 15:50:03 -08:00
|
|
|
|
if ([sender isKindOfClass:[UIBarButtonItem class]]) {
|
|
|
|
|
[originalStoryViewController view]; // Force viewDidLoad
|
|
|
|
|
[originalStoryViewController loadInitialStory];
|
|
|
|
|
[self showPopoverWithViewController:originalStoryViewController contentSize:CGSizeMake(600.0, 1000.0) barButtonItem:sender];
|
|
|
|
|
} else if ([sender isKindOfClass:[UITableViewCell class]]) {
|
|
|
|
|
UITableViewCell *cell = (UITableViewCell *)sender;
|
|
|
|
|
|
2014-02-05 17:50:19 -08:00
|
|
|
|
[originalStoryViewController view]; // Force viewDidLoad
|
2014-01-16 17:15:29 -08:00
|
|
|
|
[originalStoryViewController loadInitialStory];
|
2020-01-31 15:50:03 -08:00
|
|
|
|
[self showPopoverWithViewController:originalStoryViewController contentSize:CGSizeMake(600.0, 1000.0) sourceView:cell sourceRect:cell.bounds];
|
2020-01-28 16:10:16 -08:00
|
|
|
|
} else {
|
|
|
|
|
[self.masterContainerViewController transitionToOriginalView];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if ([[navigationController viewControllers]
|
|
|
|
|
containsObject:originalStoryViewController]) {
|
|
|
|
|
return;
|
2013-12-12 15:35:22 -08:00
|
|
|
|
}
|
2020-01-28 16:10:16 -08:00
|
|
|
|
[navigationController pushViewController:originalStoryViewController
|
|
|
|
|
animated:YES];
|
|
|
|
|
[originalStoryViewController view]; // Force viewDidLoad
|
|
|
|
|
[originalStoryViewController loadInitialStory];
|
2012-06-29 10:46:26 -07:00
|
|
|
|
}
|
2010-11-13 18:32:43 -05:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-27 20:53:31 -08:00
|
|
|
|
- (void)showSafariViewControllerWithURL:(NSURL *)url useReader:(BOOL)useReader {
|
2020-02-23 15:21:32 -08:00
|
|
|
|
SFSafariViewControllerConfiguration *config = [SFSafariViewControllerConfiguration new];
|
|
|
|
|
config.entersReaderIfAvailable = useReader;
|
|
|
|
|
self.safariViewController = [[SFSafariViewController alloc] initWithURL:url configuration:config];
|
2020-01-27 20:53:31 -08:00
|
|
|
|
self.safariViewController.delegate = self;
|
|
|
|
|
[self.storyPageControl setNavigationBarHidden:NO];
|
|
|
|
|
[navigationController presentViewController:self.safariViewController animated:YES completion:nil];
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-05 14:16:11 -08:00
|
|
|
|
- (BOOL)showingSafariViewController {
|
|
|
|
|
return self.safariViewController.delegate != nil;
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-17 19:11:17 -07:00
|
|
|
|
- (void)safariViewControllerDidFinish:(SFSafariViewController *)controller {
|
2015-11-11 21:26:11 -08:00
|
|
|
|
// You'd think doing this in the dismiss completion block would work... but nope.
|
|
|
|
|
[self performSelector:@selector(deferredSafariCleanup) withObject:nil afterDelay:0.2];
|
2015-10-22 21:09:03 -07:00
|
|
|
|
controller.delegate = nil;
|
2015-10-24 22:22:16 -07:00
|
|
|
|
[controller dismissViewControllerAnimated:YES completion:nil];
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-11 21:26:11 -08:00
|
|
|
|
- (void)deferredSafariCleanup {
|
2019-04-15 21:00:14 -07:00
|
|
|
|
// if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
// self.navigationController.view.frame = CGRectMake(self.navigationController.view.frame.origin.x, self.navigationController.view.frame.origin.y, self.isPortrait ? 270.0 : 370.0, self.navigationController.view.frame.size.height);
|
|
|
|
|
// }
|
2015-11-11 21:26:11 -08:00
|
|
|
|
|
|
|
|
|
[self.storyPageControl reorientPages];
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-17 19:11:17 -07:00
|
|
|
|
- (void)navigationController:(UINavigationController *)_navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
|
2019-06-21 20:09:09 -07:00
|
|
|
|
if ([viewController isKindOfClass:[SFSafariViewController class]] || [viewController isKindOfClass:[FontSettingsViewController class]]) {
|
2015-09-17 19:11:17 -07:00
|
|
|
|
[_navigationController setNavigationBarHidden:YES animated:YES];
|
|
|
|
|
} else {
|
|
|
|
|
[_navigationController setNavigationBarHidden:NO animated:YES];
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-10-31 16:35:32 -07:00
|
|
|
|
|
2016-03-17 20:40:09 -07:00
|
|
|
|
- (UINavigationController *)addSiteNavigationController {
|
|
|
|
|
if (!_addSiteNavigationController) {
|
|
|
|
|
self.addSiteNavigationController = [[UINavigationController alloc] initWithRootViewController:self.addSiteViewController];
|
|
|
|
|
self.addSiteNavigationController.delegate = self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _addSiteNavigationController;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-31 16:35:32 -07:00
|
|
|
|
- (UINavigationController *)fontSettingsNavigationController {
|
|
|
|
|
if (!_fontSettingsNavigationController) {
|
|
|
|
|
self.fontSettingsNavigationController = [[UINavigationController alloc] initWithRootViewController:self.fontSettingsViewController];
|
|
|
|
|
self.fontSettingsNavigationController.delegate = self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _fontSettingsNavigationController;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-13 18:32:43 -05:00
|
|
|
|
- (void)closeOriginalStory {
|
2014-01-07 18:26:17 -08:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
[self.masterContainerViewController transitionFromOriginalView];
|
|
|
|
|
} else {
|
|
|
|
|
if ([[navigationController viewControllers] containsObject:originalStoryViewController]) {
|
|
|
|
|
[navigationController popToViewController:storyPageControl animated:YES];
|
|
|
|
|
}
|
2013-03-04 17:15:50 -08:00
|
|
|
|
}
|
2010-11-13 18:32:43 -05:00
|
|
|
|
}
|
2010-06-24 00:22:26 -04:00
|
|
|
|
|
2012-07-10 12:34:58 -07:00
|
|
|
|
- (void)hideStoryDetailView {
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
2012-07-24 16:03:16 -07:00
|
|
|
|
[self.masterContainerViewController transitionFromFeedDetail];
|
2012-07-11 18:08:07 -07:00
|
|
|
|
} else {
|
|
|
|
|
[self.navigationController popViewControllerAnimated:YES];
|
2012-07-10 12:34:58 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-24 20:32:04 -07:00
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Siri Shortcuts
|
|
|
|
|
|
|
|
|
|
- (void)handleUserActivity:(NSUserActivity *)activity {
|
|
|
|
|
if ([activity.activityType isEqualToString:@"com.newsblur.refresh"]) {
|
|
|
|
|
[self.navigationController popToRootViewControllerAnimated:NO];
|
|
|
|
|
[self.feedsViewController refreshFeedList];
|
|
|
|
|
} else if ([activity.activityType isEqualToString:@"com.newsblur.gotoFolder"]) {
|
|
|
|
|
NSString *folder = activity.userInfo[@"folder"];
|
|
|
|
|
|
|
|
|
|
[self.navigationController popToRootViewControllerAnimated:NO];
|
|
|
|
|
[self loadRiverFeedDetailView:self.feedDetailViewController withFolder:folder];
|
|
|
|
|
} else if ([activity.activityType isEqualToString:@"com.newsblur.gotoFeed"]) {
|
|
|
|
|
NSString *folder = activity.userInfo[@"folder"];
|
|
|
|
|
NSString *feedID = activity.userInfo[@"feedID"];
|
|
|
|
|
|
|
|
|
|
[self.navigationController popToRootViewControllerAnimated:NO];
|
|
|
|
|
[self loadFolder:folder feedID:feedID];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)donateRefresh {
|
|
|
|
|
NSUserActivity *activity = [[NSUserActivity alloc] initWithActivityType:@"com.newsblur.refresh"];
|
|
|
|
|
|
|
|
|
|
activity.title = @"Refresh NewsBlur";
|
|
|
|
|
activity.userInfo = @{};
|
|
|
|
|
activity.requiredUserInfoKeys = [NSSet new];
|
|
|
|
|
activity.eligibleForSearch = YES;
|
|
|
|
|
|
|
|
|
|
if (@available(iOS 12.0, *)) {
|
|
|
|
|
activity.eligibleForPrediction = YES;
|
|
|
|
|
activity.suggestedInvocationPhrase = @"Refresh NewsBlur";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CSSearchableItemAttributeSet *attributes = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeItem];
|
|
|
|
|
|
|
|
|
|
attributes.contentDescription = @"Fetch new stories in NewsBlur.";
|
|
|
|
|
|
|
|
|
|
activity.contentAttributeSet = attributes;
|
|
|
|
|
|
|
|
|
|
self.userActivity = activity;
|
|
|
|
|
[self.userActivity becomeCurrent];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)donateFolder {
|
|
|
|
|
NSUserActivity *activity = [[NSUserActivity alloc] initWithActivityType:@"com.newsblur.gotoFolder"];
|
|
|
|
|
NSString *folder = storiesCollection.activeFolder;
|
|
|
|
|
NSString *title = storiesCollection.activeTitle;
|
|
|
|
|
|
2019-08-23 20:14:22 -07:00
|
|
|
|
if (folder == nil || title == nil) {
|
|
|
|
|
return;
|
|
|
|
|
} else if ([folder isEqualToString:@"river_blurblogs"]) {
|
2019-04-24 20:32:04 -07:00
|
|
|
|
activity.title = @"Read All Shared Stories";
|
|
|
|
|
} else if ([folder isEqualToString:@"river_global"]) {
|
|
|
|
|
activity.title = @"Read Global Shared Stories";
|
|
|
|
|
} else if ([folder isEqualToString:@"everything"]) {
|
|
|
|
|
activity.title = @"Read All the Stories";
|
|
|
|
|
} else if ([folder isEqualToString:@"infrequent"]) {
|
|
|
|
|
activity.title = @"Read Infrequent Site Stories";
|
|
|
|
|
} else if (storiesCollection.isSavedView && storiesCollection.activeSavedStoryTag) {
|
|
|
|
|
activity.title = [NSString stringWithFormat:@"Read %@", storiesCollection.activeSavedStoryTag];
|
|
|
|
|
} else if ([folder isEqualToString:@"read_stories"]) {
|
|
|
|
|
activity.title = @"Re-read Stories";
|
2020-05-27 21:26:44 -07:00
|
|
|
|
} else if ([folder isEqualToString:@"saved_searches"]) {
|
|
|
|
|
activity.title = @"Re-read Saved Searches";
|
2019-04-24 20:32:04 -07:00
|
|
|
|
} else if ([folder isEqualToString:@"saved_stories"]) {
|
|
|
|
|
activity.title = @"Re-read Saved Stories";
|
|
|
|
|
} else {
|
|
|
|
|
activity.title = [NSString stringWithFormat:@"Read %@", title];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
activity.userInfo = @{@"folder" : folder};
|
|
|
|
|
activity.requiredUserInfoKeys = [NSSet setWithObject:@"folder"];
|
|
|
|
|
activity.eligibleForSearch = YES;
|
|
|
|
|
|
|
|
|
|
if (@available(iOS 12.0, *)) {
|
|
|
|
|
activity.eligibleForPrediction = YES;
|
|
|
|
|
activity.suggestedInvocationPhrase = activity.title;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CSSearchableItemAttributeSet *attributes = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeItem];
|
|
|
|
|
|
|
|
|
|
attributes.contentDescription = [NSString stringWithFormat:@"Go to the %@ folder in NewsBlur.", title];
|
|
|
|
|
|
|
|
|
|
activity.contentAttributeSet = attributes;
|
|
|
|
|
|
|
|
|
|
self.userActivity = activity;
|
|
|
|
|
[self.userActivity becomeCurrent];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)donateFeed {
|
|
|
|
|
NSUserActivity *activity = [[NSUserActivity alloc] initWithActivityType:@"com.newsblur.gotoFeed"];
|
|
|
|
|
NSString *folder = storiesCollection.activeFolder;
|
|
|
|
|
NSDictionary *feed = storiesCollection.activeFeed;
|
|
|
|
|
NSString *title = storiesCollection.activeTitle;
|
|
|
|
|
NSString *feedID = [NSString stringWithFormat:@"%@", feed[@"id"]];
|
|
|
|
|
|
|
|
|
|
activity.title = [NSString stringWithFormat:@"Read %@", title];
|
|
|
|
|
activity.eligibleForSearch = YES;
|
|
|
|
|
|
|
|
|
|
if (folder != nil) {
|
|
|
|
|
activity.userInfo = @{@"folder" : folder, @"feedID" : feedID};
|
|
|
|
|
activity.requiredUserInfoKeys = [NSSet setWithArray:@[@"folder", @"feedID"]];
|
|
|
|
|
} else {
|
|
|
|
|
activity.userInfo = @{@"feedID" : feedID};
|
|
|
|
|
activity.requiredUserInfoKeys = [NSSet setWithArray:@[@"feedID"]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (@available(iOS 12.0, *)) {
|
|
|
|
|
activity.eligibleForPrediction = YES;
|
|
|
|
|
activity.suggestedInvocationPhrase = activity.title;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CSSearchableItemAttributeSet *attributes = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeItem];
|
|
|
|
|
BOOL isSocial = [self isSocialFeed:feedID];
|
|
|
|
|
BOOL isSaved = [self isSavedFeed:feedID];
|
|
|
|
|
UIImage *thumbnailImage = [self getFavicon:feedID isSocial:isSocial isSaved:isSaved];
|
|
|
|
|
UIImage *scaledImage = [Utilities imageWithImage:thumbnailImage convertToSize:CGSizeMake(128, 128)];
|
|
|
|
|
|
|
|
|
|
attributes.contentDescription = [NSString stringWithFormat:@"Go to the %@ feed in NewsBlur.", title];
|
|
|
|
|
attributes.thumbnailData = UIImagePNGRepresentation(scaledImage);
|
|
|
|
|
|
|
|
|
|
activity.contentAttributeSet = attributes;
|
|
|
|
|
|
|
|
|
|
self.userActivity = activity;
|
|
|
|
|
[self.userActivity becomeCurrent];
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-05 11:45:33 -07:00
|
|
|
|
#pragma mark - Text View
|
|
|
|
|
|
|
|
|
|
- (void)populateDictTextFeeds {
|
|
|
|
|
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
NSDictionary *textFeeds = [preferences dictionaryForKey:@"feeds:text"];
|
|
|
|
|
if (!textFeeds) {
|
|
|
|
|
self.dictTextFeeds = [[NSMutableDictionary alloc] init];
|
|
|
|
|
} else {
|
|
|
|
|
self.dictTextFeeds = [textFeeds mutableCopy];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)isFeedInTextView:(id)feedId {
|
2017-04-11 16:15:18 -07:00
|
|
|
|
id text = [self.dictTextFeeds objectForKey:feedId];
|
2017-04-12 11:19:24 -07:00
|
|
|
|
if (text != nil) return YES;
|
2017-04-11 16:15:18 -07:00
|
|
|
|
return NO;
|
2015-10-05 11:45:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)toggleFeedTextView:(id)feedId {
|
|
|
|
|
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
if ([self.dictTextFeeds objectForKey:feedId]) {
|
|
|
|
|
[self.dictTextFeeds removeObjectForKey:feedId];
|
|
|
|
|
} else {
|
|
|
|
|
[self.dictTextFeeds setObject:[NSNumber numberWithBool:YES] forKey:feedId];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[preferences setObject:self.dictTextFeeds forKey:@"feeds:text"];
|
|
|
|
|
[preferences synchronize];
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-29 17:28:41 -07:00
|
|
|
|
#pragma mark - Unread Counts
|
|
|
|
|
|
2014-05-20 12:21:17 -07:00
|
|
|
|
- (void)populateDictUnreadCounts {
|
2013-06-29 17:28:41 -07:00
|
|
|
|
[self.database inDatabase:^(FMDatabase *db) {
|
|
|
|
|
FMResultSet *cursor = [db executeQuery:@"SELECT * FROM unread_counts"];
|
|
|
|
|
|
|
|
|
|
while ([cursor next]) {
|
|
|
|
|
NSDictionary *unreadCounts = [cursor resultDictionary];
|
|
|
|
|
[self.dictUnreadCounts setObject:unreadCounts forKey:[unreadCounts objectForKey:@"feed_id"]];
|
|
|
|
|
}
|
2013-10-03 18:07:39 -07:00
|
|
|
|
|
|
|
|
|
[cursor close];
|
2013-06-29 17:28:41 -07:00
|
|
|
|
}];
|
|
|
|
|
}
|
2013-06-21 18:35:06 -07:00
|
|
|
|
|
2013-09-25 17:43:00 -07:00
|
|
|
|
- (NSInteger)unreadCount {
|
2014-02-12 20:09:37 -08:00
|
|
|
|
if (storiesCollection.isRiverView || storiesCollection.isSocialRiverView) {
|
2011-10-31 10:10:38 -07:00
|
|
|
|
return [self unreadCountForFolder:nil];
|
|
|
|
|
} else {
|
|
|
|
|
return [self unreadCountForFeed:nil];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-25 17:43:00 -07:00
|
|
|
|
- (NSInteger)allUnreadCount {
|
|
|
|
|
NSInteger total = 0;
|
2012-07-29 22:14:08 -07:00
|
|
|
|
for (id key in self.dictSocialFeeds) {
|
|
|
|
|
NSDictionary *feed = [self.dictSocialFeeds objectForKey:key];
|
2013-09-25 17:43:00 -07:00
|
|
|
|
total += [[feed objectForKey:@"ps"] integerValue];
|
|
|
|
|
total += [[feed objectForKey:@"nt"] integerValue];
|
2012-07-29 22:14:08 -07:00
|
|
|
|
NSLog(@"feed title and number is %@ %i", [feed objectForKey:@"feed_title"], ([[feed objectForKey:@"ps"] intValue] + [[feed objectForKey:@"nt"] intValue]));
|
2013-09-25 17:43:00 -07:00
|
|
|
|
NSLog(@"total is %ld", (long)total);
|
2012-07-29 22:14:08 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-29 17:28:41 -07:00
|
|
|
|
for (id key in self.dictUnreadCounts) {
|
|
|
|
|
NSDictionary *feed = [self.dictUnreadCounts objectForKey:key];
|
2012-07-29 22:14:08 -07:00
|
|
|
|
total += [[feed objectForKey:@"ps"] intValue];
|
|
|
|
|
total += [[feed objectForKey:@"nt"] intValue];
|
2013-06-29 17:28:41 -07:00
|
|
|
|
// NSLog(@"feed title and number is %@ %i", [feed objectForKey:@"feed_title"], ([[feed objectForKey:@"ps"] intValue] + [[feed objectForKey:@"nt"] intValue]));
|
|
|
|
|
// NSLog(@"total is %i", total);
|
2012-07-29 22:14:08 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return total;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-25 17:43:00 -07:00
|
|
|
|
- (NSInteger)unreadCountForFeed:(NSString *)feedId {
|
|
|
|
|
NSInteger total = 0;
|
2011-10-31 10:10:38 -07:00
|
|
|
|
NSDictionary *feed;
|
|
|
|
|
|
|
|
|
|
if (feedId) {
|
|
|
|
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@",feedId];
|
2012-10-07 15:47:21 -04:00
|
|
|
|
if ([feedIdStr containsString:@"social:"]) {
|
2012-08-01 12:41:02 -07:00
|
|
|
|
feed = [self.dictSocialFeeds objectForKey:feedIdStr];
|
|
|
|
|
} else {
|
2013-06-29 17:28:41 -07:00
|
|
|
|
feed = [self.dictUnreadCounts objectForKey:feedIdStr];
|
2012-08-01 12:41:02 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-10-31 10:10:38 -07:00
|
|
|
|
} else {
|
2014-02-12 20:09:37 -08:00
|
|
|
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@", [storiesCollection.activeFeed objectForKey:@"id"]];
|
2013-07-16 19:09:46 -07:00
|
|
|
|
feed = [self.dictUnreadCounts objectForKey:feedIdStr];
|
2011-10-31 10:10:38 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
total += [[feed objectForKey:@"ps"] intValue];
|
2016-03-26 21:27:25 -07:00
|
|
|
|
if (self.isSavedStoriesIntelligenceMode) {
|
|
|
|
|
NSInteger savedCount = [self.dictSavedStoryFeedCounts[feedId] integerValue];
|
|
|
|
|
total += savedCount;
|
|
|
|
|
}
|
2011-08-09 17:58:43 -07:00
|
|
|
|
if ([self selectedIntelligence] <= 0) {
|
2011-10-31 10:10:38 -07:00
|
|
|
|
total += [[feed objectForKey:@"nt"] intValue];
|
2011-08-09 17:58:43 -07:00
|
|
|
|
}
|
|
|
|
|
if ([self selectedIntelligence] <= -1) {
|
2011-10-31 10:10:38 -07:00
|
|
|
|
total += [[feed objectForKey:@"ng"] intValue];
|
2011-08-09 17:58:43 -07:00
|
|
|
|
}
|
2011-10-31 10:10:38 -07:00
|
|
|
|
|
2011-08-09 17:58:43 -07:00
|
|
|
|
return total;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-25 17:43:00 -07:00
|
|
|
|
- (NSInteger)unreadCountForFolder:(NSString *)folderName {
|
|
|
|
|
NSInteger total = 0;
|
2011-10-31 10:10:38 -07:00
|
|
|
|
NSArray *folder;
|
|
|
|
|
|
2013-02-06 15:15:43 -08:00
|
|
|
|
if ([folderName isEqual:@"river_blurblogs"] ||
|
2014-02-12 20:09:37 -08:00
|
|
|
|
(!folderName && [storiesCollection.activeFolder isEqual:@"river_blurblogs"])) {
|
2012-08-13 17:07:26 -07:00
|
|
|
|
for (id feedId in self.dictSocialFeeds) {
|
|
|
|
|
total += [self unreadCountForFeed:feedId];
|
|
|
|
|
}
|
2013-02-06 15:15:43 -08:00
|
|
|
|
} else if ([folderName isEqual:@"river_global"] ||
|
2014-02-12 20:09:37 -08:00
|
|
|
|
(!folderName && [storiesCollection.activeFolder isEqual:@"river_global"])) {
|
2012-12-07 15:17:22 -08:00
|
|
|
|
total = 0;
|
2013-02-06 15:15:43 -08:00
|
|
|
|
} else if ([folderName isEqual:@"everything"] ||
|
2017-11-05 22:07:43 -08:00
|
|
|
|
[folderName isEqual:@"infrequent"] ||
|
|
|
|
|
(!folderName && ([storiesCollection.activeFolder isEqual:@"everything"] ||
|
|
|
|
|
[storiesCollection.activeFolder isEqual:@"infrequent"]))) {
|
2014-05-15 17:03:25 -07:00
|
|
|
|
// TODO: Fix race condition where self.dictUnreadCounts can be changed while being updated.
|
2013-06-29 17:28:41 -07:00
|
|
|
|
for (id feedId in self.dictUnreadCounts) {
|
2011-12-01 09:01:18 -08:00
|
|
|
|
total += [self unreadCountForFeed:feedId];
|
|
|
|
|
}
|
2011-10-31 10:10:38 -07:00
|
|
|
|
} else {
|
2011-12-01 09:01:18 -08:00
|
|
|
|
if (!folderName) {
|
2014-02-12 20:09:37 -08:00
|
|
|
|
folder = [self.dictFolders objectForKey:storiesCollection.activeFolder];
|
2011-12-01 09:01:18 -08:00
|
|
|
|
} else {
|
|
|
|
|
folder = [self.dictFolders objectForKey:folderName];
|
|
|
|
|
}
|
2011-10-31 10:10:38 -07:00
|
|
|
|
|
2011-12-01 09:01:18 -08:00
|
|
|
|
for (id feedId in folder) {
|
|
|
|
|
total += [self unreadCountForFeed:feedId];
|
|
|
|
|
}
|
2011-10-31 10:10:38 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return total;
|
2011-07-26 08:37:10 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-10-07 15:47:21 -04:00
|
|
|
|
|
2012-10-12 15:33:40 -04:00
|
|
|
|
- (UnreadCounts *)splitUnreadCountForFeed:(NSString *)feedId {
|
|
|
|
|
UnreadCounts *counts = [UnreadCounts alloc];
|
2013-06-29 17:28:41 -07:00
|
|
|
|
NSDictionary *feedCounts;
|
2012-10-07 15:47:21 -04:00
|
|
|
|
|
2013-06-29 17:28:41 -07:00
|
|
|
|
if (!feedId) {
|
2014-02-12 20:09:37 -08:00
|
|
|
|
feedId = [storiesCollection.activeFeed objectForKey:@"id"];
|
2012-10-07 15:47:21 -04:00
|
|
|
|
}
|
2013-06-29 17:28:41 -07:00
|
|
|
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@", feedId];
|
|
|
|
|
feedCounts = [self.dictUnreadCounts objectForKey:feedIdStr];
|
2012-10-07 15:47:21 -04:00
|
|
|
|
|
2013-06-29 17:28:41 -07:00
|
|
|
|
counts.ps += [[feedCounts objectForKey:@"ps"] intValue];
|
|
|
|
|
counts.nt += [[feedCounts objectForKey:@"nt"] intValue];
|
|
|
|
|
counts.ng += [[feedCounts objectForKey:@"ng"] intValue];
|
2012-10-07 15:47:21 -04:00
|
|
|
|
|
|
|
|
|
return counts;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-12 15:33:40 -04:00
|
|
|
|
- (UnreadCounts *)splitUnreadCountForFolder:(NSString *)folderName {
|
|
|
|
|
UnreadCounts *counts = [UnreadCounts alloc];
|
2012-10-07 15:47:21 -04:00
|
|
|
|
NSArray *folder;
|
|
|
|
|
|
2012-10-14 17:30:03 -07:00
|
|
|
|
if ([[self.folderCountCache objectForKey:folderName] boolValue]) {
|
|
|
|
|
counts.ps = [[self.folderCountCache objectForKey:[NSString stringWithFormat:@"%@-ps", folderName]] intValue];
|
|
|
|
|
counts.nt = [[self.folderCountCache objectForKey:[NSString stringWithFormat:@"%@-nt", folderName]] intValue];
|
|
|
|
|
counts.ng = [[self.folderCountCache objectForKey:[NSString stringWithFormat:@"%@-ng", folderName]] intValue];
|
|
|
|
|
return counts;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-06 15:15:43 -08:00
|
|
|
|
if ([folderName isEqual:@"river_blurblogs"] ||
|
2014-02-12 20:09:37 -08:00
|
|
|
|
(!folderName && [storiesCollection.activeFolder isEqual:@"river_blurblogs"])) {
|
2012-10-07 15:47:21 -04:00
|
|
|
|
for (id feedId in self.dictSocialFeeds) {
|
|
|
|
|
[counts addCounts:[self splitUnreadCountForFeed:feedId]];
|
|
|
|
|
}
|
2013-02-06 15:15:43 -08:00
|
|
|
|
} else if ([folderName isEqual:@"river_global"] ||
|
2014-02-12 20:09:37 -08:00
|
|
|
|
(!folderName && [storiesCollection.activeFolder isEqual:@"river_global"])) {
|
2012-12-07 15:17:22 -08:00
|
|
|
|
// Nothing for global
|
2013-02-06 15:15:43 -08:00
|
|
|
|
} else if ([folderName isEqual:@"everything"] ||
|
2017-11-05 22:07:43 -08:00
|
|
|
|
[folderName isEqual:@"infrequent"] ||
|
|
|
|
|
(!folderName && ([storiesCollection.activeFolder isEqual:@"everything"] ||
|
|
|
|
|
[storiesCollection.activeFolder isEqual:@"infrequent"]))) {
|
2013-02-21 12:19:15 -08:00
|
|
|
|
for (NSArray *folder in [self.dictFolders allValues]) {
|
|
|
|
|
for (id feedId in folder) {
|
2014-06-21 14:40:06 -07:00
|
|
|
|
if ([feedId isKindOfClass:[NSString class]] && [feedId startsWith:@"saved:"]) {
|
|
|
|
|
// Skip saved feeds which have fake unread counts.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2013-02-21 12:19:15 -08:00
|
|
|
|
[counts addCounts:[self splitUnreadCountForFeed:feedId]];
|
|
|
|
|
}
|
2012-10-07 15:47:21 -04:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (!folderName) {
|
2014-02-12 20:09:37 -08:00
|
|
|
|
folder = [self.dictFolders objectForKey:storiesCollection.activeFolder];
|
2012-10-07 15:47:21 -04:00
|
|
|
|
} else {
|
|
|
|
|
folder = [self.dictFolders objectForKey:folderName];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (id feedId in folder) {
|
|
|
|
|
[counts addCounts:[self splitUnreadCountForFeed:feedId]];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-14 17:30:03 -07:00
|
|
|
|
if (!self.folderCountCache) {
|
|
|
|
|
self.folderCountCache = [[NSMutableDictionary alloc] init];
|
|
|
|
|
}
|
|
|
|
|
[self.folderCountCache setObject:[NSNumber numberWithBool:YES] forKey:folderName];
|
|
|
|
|
[self.folderCountCache setObject:[NSNumber numberWithInt:counts.ps] forKey:[NSString stringWithFormat:@"%@-ps", folderName]];
|
|
|
|
|
[self.folderCountCache setObject:[NSNumber numberWithInt:counts.nt] forKey:[NSString stringWithFormat:@"%@-nt", folderName]];
|
|
|
|
|
[self.folderCountCache setObject:[NSNumber numberWithInt:counts.ng] forKey:[NSString stringWithFormat:@"%@-ng", folderName]];
|
|
|
|
|
|
2012-10-07 15:47:21 -04:00
|
|
|
|
return counts;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 18:35:40 -07:00
|
|
|
|
- (BOOL)isFolderCollapsed:(NSString *)folderName {
|
|
|
|
|
if (!self.collapsedFolders) {
|
|
|
|
|
self.collapsedFolders = [[NSMutableDictionary alloc] init];
|
|
|
|
|
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
for (NSString *folderName in self.dictFoldersArray) {
|
|
|
|
|
NSString *collapseKey = [NSString stringWithFormat:@"folderCollapsed:%@",
|
|
|
|
|
folderName];
|
|
|
|
|
if ([userPreferences boolForKey:collapseKey]) {
|
|
|
|
|
[self.collapsedFolders setObject:folderName forKey:folderName];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return !![self.collapsedFolders objectForKey:folderName];
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-29 17:28:41 -07:00
|
|
|
|
#pragma mark - Story Management
|
|
|
|
|
|
2011-11-10 18:28:22 -08:00
|
|
|
|
- (NSDictionary *)markVisibleStoriesRead {
|
|
|
|
|
NSMutableDictionary *feedsStories = [NSMutableDictionary dictionary];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
for (NSDictionary *story in storiesCollection.activeFeedStories) {
|
2011-11-10 18:28:22 -08:00
|
|
|
|
if ([[story objectForKey:@"read_status"] intValue] != 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@",[story objectForKey:@"story_feed_id"]];
|
2013-10-17 17:23:52 -07:00
|
|
|
|
NSDictionary *feed = [self getFeed:feedIdStr];
|
2011-11-10 18:28:22 -08:00
|
|
|
|
if (![feedsStories objectForKey:feedIdStr]) {
|
|
|
|
|
[feedsStories setObject:[NSMutableArray array] forKey:feedIdStr];
|
|
|
|
|
}
|
|
|
|
|
NSMutableArray *stories = [feedsStories objectForKey:feedIdStr];
|
2013-08-06 18:08:55 -07:00
|
|
|
|
[stories addObject:[story objectForKey:@"story_hash"]];
|
2014-02-20 18:23:58 -08:00
|
|
|
|
[storiesCollection markStoryRead:story feed:feed];
|
2011-11-10 18:28:22 -08:00
|
|
|
|
}
|
|
|
|
|
return feedsStories;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-30 16:18:11 -07:00
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Mark as read
|
2011-07-24 22:23:38 -07:00
|
|
|
|
|
2011-12-01 09:01:18 -08:00
|
|
|
|
- (void)markActiveFolderAllRead {
|
2017-11-05 22:07:43 -08:00
|
|
|
|
if ([storiesCollection.activeFolder isEqual:@"everything"] || [storiesCollection.activeFolder isEqual:@"infrequent"]) {
|
2011-12-01 09:01:18 -08:00
|
|
|
|
for (NSString *folderName in self.dictFoldersArray) {
|
|
|
|
|
for (id feedId in [self.dictFolders objectForKey:folderName]) {
|
|
|
|
|
[self markFeedAllRead:feedId];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2014-02-12 20:09:37 -08:00
|
|
|
|
for (id feedId in [self.dictFolders objectForKey:storiesCollection.activeFolder]) {
|
2011-12-01 09:01:18 -08:00
|
|
|
|
[self markFeedAllRead:feedId];
|
|
|
|
|
}
|
2011-11-09 09:51:42 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-10 18:28:22 -08:00
|
|
|
|
- (void)markFeedAllRead:(id)feedId {
|
|
|
|
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@",feedId];
|
2013-06-29 17:28:41 -07:00
|
|
|
|
NSMutableDictionary *unreadCounts = [NSMutableDictionary dictionary];
|
2011-11-10 18:28:22 -08:00
|
|
|
|
|
2013-06-29 17:28:41 -07:00
|
|
|
|
[unreadCounts setValue:[NSNumber numberWithInt:0] forKey:@"ps"];
|
|
|
|
|
[unreadCounts setValue:[NSNumber numberWithInt:0] forKey:@"nt"];
|
|
|
|
|
[unreadCounts setValue:[NSNumber numberWithInt:0] forKey:@"ng"];
|
|
|
|
|
|
|
|
|
|
[self.dictUnreadCounts setObject:unreadCounts forKey:feedIdStr];
|
2013-09-30 16:18:11 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)markFeedReadInCache:(NSArray *)feedIds {
|
|
|
|
|
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
|
|
|
|
|
dispatch_async(queue, ^{
|
|
|
|
|
[self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
|
|
|
|
[db executeUpdate:[NSString
|
|
|
|
|
stringWithFormat:@"UPDATE unread_counts SET ps = 0, nt = 0, ng = 0 "
|
|
|
|
|
"WHERE feed_id IN (\"%@\")",
|
|
|
|
|
[feedIds componentsJoinedByString:@"\",\""]]];
|
|
|
|
|
[db executeUpdate:[NSString
|
|
|
|
|
stringWithFormat:@"DELETE FROM unread_hashes "
|
|
|
|
|
"WHERE story_feed_id IN (\"%@\")",
|
|
|
|
|
[feedIds componentsJoinedByString:@"\",\""]]];
|
|
|
|
|
}];
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-08 19:33:11 -07:00
|
|
|
|
- (void)markFeedReadInCache:(NSArray *)feedIds cutoffTimestamp:(NSInteger)cutoff {
|
2015-10-28 17:02:45 -07:00
|
|
|
|
[self markFeedReadInCache:feedIds cutoffTimestamp:cutoff older:YES];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)markFeedReadInCache:(NSArray *)feedIds cutoffTimestamp:(NSInteger)cutoff older:(BOOL)older {
|
2013-10-08 19:33:11 -07:00
|
|
|
|
for (NSString *feedId in feedIds) {
|
2015-10-28 20:20:31 -07:00
|
|
|
|
NSString *feedIdString = [NSString stringWithFormat:@"%@", feedId];
|
|
|
|
|
NSDictionary *unreadCounts = [self.dictUnreadCounts objectForKey:feedIdString];
|
2013-10-08 19:33:11 -07:00
|
|
|
|
NSMutableDictionary *newUnreadCounts = [unreadCounts mutableCopy];
|
|
|
|
|
NSMutableArray *stories = [NSMutableArray array];
|
2015-10-28 17:02:45 -07:00
|
|
|
|
NSString *direction = older ? @"<" : @">";
|
2013-10-08 19:33:11 -07:00
|
|
|
|
|
|
|
|
|
[self.database inDatabase:^(FMDatabase *db) {
|
|
|
|
|
NSString *sql = [NSString stringWithFormat:@"SELECT * FROM stories s "
|
|
|
|
|
"INNER JOIN unread_hashes uh ON s.story_hash = uh.story_hash "
|
2015-10-28 17:02:45 -07:00
|
|
|
|
"WHERE s.story_feed_id = %@ AND s.story_timestamp %@ %ld",
|
2015-10-28 20:20:31 -07:00
|
|
|
|
feedIdString, direction, (long)cutoff];
|
2013-10-08 19:33:11 -07:00
|
|
|
|
FMResultSet *cursor = [db executeQuery:sql];
|
|
|
|
|
|
|
|
|
|
while ([cursor next]) {
|
|
|
|
|
NSDictionary *story = [cursor resultDictionary];
|
|
|
|
|
[stories addObject:[NSJSONSerialization
|
|
|
|
|
JSONObjectWithData:[[story objectForKey:@"story_json"]
|
|
|
|
|
dataUsingEncoding:NSUTF8StringEncoding]
|
2016-10-05 21:03:32 -07:00
|
|
|
|
options:0 error:nil]];
|
2013-10-08 19:33:11 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[cursor close];
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
for (NSDictionary *story in stories) {
|
|
|
|
|
NSInteger score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]];
|
|
|
|
|
if (score > 0) {
|
|
|
|
|
int unreads = MAX(0, [[newUnreadCounts objectForKey:@"ps"] intValue] - 1);
|
|
|
|
|
[newUnreadCounts setValue:[NSNumber numberWithInt:unreads] forKey:@"ps"];
|
|
|
|
|
} else if (score == 0) {
|
|
|
|
|
int unreads = MAX(0, [[newUnreadCounts objectForKey:@"nt"] intValue] - 1);
|
|
|
|
|
[newUnreadCounts setValue:[NSNumber numberWithInt:unreads] forKey:@"nt"];
|
|
|
|
|
} else if (score < 0) {
|
|
|
|
|
int unreads = MAX(0, [[newUnreadCounts objectForKey:@"ng"] intValue] - 1);
|
|
|
|
|
[newUnreadCounts setValue:[NSNumber numberWithInt:unreads] forKey:@"ng"];
|
|
|
|
|
}
|
2015-10-28 20:20:31 -07:00
|
|
|
|
[self.dictUnreadCounts setObject:newUnreadCounts forKey:feedIdString];
|
2013-10-08 19:33:11 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
|
|
|
|
for (NSDictionary *story in stories) {
|
|
|
|
|
NSMutableDictionary *newStory = [story mutableCopy];
|
|
|
|
|
[newStory setObject:[NSNumber numberWithInt:1] forKey:@"read_status"];
|
|
|
|
|
NSString *storyHash = [newStory objectForKey:@"story_hash"];
|
|
|
|
|
[db executeUpdate:@"UPDATE stories SET story_json = ? WHERE story_hash = ?",
|
|
|
|
|
[newStory JSONRepresentation],
|
|
|
|
|
storyHash];
|
|
|
|
|
}
|
|
|
|
|
NSString *deleteSql = [NSString
|
|
|
|
|
stringWithFormat:@"DELETE FROM unread_hashes "
|
|
|
|
|
"WHERE story_feed_id = \"%@\" "
|
|
|
|
|
"AND story_timestamp < %ld",
|
2015-10-28 20:20:31 -07:00
|
|
|
|
feedIdString, (long)cutoff];
|
2013-10-08 19:33:11 -07:00
|
|
|
|
[db executeUpdate:deleteSql];
|
|
|
|
|
[db executeUpdate:@"UPDATE unread_counts SET ps = ?, nt = ?, ng = ? WHERE feed_id = ?",
|
|
|
|
|
[newUnreadCounts objectForKey:@"ps"],
|
|
|
|
|
[newUnreadCounts objectForKey:@"nt"],
|
|
|
|
|
[newUnreadCounts objectForKey:@"ng"],
|
2015-10-28 20:20:31 -07:00
|
|
|
|
feedIdString];
|
2013-10-08 19:33:11 -07:00
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-26 10:48:02 -07:00
|
|
|
|
- (void)markStoryAsRead:(NSString *)storyHash inFeed:(NSString *)feed withCallback:(void(^)(void))callback {
|
2016-11-21 15:31:14 -08:00
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_story_hashes_as_read",
|
|
|
|
|
self.url];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
|
|
|
|
[params setObject:storyHash forKey:@"story_hash"];
|
|
|
|
|
|
2019-08-22 12:03:39 -07:00
|
|
|
|
[self POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
2016-11-21 15:31:14 -08:00
|
|
|
|
NSLog(@"Marked as read: %@", storyHash);
|
|
|
|
|
callback();
|
2017-04-03 16:07:01 -07:00
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
2016-11-21 15:31:14 -08:00
|
|
|
|
NSLog(@"Failed marked as read, queueing: %@", storyHash);
|
|
|
|
|
NSMutableDictionary *stories = [NSMutableDictionary dictionary];
|
|
|
|
|
[stories setObject:@[storyHash] forKey:feed];
|
|
|
|
|
[self queueReadStories:stories];
|
|
|
|
|
callback();
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-26 10:48:02 -07:00
|
|
|
|
- (void)markStoryAsStarred:(NSString *)storyHash withCallback:(void(^)(void))callback {
|
2016-11-21 16:33:24 -08:00
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_story_hash_as_starred",
|
2016-11-21 15:31:14 -08:00
|
|
|
|
self.url];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
|
|
|
|
[params setObject:storyHash forKey:@"story_hash"];
|
|
|
|
|
|
2019-08-22 12:03:39 -07:00
|
|
|
|
[self POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
2016-11-21 15:31:14 -08:00
|
|
|
|
NSLog(@"Marked as starred: %@", storyHash);
|
|
|
|
|
callback();
|
2017-04-03 16:07:01 -07:00
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
2016-11-21 15:31:14 -08:00
|
|
|
|
NSLog(@"Failed marked as starred: %@", storyHash);
|
|
|
|
|
callback();
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-08 19:33:11 -07:00
|
|
|
|
- (void)markStoriesRead:(NSDictionary *)stories inFeeds:(NSArray *)feeds cutoffTimestamp:(NSInteger)cutoff {
|
2013-09-30 16:18:11 -07:00
|
|
|
|
// Must be offline and marking all as read, so load all stories.
|
|
|
|
|
|
|
|
|
|
if (stories && [[stories allKeys] count]) {
|
|
|
|
|
[self queueReadStories:stories];
|
|
|
|
|
}
|
2013-06-29 17:28:41 -07:00
|
|
|
|
|
2013-09-30 16:18:11 -07:00
|
|
|
|
if ([feeds count]) {
|
|
|
|
|
NSMutableDictionary *feedsStories = [NSMutableDictionary dictionary];
|
|
|
|
|
|
|
|
|
|
[self.database inDatabase:^(FMDatabase *db) {
|
|
|
|
|
NSString *sql = [NSString stringWithFormat:@"SELECT u.story_feed_id, u.story_hash "
|
|
|
|
|
"FROM unread_hashes u WHERE u.story_feed_id IN (\"%@\")",
|
|
|
|
|
[feeds componentsJoinedByString:@"\",\""]];
|
2013-10-08 19:33:11 -07:00
|
|
|
|
if (cutoff) {
|
|
|
|
|
sql = [NSString stringWithFormat:@"%@ AND u.story_timestamp < %ld", sql, (long)cutoff];
|
|
|
|
|
}
|
2013-09-30 16:18:11 -07:00
|
|
|
|
FMResultSet *cursor = [db executeQuery:sql];
|
|
|
|
|
|
|
|
|
|
while ([cursor next]) {
|
|
|
|
|
NSDictionary *story = [cursor resultDictionary];
|
|
|
|
|
NSString *feedIdStr = [story objectForKey:@"story_feed_id"];
|
|
|
|
|
NSString *storyHash = [story objectForKey:@"story_hash"];
|
|
|
|
|
|
|
|
|
|
if (![feedsStories objectForKey:feedIdStr]) {
|
|
|
|
|
[feedsStories setObject:[NSMutableArray array] forKey:feedIdStr];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSMutableArray *stories = [feedsStories objectForKey:feedIdStr];
|
|
|
|
|
[stories addObject:storyHash];
|
|
|
|
|
}
|
2013-10-03 18:07:39 -07:00
|
|
|
|
|
|
|
|
|
[cursor close];
|
2013-09-30 16:18:11 -07:00
|
|
|
|
}];
|
|
|
|
|
[self queueReadStories:feedsStories];
|
2013-10-08 19:33:11 -07:00
|
|
|
|
if (cutoff) {
|
|
|
|
|
[self markFeedReadInCache:[feedsStories allKeys] cutoffTimestamp:cutoff];
|
|
|
|
|
} else {
|
|
|
|
|
for (NSString *feedId in [feedsStories allKeys]) {
|
|
|
|
|
[self markFeedAllRead:feedId];
|
|
|
|
|
}
|
|
|
|
|
[self markFeedReadInCache:[feedsStories allKeys]];
|
2013-09-30 16:18:11 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-10 18:28:22 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-05 14:13:49 -08:00
|
|
|
|
- (void)finishMarkAsRead:(NSDictionary *)story {
|
2015-03-12 19:43:39 -07:00
|
|
|
|
if (!storyPageControl.previousPage || !storyPageControl.currentPage || !storyPageControl.nextPage) return;
|
2014-03-05 14:13:49 -08:00
|
|
|
|
for (StoryDetailViewController *page in @[storyPageControl.previousPage,
|
|
|
|
|
storyPageControl.currentPage,
|
|
|
|
|
storyPageControl.nextPage]) {
|
2014-03-05 15:20:31 -08:00
|
|
|
|
if ([[page.activeStory objectForKey:@"story_hash"]
|
2015-10-06 18:52:58 -07:00
|
|
|
|
isEqualToString:[story objectForKey:@"story_hash"]] && page.isRecentlyUnread) {
|
2014-03-05 14:13:49 -08:00
|
|
|
|
page.isRecentlyUnread = NO;
|
|
|
|
|
[storyPageControl refreshHeaders];
|
|
|
|
|
}
|
2014-03-03 16:21:26 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-05 14:13:49 -08:00
|
|
|
|
- (void)finishMarkAsUnread:(NSDictionary *)story {
|
2015-11-27 22:53:49 -05:00
|
|
|
|
if (!storyPageControl.previousPage || !storyPageControl.currentPage || !storyPageControl.nextPage) return;
|
2014-03-05 14:13:49 -08:00
|
|
|
|
for (StoryDetailViewController *page in @[storyPageControl.previousPage,
|
|
|
|
|
storyPageControl.currentPage,
|
|
|
|
|
storyPageControl.nextPage]) {
|
2014-03-05 15:20:31 -08:00
|
|
|
|
if ([[page.activeStory objectForKey:@"story_hash"]
|
|
|
|
|
isEqualToString:[story objectForKey:@"story_hash"]]) {
|
2014-03-05 14:13:49 -08:00
|
|
|
|
page.isRecentlyUnread = YES;
|
|
|
|
|
[storyPageControl refreshHeaders];
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-03-05 15:29:22 -08:00
|
|
|
|
[storyPageControl setNextPreviousButtons];
|
|
|
|
|
originalStoryCount += 1;
|
2014-03-05 14:13:49 -08:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-19 18:15:36 -07:00
|
|
|
|
- (void)failedMarkAsUnread:(NSDictionary *)params {
|
|
|
|
|
if (![storyPageControl failedMarkAsUnread:params]) {
|
|
|
|
|
[feedDetailViewController failedMarkAsUnread:params];
|
|
|
|
|
[dashboardViewController.storiesModule failedMarkAsUnread:params];
|
2017-06-01 22:32:35 -07:00
|
|
|
|
[storyPageControl failedMarkAsUnread:params];
|
2014-03-05 14:13:49 -08:00
|
|
|
|
}
|
|
|
|
|
[feedDetailViewController reloadData];
|
2014-03-05 15:42:53 -08:00
|
|
|
|
[dashboardViewController.storiesModule reloadData];
|
2014-03-05 14:13:49 -08:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-19 18:15:36 -07:00
|
|
|
|
- (void)finishMarkAsSaved:(NSDictionary *)params {
|
|
|
|
|
[storyPageControl finishMarkAsSaved:params];
|
|
|
|
|
[feedDetailViewController finishMarkAsSaved:params];
|
2014-03-05 14:13:49 -08:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-19 18:15:36 -07:00
|
|
|
|
- (void)failedMarkAsSaved:(NSDictionary *)params {
|
|
|
|
|
if (![storyPageControl failedMarkAsSaved:params]) {
|
|
|
|
|
[feedDetailViewController failedMarkAsSaved:params];
|
|
|
|
|
[dashboardViewController.storiesModule failedMarkAsSaved:params];
|
2017-06-01 22:32:35 -07:00
|
|
|
|
[storyPageControl failedMarkAsSaved:params];
|
2014-03-05 14:13:49 -08:00
|
|
|
|
}
|
|
|
|
|
[feedDetailViewController reloadData];
|
2014-03-05 15:42:53 -08:00
|
|
|
|
[dashboardViewController.storiesModule reloadData];
|
2014-03-05 14:13:49 -08:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-19 18:15:36 -07:00
|
|
|
|
- (void)finishMarkAsUnsaved:(NSDictionary *)params {
|
|
|
|
|
[storyPageControl finishMarkAsUnsaved:params];
|
|
|
|
|
[feedDetailViewController finishMarkAsUnsaved:params];
|
2014-03-05 14:13:49 -08:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-19 18:15:36 -07:00
|
|
|
|
- (void)failedMarkAsUnsaved:(NSDictionary *)params {
|
|
|
|
|
if (![storyPageControl failedMarkAsUnsaved:params]) {
|
|
|
|
|
[feedDetailViewController failedMarkAsUnsaved:params];
|
|
|
|
|
[dashboardViewController.storiesModule failedMarkAsUnsaved:params];
|
2017-06-01 22:32:35 -07:00
|
|
|
|
[storyPageControl failedMarkAsUnsaved:params];
|
2014-03-03 16:21:26 -08:00
|
|
|
|
}
|
2014-03-05 14:13:49 -08:00
|
|
|
|
[feedDetailViewController reloadData];
|
2014-03-05 15:42:53 -08:00
|
|
|
|
[dashboardViewController.storiesModule reloadData];
|
2014-03-03 16:21:26 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-16 15:36:58 -08:00
|
|
|
|
|
|
|
|
|
- (NSInteger)adjustSavedStoryCount:(NSString *)tagName direction:(NSInteger)direction {
|
|
|
|
|
NSString *savedTagId = [NSString stringWithFormat:@"saved:%@", tagName];
|
|
|
|
|
NSMutableDictionary *newTag = [[self.dictSavedStoryTags objectForKey:savedTagId] mutableCopy];
|
|
|
|
|
if (!newTag) {
|
|
|
|
|
newTag = [@{@"ps": [NSNumber numberWithInt:0],
|
|
|
|
|
@"feed_title": tagName
|
|
|
|
|
} mutableCopy];
|
|
|
|
|
}
|
|
|
|
|
NSInteger newCount = [[newTag objectForKey:@"ps"] integerValue] + direction;
|
|
|
|
|
[newTag setObject:[NSNumber numberWithInteger:newCount] forKey:@"ps"];
|
|
|
|
|
NSMutableDictionary *savedStoryDict = [[NSMutableDictionary alloc] init];
|
|
|
|
|
for (NSString *tagId in [self.dictSavedStoryTags allKeys]) {
|
|
|
|
|
if ([tagId isEqualToString:savedTagId]) {
|
|
|
|
|
if (newCount > 0) {
|
|
|
|
|
[savedStoryDict setObject:newTag forKey:tagId];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
[savedStoryDict setObject:[self.dictSavedStoryTags objectForKey:tagId]
|
|
|
|
|
forKey:tagId];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If adding a tag, it won't already be in dictSavedStoryTags
|
|
|
|
|
if (![self.dictSavedStoryTags objectForKey:savedStoryDict] && newCount > 0) {
|
|
|
|
|
[savedStoryDict setObject:newTag forKey:savedTagId];
|
|
|
|
|
}
|
|
|
|
|
self.dictSavedStoryTags = savedStoryDict;
|
|
|
|
|
|
|
|
|
|
return newCount;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-04 17:53:27 -08:00
|
|
|
|
- (NSArray *)updateStarredStoryCounts:(NSDictionary *)results {
|
|
|
|
|
if ([results objectForKey:@"starred_count"]) {
|
|
|
|
|
self.savedStoriesCount = [[results objectForKey:@"starred_count"] intValue];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!self.savedStoriesCount) return [[NSArray alloc] init];
|
2016-03-26 21:27:25 -07:00
|
|
|
|
|
|
|
|
|
NSMutableDictionary *savedStoryDict = [NSMutableDictionary dictionary];
|
|
|
|
|
NSMutableDictionary *savedStoryFeedCounts = [NSMutableDictionary dictionary];
|
2014-11-04 17:53:27 -08:00
|
|
|
|
NSMutableArray *savedStories = [NSMutableArray array];
|
2014-12-16 15:36:58 -08:00
|
|
|
|
|
|
|
|
|
if (![results objectForKey:@"starred_counts"] ||
|
|
|
|
|
[[results objectForKey:@"starred_counts"] isKindOfClass:[NSNull class]]) {
|
|
|
|
|
return savedStories;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-04 17:53:27 -08:00
|
|
|
|
for (NSDictionary *userTag in [results objectForKey:@"starred_counts"]) {
|
2016-03-26 21:27:25 -07:00
|
|
|
|
id feedId = [userTag objectForKey:@"feed_id"];
|
|
|
|
|
|
|
|
|
|
if (![feedId isKindOfClass:[NSNull class]]) {
|
|
|
|
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@", feedId];
|
|
|
|
|
savedStoryFeedCounts[feedIdStr] = userTag[@"count"];
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-04 17:53:27 -08:00
|
|
|
|
if ([[userTag objectForKey:@"tag"] isKindOfClass:[NSNull class]] ||
|
|
|
|
|
[[userTag objectForKey:@"tag"] isEqualToString:@""]) continue;
|
|
|
|
|
NSString *savedTagId = [NSString stringWithFormat:@"saved:%@", [userTag objectForKey:@"tag"]];
|
|
|
|
|
NSDictionary *savedTag = @{@"ps": [userTag objectForKey:@"count"],
|
|
|
|
|
@"feed_title": [userTag objectForKey:@"tag"],
|
|
|
|
|
@"id": [userTag objectForKey:@"tag"],
|
|
|
|
|
@"tag": [userTag objectForKey:@"tag"]};
|
|
|
|
|
[savedStories addObject:savedTagId];
|
|
|
|
|
[savedStoryDict setObject:savedTag forKey:savedTagId];
|
|
|
|
|
[self.dictUnreadCounts setObject:@{@"ps": [userTag objectForKey:@"count"],
|
|
|
|
|
@"nt": [NSNumber numberWithInt:0],
|
|
|
|
|
@"ng": [NSNumber numberWithInt:0]}
|
|
|
|
|
forKey:savedTagId];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.dictSavedStoryTags = savedStoryDict;
|
2016-03-26 21:27:25 -07:00
|
|
|
|
self.dictSavedStoryFeedCounts = savedStoryFeedCounts;
|
2014-11-04 17:53:27 -08:00
|
|
|
|
|
|
|
|
|
return savedStories;
|
|
|
|
|
}
|
2014-03-03 16:21:26 -08:00
|
|
|
|
|
2020-05-27 21:26:44 -07:00
|
|
|
|
- (NSArray *)updateSavedSearches:(NSDictionary *)results {
|
|
|
|
|
NSArray *savedSearches = results[@"saved_searches"];
|
2020-06-11 21:02:22 -07:00
|
|
|
|
NSInteger count = 0;
|
2020-05-27 21:26:44 -07:00
|
|
|
|
NSMutableArray *feedIds = [NSMutableArray arrayWithCapacity:savedSearches.count];
|
|
|
|
|
|
|
|
|
|
for (NSDictionary *search in savedSearches) {
|
2020-06-11 21:02:22 -07:00
|
|
|
|
NSString *feedStr = search[@"feed_id"];
|
2020-05-27 21:26:44 -07:00
|
|
|
|
NSString *prefix = @"feed:";
|
|
|
|
|
|
2020-06-11 21:02:22 -07:00
|
|
|
|
if ([feedStr hasPrefix:prefix]) {
|
|
|
|
|
feedStr = [feedStr substringFromIndex:prefix.length];
|
2020-05-27 21:26:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-11 21:02:22 -07:00
|
|
|
|
if ([feedStr isEqualToString:@"river:"]) {
|
|
|
|
|
feedStr = @"river:everything";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSString *feedId = [NSString stringWithFormat:@"%@?%@", feedStr, search[@"query"]];
|
|
|
|
|
|
2020-05-27 21:26:44 -07:00
|
|
|
|
[feedIds addObject:feedId];
|
2020-06-11 21:02:22 -07:00
|
|
|
|
count++;
|
2020-05-27 21:26:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-11 21:02:22 -07:00
|
|
|
|
self.savedSearchesCount = count;
|
2020-05-27 21:26:44 -07:00
|
|
|
|
|
|
|
|
|
return feedIds;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-26 22:34:12 -07:00
|
|
|
|
- (void)renameFeed:(NSString *)newTitle {
|
|
|
|
|
NSMutableDictionary *newActiveFeed = [storiesCollection.activeFeed mutableCopy];
|
|
|
|
|
[newActiveFeed setObject:newTitle forKey:@"feed_title"];
|
|
|
|
|
storiesCollection.activeFeed = newActiveFeed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)renameFolder:(NSString *)newTitle {
|
|
|
|
|
storiesCollection.activeFolder = newTitle;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-13 21:54:32 -08:00
|
|
|
|
- (void)showMarkReadMenuWithFeedIds:(NSArray *)feedIds collectionTitle:(NSString *)collectionTitle visibleUnreadCount:(NSInteger)visibleUnreadCount barButtonItem:(UIBarButtonItem *)barButtonItem completionHandler:(void (^)(BOOL marked))completionHandler {
|
2016-01-08 21:53:16 -08:00
|
|
|
|
[self showMarkReadMenuWithFeedIds:feedIds collectionTitle:collectionTitle visibleUnreadCount:visibleUnreadCount olderNewerCollection:nil olderNewerStory:nil barButtonItem:barButtonItem sourceView:nil sourceRect:CGRectZero extraItems:nil completionHandler:completionHandler];
|
2015-11-13 21:54:32 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showMarkReadMenuWithFeedIds:(NSArray *)feedIds collectionTitle:(NSString *)collectionTitle sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect completionHandler:(void (^)(BOOL marked))completionHandler {
|
2016-01-08 21:53:16 -08:00
|
|
|
|
[self showMarkReadMenuWithFeedIds:feedIds collectionTitle:collectionTitle visibleUnreadCount:0 olderNewerCollection:nil olderNewerStory:nil barButtonItem:nil sourceView:sourceView sourceRect:sourceRect extraItems:nil completionHandler:completionHandler];
|
2015-11-13 21:54:32 -08:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-08 21:53:16 -08:00
|
|
|
|
- (void)showMarkOlderNewerReadMenuWithStoriesCollection:(StoriesCollection *)olderNewerCollection story:(NSDictionary *)olderNewerStory sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect extraItems:(NSArray *)extraItems completionHandler:(void (^)(BOOL marked))completionHandler {
|
|
|
|
|
[self showMarkReadMenuWithFeedIds:nil collectionTitle:nil visibleUnreadCount:0 olderNewerCollection:storiesCollection olderNewerStory:olderNewerStory barButtonItem:nil sourceView:sourceView sourceRect:sourceRect extraItems:extraItems completionHandler:completionHandler];
|
2015-11-29 18:08:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-08 21:53:16 -08:00
|
|
|
|
- (void)showMarkReadMenuWithFeedIds:(NSArray *)feedIds collectionTitle:(NSString *)collectionTitle visibleUnreadCount:(NSInteger)visibleUnreadCount olderNewerCollection:(StoriesCollection *)olderNewerCollection olderNewerStory:(NSDictionary *)olderNewerStory barButtonItem:(UIBarButtonItem *)barButtonItem sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect extraItems:(NSArray *)extraItems completionHandler:(void (^)(BOOL marked))completionHandler {
|
2015-11-13 21:54:32 -08:00
|
|
|
|
if (!self.markReadMenuViewController) {
|
|
|
|
|
self.markReadMenuViewController = [MarkReadMenuViewController new];
|
|
|
|
|
self.markReadMenuViewController.modalPresentationStyle = UIModalPresentationPopover;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.markReadMenuViewController.collectionTitle = collectionTitle;
|
|
|
|
|
self.markReadMenuViewController.feedIds = feedIds;
|
|
|
|
|
self.markReadMenuViewController.visibleUnreadCount = visibleUnreadCount;
|
2015-11-29 18:08:44 -08:00
|
|
|
|
self.markReadMenuViewController.olderNewerStoriesCollection = olderNewerCollection;
|
|
|
|
|
self.markReadMenuViewController.olderNewerStory = olderNewerStory;
|
2016-01-08 21:53:16 -08:00
|
|
|
|
self.markReadMenuViewController.extraItems = extraItems;
|
2015-11-13 21:54:32 -08:00
|
|
|
|
self.markReadMenuViewController.completionHandler = completionHandler;
|
2019-09-21 14:51:55 -07:00
|
|
|
|
|
|
|
|
|
if (@available(iOS 13.0, *)) {
|
|
|
|
|
self.markReadMenuViewController.menuTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAlways;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-19 13:19:06 -04:00
|
|
|
|
[self showPopoverWithViewController:self.markReadMenuViewController contentSize:CGSizeZero barButtonItem:barButtonItem sourceView:sourceView sourceRect:sourceRect permittedArrowDirections:UIPopoverArrowDirectionAny];
|
2015-11-13 21:54:32 -08:00
|
|
|
|
}
|
|
|
|
|
|
2015-12-15 12:37:18 -08:00
|
|
|
|
- (void)showPopoverWithViewController:(UIViewController *)viewController contentSize:(CGSize)contentSize barButtonItem:(UIBarButtonItem *)barButtonItem {
|
2016-03-18 21:29:42 -07:00
|
|
|
|
[self showPopoverWithViewController:viewController contentSize:contentSize barButtonItem:barButtonItem sourceView:nil sourceRect:CGRectZero permittedArrowDirections:UIPopoverArrowDirectionAny];
|
2015-12-15 12:37:18 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showPopoverWithViewController:(UIViewController *)viewController contentSize:(CGSize)contentSize sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect {
|
2016-03-18 21:29:42 -07:00
|
|
|
|
[self showPopoverWithViewController:viewController contentSize:contentSize barButtonItem:nil sourceView:sourceView sourceRect:sourceRect permittedArrowDirections:UIPopoverArrowDirectionAny];
|
2015-12-15 12:37:18 -08:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-18 21:29:42 -07:00
|
|
|
|
- (void)showPopoverWithViewController:(UIViewController *)viewController contentSize:(CGSize)contentSize sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections {
|
|
|
|
|
[self showPopoverWithViewController:viewController contentSize:contentSize barButtonItem:nil sourceView:sourceView sourceRect:sourceRect permittedArrowDirections:permittedArrowDirections];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showPopoverWithViewController:(UIViewController *)viewController contentSize:(CGSize)contentSize barButtonItem:(UIBarButtonItem *)barButtonItem sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections {
|
2015-12-15 12:37:18 -08:00
|
|
|
|
if (viewController == self.navigationControllerForPopover.presentedViewController) {
|
|
|
|
|
return; // nothing to do, already showing this controller
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self hidePopoverAnimated:YES];
|
|
|
|
|
|
|
|
|
|
viewController.modalPresentationStyle = UIModalPresentationPopover;
|
|
|
|
|
viewController.preferredContentSize = contentSize;
|
2016-04-11 20:04:54 -07:00
|
|
|
|
|
|
|
|
|
if ([viewController respondsToSelector:@selector(addKeyCommand:)]) {
|
|
|
|
|
[viewController addKeyCommand:[UIKeyCommand keyCommandWithInput:@"." modifierFlags:UIKeyModifierCommand action:@selector(hidePopover)]];
|
|
|
|
|
[viewController addKeyCommand:[UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:0 action:@selector(hidePopover)]];
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-15 12:37:18 -08:00
|
|
|
|
UIPopoverPresentationController *popoverPresentationController = viewController.popoverPresentationController;
|
|
|
|
|
popoverPresentationController.delegate = self;
|
|
|
|
|
popoverPresentationController.backgroundColor = UIColorFromRGB(NEWSBLUR_WHITE_COLOR);
|
2016-03-18 21:29:42 -07:00
|
|
|
|
popoverPresentationController.permittedArrowDirections = permittedArrowDirections;
|
2015-12-15 12:37:18 -08:00
|
|
|
|
|
|
|
|
|
if (barButtonItem) {
|
|
|
|
|
popoverPresentationController.barButtonItem = barButtonItem;
|
|
|
|
|
} else {
|
|
|
|
|
popoverPresentationController.sourceView = sourceView;
|
|
|
|
|
popoverPresentationController.sourceRect = sourceRect;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-02 21:21:30 -08:00
|
|
|
|
[self.navigationControllerForPopover presentViewController:viewController animated:YES completion:^{
|
|
|
|
|
popoverPresentationController.passthroughViews = nil;
|
2016-03-19 13:19:06 -04:00
|
|
|
|
// NSLog(@"%@ canBecomeFirstResponder? %d", viewController, viewController.canBecomeFirstResponder);
|
|
|
|
|
[viewController becomeFirstResponder];
|
2016-03-02 21:21:30 -08:00
|
|
|
|
}];
|
2015-12-15 12:37:18 -08:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-07 21:12:58 -08:00
|
|
|
|
- (void)hidePopoverAnimated:(BOOL)animated completion:(void (^)(void))completion {
|
|
|
|
|
UIViewController *presentedViewController = self.navigationControllerForPopover.presentedViewController;
|
|
|
|
|
if (!presentedViewController || presentedViewController.presentationController.presentationStyle != UIModalPresentationPopover) {
|
|
|
|
|
if (completion) {
|
|
|
|
|
completion();
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[presentedViewController dismissViewControllerAnimated:animated completion:completion];
|
2016-03-18 23:01:48 -04:00
|
|
|
|
[self.navigationController.topViewController becomeFirstResponder];
|
2016-01-07 21:12:58 -08:00
|
|
|
|
}
|
|
|
|
|
|
2015-12-15 12:37:18 -08:00
|
|
|
|
- (BOOL)hidePopoverAnimated:(BOOL)animated {
|
|
|
|
|
UIViewController *presentedViewController = self.navigationControllerForPopover.presentedViewController;
|
|
|
|
|
if (!presentedViewController || presentedViewController.presentationController.presentationStyle != UIModalPresentationPopover)
|
|
|
|
|
return NO;
|
|
|
|
|
|
|
|
|
|
[presentedViewController dismissViewControllerAnimated:animated completion:nil];
|
2016-03-18 23:01:48 -04:00
|
|
|
|
[self.navigationController.topViewController becomeFirstResponder];
|
2015-12-15 12:37:18 -08:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)hidePopover {
|
|
|
|
|
[self hidePopoverAnimated:YES];
|
|
|
|
|
[self.modalNavigationController dismissViewControllerAnimated:YES completion:nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (UINavigationController *)navigationControllerForPopover {
|
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
|
|
|
return self.masterContainerViewController.masterNavigationController;
|
|
|
|
|
} else {
|
|
|
|
|
return self.navigationController;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-30 16:18:11 -07:00
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Story functions
|
|
|
|
|
|
2014-03-21 16:23:51 -07:00
|
|
|
|
+ (int)computeStoryScore:(NSDictionary *)intelligence {
|
|
|
|
|
int score = 0;
|
2011-07-26 08:37:10 -07:00
|
|
|
|
int title = [[intelligence objectForKey:@"title"] intValue];
|
|
|
|
|
int author = [[intelligence objectForKey:@"author"] intValue];
|
|
|
|
|
int tags = [[intelligence objectForKey:@"tags"] intValue];
|
|
|
|
|
|
|
|
|
|
int score_max = MAX(title, MAX(author, tags));
|
|
|
|
|
int score_min = MIN(title, MIN(author, tags));
|
2011-07-24 20:07:38 -07:00
|
|
|
|
|
|
|
|
|
if (score_max > 0) score = score_max;
|
|
|
|
|
else if (score_min < 0) score = score_min;
|
2010-11-22 10:44:52 -05:00
|
|
|
|
|
2014-03-21 16:23:51 -07:00
|
|
|
|
if (score == 0) score = [[intelligence objectForKey:@"feed"] intValue];
|
2011-07-24 20:07:38 -07:00
|
|
|
|
|
2011-07-29 09:06:17 -07:00
|
|
|
|
// NSLog(@"%d/%d -- %d: %@", score_max, score_min, score, intelligence);
|
2010-11-22 10:44:52 -05:00
|
|
|
|
return score;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-29 17:28:41 -07:00
|
|
|
|
#pragma mark - Feed Management
|
2011-12-04 21:09:16 -08:00
|
|
|
|
|
|
|
|
|
- (NSString *)extractParentFolderName:(NSString *)folderName {
|
2012-10-15 14:57:20 -07:00
|
|
|
|
if ([folderName containsString:@"Top Level"] ||
|
2017-11-05 22:07:43 -08:00
|
|
|
|
[folderName isEqual:@"everything"] ||
|
|
|
|
|
[folderName isEqual:@"infrequent"]) {
|
2011-12-04 21:09:16 -08:00
|
|
|
|
folderName = @"";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([folderName containsString:@" - "]) {
|
2013-09-25 17:43:00 -07:00
|
|
|
|
NSInteger lastFolderLoc = [folderName rangeOfString:@" - "
|
|
|
|
|
options:NSBackwardsSearch].location;
|
2011-12-04 21:09:16 -08:00
|
|
|
|
folderName = [folderName substringToIndex:lastFolderLoc];
|
|
|
|
|
} else {
|
|
|
|
|
folderName = @"— Top Level —";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return folderName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)extractFolderName:(NSString *)folderName {
|
2012-10-15 14:57:20 -07:00
|
|
|
|
if ([folderName containsString:@"Top Level"] ||
|
2017-11-05 22:07:43 -08:00
|
|
|
|
[folderName isEqual:@"everything"] ||
|
|
|
|
|
[folderName isEqual:@"infrequent"]) {
|
2011-12-04 21:09:16 -08:00
|
|
|
|
folderName = @"";
|
|
|
|
|
}
|
|
|
|
|
if ([folderName containsString:@" - "]) {
|
2013-09-25 17:43:00 -07:00
|
|
|
|
NSInteger folder_loc = [folderName rangeOfString:@" - "
|
|
|
|
|
options:NSBackwardsSearch].location;
|
2011-12-04 21:09:16 -08:00
|
|
|
|
folderName = [folderName substringFromIndex:(folder_loc + 3)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return folderName;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-30 18:05:34 -07:00
|
|
|
|
- (NSArray *)parentFoldersForFeed:(NSString *)feedId {
|
|
|
|
|
NSMutableArray *folderNames = [[NSMutableArray alloc] init];
|
|
|
|
|
|
|
|
|
|
for (NSString *folderName in self.dictFoldersArray) {
|
|
|
|
|
NSArray *folder = [self.dictFolders objectForKey:folderName];
|
|
|
|
|
if ([folder containsObject:feedId]) {
|
|
|
|
|
[folderNames addObject:[self extractFolderName:folderName]];
|
|
|
|
|
[folderNames addObject:[self extractParentFolderName:folderName]];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NSMutableArray *uniqueFolderNames = [[NSMutableArray alloc] init];
|
|
|
|
|
for (NSString *folderName in folderNames) {
|
|
|
|
|
if ([uniqueFolderNames containsObject:folderName]) continue;
|
|
|
|
|
if ([folderName containsString:@"Top Level"]) continue;
|
|
|
|
|
if ([folderName length] < 1) continue;
|
|
|
|
|
|
|
|
|
|
[uniqueFolderNames addObject:folderName];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return uniqueFolderNames;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-11 21:02:22 -07:00
|
|
|
|
- (NSString *)feedIdWithoutSearchQuery:(NSString *)feedId {
|
|
|
|
|
NSRange range = [feedId rangeOfString:@"?"];
|
|
|
|
|
|
|
|
|
|
if (range.location == NSNotFound) {
|
|
|
|
|
return feedId;
|
|
|
|
|
} else {
|
|
|
|
|
return [feedId substringToIndex:range.location];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)searchQueryForFeedId:(NSString *)feedId {
|
|
|
|
|
NSRange range = [feedId rangeOfString:@"?"];
|
|
|
|
|
|
|
|
|
|
if (range.location == NSNotFound) {
|
|
|
|
|
return nil;
|
|
|
|
|
} else {
|
|
|
|
|
return [feedId substringFromIndex:range.location + range.length];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)searchFolderForFeedId:(NSString *)feedId {
|
|
|
|
|
NSString *prefix = @"river:";
|
|
|
|
|
|
|
|
|
|
if (![feedId hasPrefix:prefix]) {
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [[self feedIdWithoutSearchQuery:feedId] substringFromIndex:prefix.length];
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-24 15:06:50 -07:00
|
|
|
|
- (NSDictionary *)getFeedWithId:(id)feedId {
|
|
|
|
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@", feedId];
|
|
|
|
|
|
|
|
|
|
return [self getFeed:feedIdStr];
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-28 11:34:06 -08:00
|
|
|
|
- (NSDictionary *)getFeed:(NSString *)feedId {
|
2020-06-11 21:02:22 -07:00
|
|
|
|
feedId = [self feedIdWithoutSearchQuery:feedId];
|
|
|
|
|
|
2012-12-28 11:34:06 -08:00
|
|
|
|
NSDictionary *feed;
|
2014-02-12 20:09:37 -08:00
|
|
|
|
if (storiesCollection.isSocialView ||
|
|
|
|
|
storiesCollection.isSocialRiverView ||
|
|
|
|
|
[feedId startsWith:@"social:"]) {
|
2012-12-28 11:34:06 -08:00
|
|
|
|
feed = [self.dictActiveFeeds objectForKey:feedId];
|
|
|
|
|
// this is to catch when a user is already subscribed
|
2013-12-12 18:03:56 -08:00
|
|
|
|
if (!feed) {
|
|
|
|
|
feed = [self.dictSocialFeeds objectForKey:feedId];
|
|
|
|
|
}
|
2012-12-28 11:34:06 -08:00
|
|
|
|
if (!feed) {
|
|
|
|
|
feed = [self.dictFeeds objectForKey:feedId];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
feed = [self.dictFeeds objectForKey:feedId];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return feed;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-05 14:13:49 -08:00
|
|
|
|
- (NSDictionary *)getStory:(NSString *)storyHash {
|
|
|
|
|
for (NSDictionary *story in storiesCollection.activeFeedStories) {
|
|
|
|
|
if ([[story objectForKey:@"story_hash"] isEqualToString:storyHash]) {
|
|
|
|
|
return story;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-04 21:09:16 -08:00
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Feed Templates
|
|
|
|
|
|
2013-02-21 12:19:15 -08:00
|
|
|
|
+ (void)fillGradient:(CGRect)r startColor:(UIColor *)startColor endColor:(UIColor *)endColor {
|
|
|
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
|
|
|
UIGraphicsPushContext(context);
|
|
|
|
|
|
|
|
|
|
CGGradientRef gradient;
|
2013-02-14 15:36:21 -08:00
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
|
|
|
CGFloat locations[2] = {0.0f, 1.0f};
|
|
|
|
|
CGFloat startRed, startGreen, startBlue, startAlpha;
|
|
|
|
|
CGFloat endRed, endGreen, endBlue, endAlpha;
|
|
|
|
|
|
|
|
|
|
[startColor getRed:&startRed green:&startGreen blue:&startBlue alpha:&startAlpha];
|
|
|
|
|
[endColor getRed:&endRed green:&endGreen blue:&endBlue alpha:&endAlpha];
|
|
|
|
|
|
|
|
|
|
CGFloat components[8] = {
|
|
|
|
|
startRed, startGreen, startBlue, startAlpha,
|
|
|
|
|
endRed, endGreen, endBlue, endAlpha
|
|
|
|
|
};
|
2013-02-21 12:19:15 -08:00
|
|
|
|
gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 2);
|
2013-02-14 15:36:21 -08:00
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
2013-02-15 16:41:20 -08:00
|
|
|
|
|
2013-02-14 15:36:21 -08:00
|
|
|
|
CGPoint startPoint = CGPointMake(CGRectGetMinX(r), r.origin.y);
|
|
|
|
|
CGPoint endPoint = CGPointMake(startPoint.x, r.origin.y + r.size.height);
|
|
|
|
|
|
|
|
|
|
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
|
2013-02-21 12:19:15 -08:00
|
|
|
|
CGGradientRelease(gradient);
|
2013-02-14 15:36:21 -08:00
|
|
|
|
UIGraphicsPopContext();
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 19:31:08 -07:00
|
|
|
|
+ (UIView *)makeSimpleGradientView:(CGRect)rect startColor:(UIColor *)startColor endColor:(UIColor *)endColor {
|
|
|
|
|
UIView *gradientView = [[UIView alloc] initWithFrame:rect];
|
|
|
|
|
|
|
|
|
|
CAGradientLayer *gradient = [CAGradientLayer layer];
|
|
|
|
|
gradient.frame = CGRectMake(0, 0, rect.size.width, rect.size.height);
|
|
|
|
|
gradient.colors = @[(id)[startColor CGColor], (id)[endColor CGColor]];
|
|
|
|
|
|
|
|
|
|
[gradientView.layer addSublayer:gradient];
|
|
|
|
|
|
|
|
|
|
return gradientView;
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-16 20:24:11 -07:00
|
|
|
|
+ (UIColor *)faviconColor:(NSString *)colorString {
|
2015-09-28 20:01:17 -07:00
|
|
|
|
if ([colorString class] == [NSNull class] || !colorString) {
|
2015-09-16 20:24:11 -07:00
|
|
|
|
colorString = @"505050";
|
|
|
|
|
}
|
|
|
|
|
unsigned int color = 0;
|
|
|
|
|
NSScanner *scanner = [NSScanner scannerWithString:colorString];
|
|
|
|
|
[scanner scanHexInt:&color];
|
|
|
|
|
|
2015-12-07 16:09:49 -08:00
|
|
|
|
return UIColorFromFixedRGB(color);
|
2015-09-16 20:24:11 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (UIView *)makeGradientView:(CGRect)rect startColor:(NSString *)start endColor:(NSString *)end borderColor:(NSString *)borderColor {
|
2012-07-15 15:06:06 -07:00
|
|
|
|
UIView *gradientView = [[UIView alloc] initWithFrame:rect];
|
2011-10-27 19:05:38 -07:00
|
|
|
|
|
|
|
|
|
CAGradientLayer *gradient = [CAGradientLayer layer];
|
2011-10-30 18:53:10 -07:00
|
|
|
|
gradient.frame = CGRectMake(0, 1, rect.size.width, rect.size.height-1);
|
2012-06-10 13:21:07 -07:00
|
|
|
|
gradient.opacity = 0.7;
|
2015-09-28 20:01:17 -07:00
|
|
|
|
if ([start class] == [NSNull class] || !start) {
|
2011-10-27 19:05:38 -07:00
|
|
|
|
start = @"505050";
|
|
|
|
|
}
|
2015-09-28 20:01:17 -07:00
|
|
|
|
if ([end class] == [NSNull class] || !end) {
|
2011-10-27 19:05:38 -07:00
|
|
|
|
end = @"303030";
|
|
|
|
|
}
|
2015-09-16 20:24:11 -07:00
|
|
|
|
gradient.colors = [NSArray arrayWithObjects:(id)[[self faviconColor:start] CGColor], (id)[[self faviconColor:end] CGColor], nil];
|
2011-10-28 18:29:33 -07:00
|
|
|
|
|
|
|
|
|
CALayer *whiteBackground = [CALayer layer];
|
2011-10-30 18:53:10 -07:00
|
|
|
|
whiteBackground.frame = CGRectMake(0, 1, rect.size.width, rect.size.height-1);
|
2015-12-07 16:09:49 -08:00
|
|
|
|
whiteBackground.backgroundColor = [UIColorFromRGB(NEWSBLUR_WHITE_COLOR) colorWithAlphaComponent:0.7].CGColor;
|
2011-10-28 18:29:33 -07:00
|
|
|
|
[gradientView.layer addSublayer:whiteBackground];
|
|
|
|
|
|
2011-10-27 19:05:38 -07:00
|
|
|
|
[gradientView.layer addSublayer:gradient];
|
2011-10-28 18:29:33 -07:00
|
|
|
|
|
|
|
|
|
CALayer *topBorder = [CALayer layer];
|
2011-10-30 18:53:10 -07:00
|
|
|
|
topBorder.frame = CGRectMake(0, 1, rect.size.width, 1);
|
2015-09-16 20:24:11 -07:00
|
|
|
|
topBorder.backgroundColor = [[self faviconColor:borderColor] colorWithAlphaComponent:0.7].CGColor;
|
2011-10-30 15:06:25 -07:00
|
|
|
|
topBorder.opacity = 1;
|
2011-10-28 18:29:33 -07:00
|
|
|
|
[gradientView.layer addSublayer:topBorder];
|
|
|
|
|
|
|
|
|
|
CALayer *bottomBorder = [CALayer layer];
|
|
|
|
|
bottomBorder.frame = CGRectMake(0, rect.size.height-1, rect.size.width, 1);
|
2015-09-16 20:24:11 -07:00
|
|
|
|
bottomBorder.backgroundColor = [[self faviconColor:borderColor] colorWithAlphaComponent:0.7].CGColor;
|
2011-10-30 15:06:25 -07:00
|
|
|
|
bottomBorder.opacity = 1;
|
2011-10-28 18:29:33 -07:00
|
|
|
|
[gradientView.layer addSublayer:bottomBorder];
|
|
|
|
|
|
|
|
|
|
return gradientView;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (UIView *)makeFeedTitleGradient:(NSDictionary *)feed withRect:(CGRect)rect {
|
|
|
|
|
UIView *gradientView;
|
2014-02-12 20:09:37 -08:00
|
|
|
|
if (storiesCollection.isRiverView ||
|
|
|
|
|
storiesCollection.isSocialView ||
|
2014-05-20 15:29:16 -07:00
|
|
|
|
storiesCollection.isSocialRiverView ||
|
2014-10-22 17:03:48 -07:00
|
|
|
|
storiesCollection.isSavedView ||
|
|
|
|
|
storiesCollection.isReadView) {
|
2011-10-28 18:29:33 -07:00
|
|
|
|
gradientView = [NewsBlurAppDelegate
|
|
|
|
|
makeGradientView:rect
|
2012-06-30 21:28:48 -07:00
|
|
|
|
startColor:[feed objectForKey:@"favicon_fade"]
|
2015-09-16 20:24:11 -07:00
|
|
|
|
endColor:[feed objectForKey:@"favicon_color"]
|
|
|
|
|
borderColor:[feed objectForKey:@"favicon_border"]];
|
|
|
|
|
|
2012-07-15 15:06:06 -07:00
|
|
|
|
UILabel *titleLabel = [[UILabel alloc] init];
|
2011-10-28 18:29:33 -07:00
|
|
|
|
titleLabel.text = [feed objectForKey:@"feed_title"];
|
|
|
|
|
titleLabel.backgroundColor = [UIColor clearColor];
|
2013-06-17 11:50:13 -07:00
|
|
|
|
titleLabel.textAlignment = NSTextAlignmentLeft;
|
|
|
|
|
titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
2011-12-04 21:09:16 -08:00
|
|
|
|
titleLabel.numberOfLines = 1;
|
2011-10-28 18:29:33 -07:00
|
|
|
|
titleLabel.font = [UIFont fontWithName:@"Helvetica-Bold" size:11.0];
|
|
|
|
|
titleLabel.shadowOffset = CGSizeMake(0, 1);
|
|
|
|
|
if ([[feed objectForKey:@"favicon_text_color"] class] != [NSNull class]) {
|
2015-09-16 20:24:11 -07:00
|
|
|
|
BOOL lightText = [[feed objectForKey:@"favicon_text_color"]
|
|
|
|
|
isEqualToString:@"white"];
|
|
|
|
|
UIColor *fadeColor = [NewsBlurAppDelegate faviconColor:[feed objectForKey:@"favicon_fade"]];
|
|
|
|
|
UIColor *borderColor = [NewsBlurAppDelegate faviconColor:[feed objectForKey:@"favicon_border"]];
|
|
|
|
|
|
|
|
|
|
titleLabel.textColor = lightText ?
|
2016-01-27 21:58:55 -08:00
|
|
|
|
UIColorFromFixedRGB(NEWSBLUR_WHITE_COLOR) :
|
|
|
|
|
UIColorFromFixedRGB(NEWSBLUR_BLACK_COLOR);
|
2015-09-16 20:24:11 -07:00
|
|
|
|
titleLabel.shadowColor = lightText ? borderColor : fadeColor;
|
2011-10-28 18:29:33 -07:00
|
|
|
|
} else {
|
2016-01-27 21:58:55 -08:00
|
|
|
|
titleLabel.textColor = UIColorFromFixedRGB(NEWSBLUR_WHITE_COLOR);
|
|
|
|
|
titleLabel.shadowColor = UIColorFromFixedRGB(NEWSBLUR_BLACK_COLOR);
|
2011-10-28 18:29:33 -07:00
|
|
|
|
}
|
2011-12-04 21:09:16 -08:00
|
|
|
|
titleLabel.frame = CGRectMake(32, 1, rect.size.width-32, 20);
|
2011-10-28 18:29:33 -07:00
|
|
|
|
|
|
|
|
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@", [feed objectForKey:@"id"]];
|
2014-02-24 18:56:51 -08:00
|
|
|
|
UIImage *titleImage = [self getFavicon:feedIdStr];
|
2011-10-28 18:29:33 -07:00
|
|
|
|
UIImageView *titleImageView = [[UIImageView alloc] initWithImage:titleImage];
|
2011-10-30 18:53:10 -07:00
|
|
|
|
titleImageView.frame = CGRectMake(8, 3, 16.0, 16.0);
|
2011-10-28 18:29:33 -07:00
|
|
|
|
[titleLabel addSubview:titleImageView];
|
|
|
|
|
|
|
|
|
|
[gradientView addSubview:titleLabel];
|
|
|
|
|
[gradientView addSubview:titleImageView];
|
|
|
|
|
} else {
|
|
|
|
|
gradientView = [NewsBlurAppDelegate
|
2020-05-26 14:13:46 -07:00
|
|
|
|
makeGradientView:CGRectMake(0, rect.origin.y, rect.size.width, 10)
|
2012-06-22 21:53:45 -07:00
|
|
|
|
// hard coding the 1024 as a hack for window.frame.size.width
|
2015-09-16 20:24:11 -07:00
|
|
|
|
startColor:[feed objectForKey:@"favicon_fade"]
|
|
|
|
|
endColor:[feed objectForKey:@"favicon_color"]
|
|
|
|
|
borderColor:[feed objectForKey:@"favicon_border"]];
|
2011-10-28 18:29:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gradientView.opaque = YES;
|
|
|
|
|
|
2011-10-27 19:05:38 -07:00
|
|
|
|
return gradientView;
|
|
|
|
|
}
|
2011-08-18 09:56:52 -07:00
|
|
|
|
|
2011-12-02 16:23:00 -08:00
|
|
|
|
- (UIView *)makeFeedTitle:(NSDictionary *)feed {
|
2012-07-15 15:06:06 -07:00
|
|
|
|
UILabel *titleLabel = [[UILabel alloc] init];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
if (storiesCollection.isSocialRiverView &&
|
|
|
|
|
[storiesCollection.activeFolder isEqualToString:@"river_blurblogs"]) {
|
2012-10-03 15:46:02 -07:00
|
|
|
|
titleLabel.text = [NSString stringWithFormat:@" All Shared Stories"];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
} else if (storiesCollection.isSocialRiverView &&
|
|
|
|
|
[storiesCollection.activeFolder isEqualToString:@"river_global"]) {
|
2012-12-07 15:17:22 -08:00
|
|
|
|
titleLabel.text = [NSString stringWithFormat:@" Global Shared Stories"];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
} else if (storiesCollection.isRiverView &&
|
|
|
|
|
[storiesCollection.activeFolder isEqualToString:@"everything"]) {
|
2012-10-03 15:46:02 -07:00
|
|
|
|
titleLabel.text = [NSString stringWithFormat:@" All Stories"];
|
2017-11-05 22:07:43 -08:00
|
|
|
|
} else if (storiesCollection.isRiverView &&
|
|
|
|
|
[storiesCollection.activeFolder isEqualToString:@"infrequent"]) {
|
|
|
|
|
titleLabel.text = [NSString stringWithFormat:@" Infrequent Site Stories"];
|
2014-05-20 15:29:16 -07:00
|
|
|
|
} else if (storiesCollection.isSavedView && storiesCollection.activeSavedStoryTag) {
|
2014-05-20 15:46:38 -07:00
|
|
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
|
|
|
|
|
titleLabel.text = [NSString stringWithFormat:@" %@", storiesCollection.activeSavedStoryTag];
|
|
|
|
|
} else {
|
|
|
|
|
titleLabel.text = [NSString stringWithFormat:@" Saved Stories - %@", storiesCollection.activeSavedStoryTag];
|
|
|
|
|
}
|
2014-10-22 16:31:31 -07:00
|
|
|
|
} else if ([storiesCollection.activeFolder isEqualToString:@"read_stories"]) {
|
|
|
|
|
titleLabel.text = [NSString stringWithFormat:@" Read Stories"];
|
2014-05-20 15:29:16 -07:00
|
|
|
|
} else if ([storiesCollection.activeFolder isEqualToString:@"saved_stories"]) {
|
2012-10-17 15:07:53 -07:00
|
|
|
|
titleLabel.text = [NSString stringWithFormat:@" Saved Stories"];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
} else if (storiesCollection.isSocialView) {
|
2012-07-12 22:05:23 -07:00
|
|
|
|
titleLabel.text = [NSString stringWithFormat:@" %@", [feed objectForKey:@"feed_title"]];
|
2014-12-16 14:25:05 -08:00
|
|
|
|
} else if (storiesCollection.isRiverView) {
|
|
|
|
|
titleLabel.text = [NSString stringWithFormat:@" %@", storiesCollection.activeFolder];
|
2011-12-02 16:23:00 -08:00
|
|
|
|
} else {
|
|
|
|
|
titleLabel.text = [NSString stringWithFormat:@" %@", [feed objectForKey:@"feed_title"]];
|
|
|
|
|
}
|
|
|
|
|
titleLabel.backgroundColor = [UIColor clearColor];
|
2013-06-17 11:50:13 -07:00
|
|
|
|
titleLabel.textAlignment = NSTextAlignmentLeft;
|
2011-12-02 16:23:00 -08:00
|
|
|
|
titleLabel.font = [UIFont fontWithName:@"Helvetica-Bold" size:15.0];
|
2013-09-24 17:18:20 -07:00
|
|
|
|
titleLabel.textColor = UIColorFromRGB(0x4D4C4A);
|
2013-06-17 11:50:13 -07:00
|
|
|
|
titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
2011-12-04 21:09:16 -08:00
|
|
|
|
titleLabel.numberOfLines = 1;
|
2013-09-24 17:18:20 -07:00
|
|
|
|
titleLabel.shadowColor = UIColorFromRGB(0xF0F0F0);
|
|
|
|
|
titleLabel.shadowOffset = CGSizeMake(0, 1);
|
2012-07-12 22:05:23 -07:00
|
|
|
|
titleLabel.center = CGPointMake(0, -2);
|
2014-02-12 20:09:37 -08:00
|
|
|
|
if (!storiesCollection.isSocialView) {
|
2012-07-12 22:05:23 -07:00
|
|
|
|
titleLabel.center = CGPointMake(28, -2);
|
|
|
|
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@", [feed objectForKey:@"id"]];
|
|
|
|
|
UIImage *titleImage;
|
2014-02-12 20:09:37 -08:00
|
|
|
|
if (storiesCollection.isSocialRiverView &&
|
|
|
|
|
[storiesCollection.activeFolder isEqualToString:@"river_global"]) {
|
2013-02-21 12:19:15 -08:00
|
|
|
|
titleImage = [UIImage imageNamed:@"ak-icon-global.png"];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
} else if (storiesCollection.isSocialRiverView &&
|
|
|
|
|
[storiesCollection.activeFolder isEqualToString:@"river_blurblogs"]) {
|
2013-02-21 12:19:15 -08:00
|
|
|
|
titleImage = [UIImage imageNamed:@"ak-icon-blurblogs.png"];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
} else if (storiesCollection.isRiverView &&
|
|
|
|
|
[storiesCollection.activeFolder isEqualToString:@"everything"]) {
|
2013-02-21 12:19:15 -08:00
|
|
|
|
titleImage = [UIImage imageNamed:@"ak-icon-allstories.png"];
|
2017-11-05 22:07:43 -08:00
|
|
|
|
} else if (storiesCollection.isRiverView &&
|
|
|
|
|
[storiesCollection.activeFolder isEqualToString:@"infrequent"]) {
|
|
|
|
|
titleImage = [UIImage imageNamed:@"ak-icon-allstories.png"];
|
2014-05-20 15:29:16 -07:00
|
|
|
|
} else if (storiesCollection.isSavedView && storiesCollection.activeSavedStoryTag) {
|
|
|
|
|
titleImage = [UIImage imageNamed:@"tag.png"];
|
2014-10-22 16:39:37 -07:00
|
|
|
|
} else if ([storiesCollection.activeFolder isEqualToString:@"read_stories"]) {
|
2014-10-22 17:03:48 -07:00
|
|
|
|
titleImage = [UIImage imageNamed:@"g_icn_folder_read.png"];
|
2014-05-20 15:29:16 -07:00
|
|
|
|
} else if ([storiesCollection.activeFolder isEqualToString:@"saved_stories"]) {
|
2013-03-07 10:55:23 -05:00
|
|
|
|
titleImage = [UIImage imageNamed:@"clock.png"];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
} else if (storiesCollection.isRiverView) {
|
2013-02-21 14:44:34 -08:00
|
|
|
|
titleImage = [UIImage imageNamed:@"g_icn_folder.png"];
|
2012-07-12 22:05:23 -07:00
|
|
|
|
} else {
|
2014-02-24 18:56:51 -08:00
|
|
|
|
titleImage = [self getFavicon:feedIdStr];
|
2012-07-12 22:05:23 -07:00
|
|
|
|
}
|
|
|
|
|
UIImageView *titleImageView = [[UIImageView alloc] initWithImage:titleImage];
|
|
|
|
|
titleImageView.frame = CGRectMake(0.0, 2.0, 16.0, 16.0);
|
|
|
|
|
[titleLabel addSubview:titleImageView];
|
|
|
|
|
}
|
2013-10-11 17:46:09 -07:00
|
|
|
|
[titleLabel sizeToFit];
|
2012-07-12 22:05:23 -07:00
|
|
|
|
|
2013-10-11 17:46:09 -07:00
|
|
|
|
return titleLabel;
|
2011-12-02 16:23:00 -08:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-11 21:02:22 -07:00
|
|
|
|
- (NSString *)folderTitle:(NSString *)folder {
|
|
|
|
|
if ([folder isEqualToString:@"river_blurblogs"]) {
|
|
|
|
|
return @"All Shared Stories";
|
|
|
|
|
} else if ([folder isEqualToString:@"river_global"]) {
|
|
|
|
|
return @"Global Shared Stories";
|
|
|
|
|
} else if ([folder isEqualToString:@"everything"]) {
|
|
|
|
|
return @"All Stories";
|
|
|
|
|
} else if ([folder isEqualToString:@"infrequent"]) {
|
|
|
|
|
return @"Infrequent Site Stories";
|
|
|
|
|
} else if ([folder isEqualToString:@"read_stories"]) {
|
|
|
|
|
return @"Read Stories";
|
|
|
|
|
} else if ([folder isEqualToString:@"saved_searches"]) {
|
|
|
|
|
return @"Saved Searches";
|
|
|
|
|
} else if ([folder isEqualToString:@"saved_stories"]) {
|
|
|
|
|
return @"Saved Stories";
|
|
|
|
|
} else {
|
|
|
|
|
return folder;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (UIImage *)folderIcon:(NSString *)folder {
|
|
|
|
|
if ([folder isEqualToString:@"river_global"]) {
|
|
|
|
|
return [UIImage imageNamed:@"ak-icon-global.png"];
|
|
|
|
|
} else if ([folder isEqualToString:@"river_blurblogs"]) {
|
|
|
|
|
return [UIImage imageNamed:@"ak-icon-blurblogs.png"];
|
|
|
|
|
} else if ([folder isEqualToString:@"everything"]) {
|
|
|
|
|
return [UIImage imageNamed:@"ak-icon-allstories.png"];
|
|
|
|
|
} else if ([folder isEqualToString:@"infrequent"]) {
|
|
|
|
|
return [UIImage imageNamed:@"ak-icon-allstories.png"];
|
|
|
|
|
} else if ([folder isEqualToString:@"read_stories"]) {
|
|
|
|
|
return [UIImage imageNamed:@"g_icn_folder_read.png"];
|
|
|
|
|
} else if ([folder isEqualToString:@"saved_searches"]) {
|
|
|
|
|
return [UIImage imageNamed:@"g_icn_search.png"];
|
|
|
|
|
} else if ([folder isEqualToString:@"saved_stories"]) {
|
|
|
|
|
return [UIImage imageNamed:@"clock.png"];
|
|
|
|
|
} else {
|
|
|
|
|
return [UIImage imageNamed:@"g_icn_folder.png"];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-24 18:56:51 -08:00
|
|
|
|
- (void)saveFavicon:(UIImage *)image feedId:(NSString *)filename {
|
|
|
|
|
if (image && filename && ![image isKindOfClass:[NSNull class]] &&
|
|
|
|
|
[filename class] != [NSNull class]) {
|
2014-03-03 22:36:40 -08:00
|
|
|
|
[self.cachedFavicons setObject:image forKey:filename];
|
2014-02-24 18:56:51 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (UIImage *)getFavicon:(NSString *)filename {
|
|
|
|
|
return [self getFavicon:filename isSocial:NO];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (UIImage *)getFavicon:(NSString *)filename isSocial:(BOOL)isSocial {
|
2014-05-20 13:12:03 -07:00
|
|
|
|
return [self getFavicon:filename isSocial:isSocial isSaved:NO];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (UIImage *)getFavicon:(NSString *)filename isSocial:(BOOL)isSocial isSaved:(BOOL)isSaved {
|
2014-02-24 18:56:51 -08:00
|
|
|
|
UIImage *image = [self.cachedFavicons objectForKey:filename];
|
|
|
|
|
|
|
|
|
|
if (image) {
|
|
|
|
|
return image;
|
|
|
|
|
} else {
|
|
|
|
|
if (isSocial) {
|
|
|
|
|
// return [UIImage imageNamed:@"user_light.png"];
|
|
|
|
|
return nil;
|
2014-05-20 13:12:03 -07:00
|
|
|
|
} else if (isSaved) {
|
|
|
|
|
return [UIImage imageNamed:@"tag.png"];
|
2014-02-24 18:56:51 -08:00
|
|
|
|
} else {
|
|
|
|
|
return [UIImage imageNamed:@"world.png"];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-27 18:37:05 -08:00
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Classifiers
|
|
|
|
|
|
2017-04-03 16:07:01 -07:00
|
|
|
|
- (void)failedClassifierSave:(NSURLSessionDataTask *)task {
|
|
|
|
|
BaseViewController *view;
|
|
|
|
|
if (self.trainerViewController.isViewLoaded && self.trainerViewController.view.window) {
|
|
|
|
|
view = self.trainerViewController;
|
|
|
|
|
} else {
|
|
|
|
|
view = self.storyPageControl.currentPage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
|
|
|
|
|
if (response.statusCode == 503) {
|
|
|
|
|
[view informError:@"In maintenance mode"];
|
|
|
|
|
} else {
|
|
|
|
|
[view informError:@"The server barfed!"];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-27 18:37:05 -08:00
|
|
|
|
- (void)toggleAuthorClassifier:(NSString *)author feedId:(NSString *)feedId {
|
2014-02-12 20:09:37 -08:00
|
|
|
|
int authorScore = [[[[storiesCollection.activeClassifiers objectForKey:feedId]
|
2012-12-27 18:37:05 -08:00
|
|
|
|
objectForKey:@"authors"]
|
|
|
|
|
objectForKey:author] intValue];
|
|
|
|
|
if (authorScore > 0) {
|
|
|
|
|
authorScore = -1;
|
|
|
|
|
} else if (authorScore < 0) {
|
|
|
|
|
authorScore = 0;
|
|
|
|
|
} else {
|
|
|
|
|
authorScore = 1;
|
|
|
|
|
}
|
2014-02-12 20:09:37 -08:00
|
|
|
|
NSMutableDictionary *feedClassifiers = [[storiesCollection.activeClassifiers objectForKey:feedId]
|
2012-12-27 18:37:05 -08:00
|
|
|
|
mutableCopy];
|
2013-09-05 17:07:21 -07:00
|
|
|
|
if (!feedClassifiers) feedClassifiers = [NSMutableDictionary dictionary];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
NSMutableDictionary *authors = [[feedClassifiers objectForKey:@"authors"] mutableCopy];
|
2013-09-05 17:07:21 -07:00
|
|
|
|
if (!authors) authors = [NSMutableDictionary dictionary];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
[authors setObject:[NSNumber numberWithInt:authorScore] forKey:author];
|
|
|
|
|
[feedClassifiers setObject:authors forKey:@"authors"];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
[storiesCollection.activeClassifiers setObject:feedClassifiers forKey:feedId];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
[self.storyPageControl refreshHeaders];
|
|
|
|
|
[self.trainerViewController refresh];
|
|
|
|
|
|
2013-06-18 21:23:20 -04:00
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@/classifier/save",
|
2016-01-21 22:11:37 -08:00
|
|
|
|
self.url];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
2017-03-19 12:09:49 -07:00
|
|
|
|
[params setObject:author
|
2017-04-03 16:07:01 -07:00
|
|
|
|
forKey:authorScore >= 1 ? @"like_author" :
|
|
|
|
|
authorScore <= -1 ? @"dislike_author" :
|
|
|
|
|
@"remove_like_author"];
|
2017-03-19 12:09:49 -07:00
|
|
|
|
[params setObject:feedId forKey:@"feed_id"];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
|
2019-08-22 12:03:39 -07:00
|
|
|
|
[self POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
2017-03-09 20:10:44 -08:00
|
|
|
|
[self.feedsViewController refreshFeedList:feedId];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
|
|
|
[self failedClassifierSave:task];
|
2013-09-05 17:07:21 -07:00
|
|
|
|
}];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
|
2012-12-27 18:37:05 -08:00
|
|
|
|
[self recalculateIntelligenceScores:feedId];
|
|
|
|
|
[self.feedDetailViewController.storyTitlesTable reloadData];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)toggleTagClassifier:(NSString *)tag feedId:(NSString *)feedId {
|
|
|
|
|
NSLog(@"toggleTagClassifier: %@", tag);
|
2014-02-12 20:09:37 -08:00
|
|
|
|
int tagScore = [[[[storiesCollection.activeClassifiers objectForKey:feedId]
|
2012-12-27 18:37:05 -08:00
|
|
|
|
objectForKey:@"tags"]
|
|
|
|
|
objectForKey:tag] intValue];
|
|
|
|
|
|
|
|
|
|
if (tagScore > 0) {
|
|
|
|
|
tagScore = -1;
|
|
|
|
|
} else if (tagScore < 0) {
|
|
|
|
|
tagScore = 0;
|
|
|
|
|
} else {
|
|
|
|
|
tagScore = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
NSMutableDictionary *feedClassifiers = [[storiesCollection.activeClassifiers objectForKey:feedId]
|
2012-12-27 18:37:05 -08:00
|
|
|
|
mutableCopy];
|
2013-09-05 17:07:21 -07:00
|
|
|
|
if (!feedClassifiers) feedClassifiers = [NSMutableDictionary dictionary];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
NSMutableDictionary *tags = [[feedClassifiers objectForKey:@"tags"] mutableCopy];
|
2013-09-05 17:07:21 -07:00
|
|
|
|
if (!tags) tags = [NSMutableDictionary dictionary];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
[tags setObject:[NSNumber numberWithInt:tagScore] forKey:tag];
|
|
|
|
|
[feedClassifiers setObject:tags forKey:@"tags"];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
[storiesCollection.activeClassifiers setObject:feedClassifiers forKey:feedId];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
[self.storyPageControl refreshHeaders];
|
|
|
|
|
[self.trainerViewController refresh];
|
|
|
|
|
|
2013-06-18 21:23:20 -04:00
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@/classifier/save",
|
2016-01-21 22:11:37 -08:00
|
|
|
|
self.url];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
2017-03-19 12:09:49 -07:00
|
|
|
|
[params setObject:tag
|
2017-04-03 16:07:01 -07:00
|
|
|
|
forKey:tagScore >= 1 ? @"like_tag" :
|
|
|
|
|
tagScore <= -1 ? @"dislike_tag" :
|
|
|
|
|
@"remove_like_tag"];
|
2017-03-19 12:09:49 -07:00
|
|
|
|
[params setObject:feedId forKey:@"feed_id"];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
|
2019-08-22 12:03:39 -07:00
|
|
|
|
[self POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
2017-03-09 20:10:44 -08:00
|
|
|
|
[self.feedsViewController refreshFeedList:feedId];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
|
|
|
[self failedClassifierSave:task];
|
2013-09-05 17:07:21 -07:00
|
|
|
|
}];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
|
|
|
|
|
[self recalculateIntelligenceScores:feedId];
|
|
|
|
|
[self.feedDetailViewController.storyTitlesTable reloadData];
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-25 17:43:00 -07:00
|
|
|
|
- (void)toggleTitleClassifier:(NSString *)title feedId:(NSString *)feedId score:(NSInteger)score {
|
|
|
|
|
NSLog(@"toggle Title: %@ (%@) / %ld", title, feedId, (long)score);
|
2014-02-12 20:09:37 -08:00
|
|
|
|
NSInteger titleScore = [[[[storiesCollection.activeClassifiers objectForKey:feedId]
|
2013-09-25 17:43:00 -07:00
|
|
|
|
objectForKey:@"titles"]
|
|
|
|
|
objectForKey:title] intValue];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
|
|
|
|
|
if (score) {
|
|
|
|
|
titleScore = score;
|
|
|
|
|
} else {
|
|
|
|
|
if (titleScore > 0) {
|
|
|
|
|
titleScore = -1;
|
|
|
|
|
} else if (titleScore < 0) {
|
|
|
|
|
titleScore = 0;
|
|
|
|
|
} else {
|
|
|
|
|
titleScore = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
NSMutableDictionary *feedClassifiers = [[storiesCollection.activeClassifiers objectForKey:feedId]
|
2012-12-27 18:37:05 -08:00
|
|
|
|
mutableCopy];
|
2013-09-05 17:07:21 -07:00
|
|
|
|
if (!feedClassifiers) feedClassifiers = [NSMutableDictionary dictionary];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
NSMutableDictionary *titles = [[feedClassifiers objectForKey:@"titles"] mutableCopy];
|
2013-09-05 17:07:21 -07:00
|
|
|
|
if (!titles) titles = [NSMutableDictionary dictionary];
|
2013-09-25 17:43:00 -07:00
|
|
|
|
[titles setObject:[NSNumber numberWithInteger:titleScore] forKey:title];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
[feedClassifiers setObject:titles forKey:@"titles"];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
[storiesCollection.activeClassifiers setObject:feedClassifiers forKey:feedId];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
[self.storyPageControl refreshHeaders];
|
|
|
|
|
[self.trainerViewController refresh];
|
|
|
|
|
|
2013-06-18 21:23:20 -04:00
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@/classifier/save",
|
2016-01-21 22:11:37 -08:00
|
|
|
|
self.url];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
2017-03-19 12:09:49 -07:00
|
|
|
|
[params setObject:title
|
2017-04-03 16:07:01 -07:00
|
|
|
|
forKey:titleScore >= 1 ? @"like_title" :
|
|
|
|
|
titleScore <= -1 ? @"dislike_title" :
|
|
|
|
|
@"remove_like_title"];
|
2017-03-19 12:09:49 -07:00
|
|
|
|
[params setObject:feedId forKey:@"feed_id"];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
|
2019-08-22 12:03:39 -07:00
|
|
|
|
[self POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
2017-03-09 20:10:44 -08:00
|
|
|
|
[self.feedsViewController refreshFeedList:feedId];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
|
|
|
[self failedClassifierSave:task];
|
2013-09-05 17:07:21 -07:00
|
|
|
|
}];
|
2017-04-03 16:07:01 -07:00
|
|
|
|
|
2012-12-27 18:37:05 -08:00
|
|
|
|
[self recalculateIntelligenceScores:feedId];
|
|
|
|
|
[self.feedDetailViewController.storyTitlesTable reloadData];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)toggleFeedClassifier:(NSString *)feedId {
|
2014-02-12 20:09:37 -08:00
|
|
|
|
int feedScore = [[[[storiesCollection.activeClassifiers objectForKey:feedId]
|
2012-12-27 18:37:05 -08:00
|
|
|
|
objectForKey:@"feeds"]
|
|
|
|
|
objectForKey:feedId] intValue];
|
|
|
|
|
|
|
|
|
|
if (feedScore > 0) {
|
|
|
|
|
feedScore = -1;
|
|
|
|
|
} else if (feedScore < 0) {
|
|
|
|
|
feedScore = 0;
|
|
|
|
|
} else {
|
|
|
|
|
feedScore = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
NSMutableDictionary *feedClassifiers = [[storiesCollection.activeClassifiers objectForKey:feedId]
|
2012-12-27 18:37:05 -08:00
|
|
|
|
mutableCopy];
|
2013-09-05 17:07:21 -07:00
|
|
|
|
if (!feedClassifiers) feedClassifiers = [NSMutableDictionary dictionary];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
NSMutableDictionary *feeds = [[feedClassifiers objectForKey:@"feeds"] mutableCopy];
|
|
|
|
|
[feeds setObject:[NSNumber numberWithInt:feedScore] forKey:feedId];
|
|
|
|
|
[feedClassifiers setObject:feeds forKey:@"feeds"];
|
2014-02-12 20:09:37 -08:00
|
|
|
|
[storiesCollection.activeClassifiers setObject:feedClassifiers forKey:feedId];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
[self.storyPageControl refreshHeaders];
|
|
|
|
|
[self.trainerViewController refresh];
|
|
|
|
|
|
2013-06-18 21:23:20 -04:00
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@/classifier/save",
|
2016-01-21 22:11:37 -08:00
|
|
|
|
self.url];
|
2017-03-09 20:10:44 -08:00
|
|
|
|
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
|
|
|
|
[params setObject:feedId
|
2012-12-27 18:37:05 -08:00
|
|
|
|
forKey:feedScore >= 1 ? @"like_feed" :
|
|
|
|
|
feedScore <= -1 ? @"dislike_feed" :
|
|
|
|
|
@"remove_like_feed"];
|
2017-03-09 20:10:44 -08:00
|
|
|
|
[params setObject:feedId forKey:@"feed_id"];
|
2012-12-27 18:37:05 -08:00
|
|
|
|
|
2019-08-22 12:03:39 -07:00
|
|
|
|
[self POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
2017-03-09 20:10:44 -08:00
|
|
|
|
[self.feedsViewController refreshFeedList:feedId];
|
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
|
|
|
[self failedRequest:task.response];
|
|
|
|
|
}];
|
|
|
|
|
|
2012-12-27 18:37:05 -08:00
|
|
|
|
[self recalculateIntelligenceScores:feedId];
|
|
|
|
|
[self.feedDetailViewController.storyTitlesTable reloadData];
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-09 20:10:44 -08:00
|
|
|
|
- (void)failedRequest:(NSURLResponse *)response {
|
|
|
|
|
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
2013-09-05 17:07:21 -07:00
|
|
|
|
BaseViewController *view;
|
|
|
|
|
if (self.trainerViewController.isViewLoaded && self.trainerViewController.view.window) {
|
|
|
|
|
view = self.trainerViewController;
|
|
|
|
|
} else {
|
|
|
|
|
view = self.storyPageControl.currentPage;
|
|
|
|
|
}
|
2017-03-09 20:10:44 -08:00
|
|
|
|
if (httpResponse.statusCode == 503) {
|
2013-09-05 17:07:21 -07:00
|
|
|
|
return [view informError:@"In maintenance mode"];
|
2017-03-09 20:10:44 -08:00
|
|
|
|
} else if (httpResponse.statusCode != 200) {
|
2013-09-05 17:07:21 -07:00
|
|
|
|
return [view informError:@"The server barfed!"];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-30 18:45:51 -07:00
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Storing Stories for Offline
|
|
|
|
|
|
2015-09-17 13:15:10 -07:00
|
|
|
|
// Returns the URL to the application's Documents directory.
|
|
|
|
|
- (NSURL *)applicationDocumentsDirectory
|
|
|
|
|
{
|
|
|
|
|
NSLog(@" ---> DB dir: %@",[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]);
|
|
|
|
|
|
|
|
|
|
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-25 17:43:00 -07:00
|
|
|
|
- (NSInteger)databaseSchemaVersion:(FMDatabase *)db {
|
2013-06-05 17:11:01 -07:00
|
|
|
|
int version = 0;
|
2013-06-14 19:21:30 -07:00
|
|
|
|
FMResultSet *resultSet = [db executeQuery:@"PRAGMA user_version"];
|
2013-06-05 17:11:01 -07:00
|
|
|
|
if ([resultSet next]) {
|
|
|
|
|
version = [resultSet intForColumnIndex:0];
|
|
|
|
|
}
|
2013-10-03 18:07:39 -07:00
|
|
|
|
[resultSet close];
|
2013-06-05 17:11:01 -07:00
|
|
|
|
return version;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-14 19:21:30 -07:00
|
|
|
|
- (void)createDatabaseConnection {
|
2013-10-08 16:00:55 -07:00
|
|
|
|
NSError *error;
|
|
|
|
|
|
|
|
|
|
// Remove the deletion of old sqlite dbs past version 3.1, once everybody's
|
|
|
|
|
// upgraded and removed the old files.
|
|
|
|
|
NSFileManager *fileManager = [[NSFileManager alloc] init];
|
|
|
|
|
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
|
|
|
|
NSString *oldDBPath = [documentPaths objectAtIndex:0];
|
|
|
|
|
NSArray *directoryContents = [fileManager contentsOfDirectoryAtPath:oldDBPath error:&error];
|
|
|
|
|
int removed = 0;
|
|
|
|
|
|
|
|
|
|
if (error == nil) {
|
|
|
|
|
for (NSString *path in directoryContents) {
|
|
|
|
|
NSString *fullPath = [oldDBPath stringByAppendingPathComponent:path];
|
|
|
|
|
if ([fullPath hasSuffix:@".sqlite"]) {
|
|
|
|
|
[fileManager removeItemAtPath:fullPath error:&error];
|
|
|
|
|
removed++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (removed) {
|
|
|
|
|
NSLog(@"Deleted %d sql dbs.", removed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSArray *cachePaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
|
|
|
|
NSString *dbPath = [cachePaths objectAtIndex:0];
|
2016-01-21 22:11:37 -08:00
|
|
|
|
NSString *dbName = [NSString stringWithFormat:@"%@.sqlite", self.host];
|
2013-10-08 16:00:55 -07:00
|
|
|
|
NSString *path = [dbPath stringByAppendingPathComponent:dbName];
|
2015-09-17 13:15:10 -07:00
|
|
|
|
[self applicationDocumentsDirectory];
|
2013-06-05 17:11:01 -07:00
|
|
|
|
|
2013-06-14 19:21:30 -07:00
|
|
|
|
database = [FMDatabaseQueue databaseQueueWithPath:path];
|
|
|
|
|
[database inDatabase:^(FMDatabase *db) {
|
2015-09-16 21:34:41 -07:00
|
|
|
|
// db.traceExecution = YES;
|
2017-04-04 15:33:08 -07:00
|
|
|
|
[self setupDatabase:db force:NO];
|
2013-06-14 19:21:30 -07:00
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-04 15:33:08 -07:00
|
|
|
|
- (void)setupDatabase:(FMDatabase *)db force:(BOOL)force {
|
2018-09-13 10:44:00 -07:00
|
|
|
|
NSUInteger databaseVersion = [self databaseSchemaVersion:db];
|
|
|
|
|
|
|
|
|
|
if (databaseVersion < CURRENT_DB_VERSION || force) {
|
2013-06-05 17:11:01 -07:00
|
|
|
|
// FMDB cannot execute this query because FMDB tries to use prepared statements
|
2013-06-21 22:18:54 -07:00
|
|
|
|
[db closeOpenResultSets];
|
2014-09-26 17:51:01 -07:00
|
|
|
|
|
2018-09-13 10:44:00 -07:00
|
|
|
|
// Perform just the needed updates (in the future, if any of these table schemas change, move their drop statement to a new block below)
|
|
|
|
|
if (databaseVersion < 35) {
|
|
|
|
|
[db executeUpdate:@"drop table if exists `stories`"];
|
|
|
|
|
[db executeUpdate:@"drop table if exists `unread_hashes`"];
|
|
|
|
|
[db executeUpdate:@"drop table if exists `accounts`"];
|
|
|
|
|
[db executeUpdate:@"drop table if exists `unread_counts`"];
|
|
|
|
|
[db executeUpdate:@"drop table if exists `cached_images`"];
|
|
|
|
|
[db executeUpdate:@"drop table if exists `users`"];
|
|
|
|
|
// [db executeUpdate:@"drop table if exists `queued_read_hashes`"]; // Nope, don't clear this.
|
|
|
|
|
// [db executeUpdate:@"drop table if exists `queued_saved_hashes`"]; // Nope, don't clear this.
|
|
|
|
|
|
|
|
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
|
|
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
|
|
|
|
NSString *cacheDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"story_images"];
|
|
|
|
|
NSError *error = nil;
|
|
|
|
|
BOOL success = [fileManager removeItemAtPath:cacheDirectory error:&error];
|
|
|
|
|
if (!success || error) {
|
|
|
|
|
// something went wrong
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (databaseVersion < 36) {
|
|
|
|
|
[db executeUpdate:@"drop table if exists `queued_saved_hashes`"];
|
2014-09-26 17:51:01 -07:00
|
|
|
|
}
|
2018-09-13 10:44:00 -07:00
|
|
|
|
|
2019-10-25 20:52:51 -07:00
|
|
|
|
if (databaseVersion < 37) {
|
|
|
|
|
[db executeUpdate:@"drop table if exists `cached_text`"];
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-13 10:44:00 -07:00
|
|
|
|
NSLog(@"Dropped db: %@", [db lastErrorMessage]);
|
|
|
|
|
sqlite3_exec(db.sqliteHandle, [[NSString stringWithFormat:@"PRAGMA user_version = %d", CURRENT_DB_VERSION] UTF8String], NULL, NULL, NULL);
|
2013-06-14 19:21:30 -07:00
|
|
|
|
}
|
2013-06-21 22:18:54 -07:00
|
|
|
|
NSString *createAccountsTable = [NSString stringWithFormat:@"create table if not exists accounts "
|
2013-06-20 19:25:57 -07:00
|
|
|
|
"("
|
|
|
|
|
" username varchar(36),"
|
|
|
|
|
" download_date date,"
|
|
|
|
|
" feeds_json text,"
|
|
|
|
|
" UNIQUE(username) ON CONFLICT REPLACE"
|
|
|
|
|
")"];
|
2013-06-21 22:18:54 -07:00
|
|
|
|
[db executeUpdate:createAccountsTable];
|
|
|
|
|
|
2013-06-29 17:28:41 -07:00
|
|
|
|
NSString *createCountsTable = [NSString stringWithFormat:@"create table if not exists unread_counts "
|
2013-06-21 22:18:54 -07:00
|
|
|
|
"("
|
2013-06-29 17:28:41 -07:00
|
|
|
|
" feed_id varchar(20),"
|
2013-06-21 22:18:54 -07:00
|
|
|
|
" ps number,"
|
|
|
|
|
" nt number,"
|
|
|
|
|
" ng number,"
|
|
|
|
|
" UNIQUE(feed_id) ON CONFLICT REPLACE"
|
|
|
|
|
")"];
|
2013-06-29 17:28:41 -07:00
|
|
|
|
[db executeUpdate:createCountsTable];
|
2013-06-20 19:25:57 -07:00
|
|
|
|
|
2013-06-14 19:21:30 -07:00
|
|
|
|
NSString *createStoryTable = [NSString stringWithFormat:@"create table if not exists stories "
|
|
|
|
|
"("
|
2013-10-03 18:07:39 -07:00
|
|
|
|
" story_feed_id varchar(20),"
|
2013-06-14 19:21:30 -07:00
|
|
|
|
" story_hash varchar(24),"
|
|
|
|
|
" story_timestamp number,"
|
|
|
|
|
" story_json text,"
|
2015-09-16 21:34:41 -07:00
|
|
|
|
" scroll number,"
|
2013-06-14 19:21:30 -07:00
|
|
|
|
" UNIQUE(story_hash) ON CONFLICT REPLACE"
|
|
|
|
|
")"];
|
|
|
|
|
[db executeUpdate:createStoryTable];
|
2013-06-26 16:22:44 -07:00
|
|
|
|
NSString *indexStoriesFeed = @"CREATE INDEX IF NOT EXISTS stories_story_feed_id ON stories (story_feed_id)";
|
|
|
|
|
[db executeUpdate:indexStoriesFeed];
|
2015-09-17 13:15:10 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NSString *createStoryScrollsTable = [NSString stringWithFormat:@"create table if not exists story_scrolls "
|
|
|
|
|
"("
|
|
|
|
|
" story_feed_id varchar(20),"
|
|
|
|
|
" story_hash varchar(24),"
|
|
|
|
|
" story_timestamp number,"
|
|
|
|
|
" scroll number,"
|
|
|
|
|
" UNIQUE(story_hash) ON CONFLICT REPLACE"
|
|
|
|
|
")"];
|
|
|
|
|
[db executeUpdate:createStoryScrollsTable];
|
|
|
|
|
NSString *indexStoriesHash = @"CREATE INDEX IF NOT EXISTS story_scrolls_story_hash ON story_scrolls (story_hash)";
|
2015-09-16 21:34:41 -07:00
|
|
|
|
[db executeUpdate:indexStoriesHash];
|
2013-06-14 19:21:30 -07:00
|
|
|
|
|
|
|
|
|
NSString *createUnreadHashTable = [NSString stringWithFormat:@"create table if not exists unread_hashes "
|
|
|
|
|
"("
|
2013-10-03 18:07:39 -07:00
|
|
|
|
" story_feed_id varchar(20),"
|
2013-06-14 19:21:30 -07:00
|
|
|
|
" story_hash varchar(24),"
|
2013-06-16 21:39:38 -07:00
|
|
|
|
" story_timestamp number,"
|
2013-06-14 19:21:30 -07:00
|
|
|
|
" UNIQUE(story_hash) ON CONFLICT IGNORE"
|
|
|
|
|
")"];
|
|
|
|
|
[db executeUpdate:createUnreadHashTable];
|
2013-06-26 16:22:44 -07:00
|
|
|
|
NSString *indexUnreadHashes = @"CREATE INDEX IF NOT EXISTS unread_hashes_story_feed_id ON unread_hashes (story_feed_id)";
|
|
|
|
|
[db executeUpdate:indexUnreadHashes];
|
|
|
|
|
NSString *indexUnreadTimestamp = @"CREATE INDEX IF NOT EXISTS unread_hashes_timestamp ON stories (story_timestamp)";
|
|
|
|
|
[db executeUpdate:indexUnreadTimestamp];
|
2013-06-14 19:21:30 -07:00
|
|
|
|
|
|
|
|
|
NSString *createReadTable = [NSString stringWithFormat:@"create table if not exists queued_read_hashes "
|
|
|
|
|
"("
|
2013-10-03 18:07:39 -07:00
|
|
|
|
" story_feed_id varchar(20),"
|
2013-06-14 19:21:30 -07:00
|
|
|
|
" story_hash varchar(24),"
|
|
|
|
|
" UNIQUE(story_hash) ON CONFLICT IGNORE"
|
|
|
|
|
")"];
|
|
|
|
|
[db executeUpdate:createReadTable];
|
2013-09-05 16:34:39 -07:00
|
|
|
|
|
2013-10-04 12:37:51 -07:00
|
|
|
|
NSString *createSavedTable = [NSString stringWithFormat:@"create table if not exists queued_saved_hashes "
|
|
|
|
|
"("
|
|
|
|
|
" story_feed_id varchar(20),"
|
|
|
|
|
" story_hash varchar(24),"
|
2018-09-13 10:44:00 -07:00
|
|
|
|
" saved boolean,"
|
|
|
|
|
" info_json text,"
|
2013-10-04 12:37:51 -07:00
|
|
|
|
" UNIQUE(story_hash) ON CONFLICT IGNORE"
|
|
|
|
|
")"];
|
|
|
|
|
[db executeUpdate:createSavedTable];
|
|
|
|
|
|
2019-10-25 20:52:51 -07:00
|
|
|
|
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];
|
|
|
|
|
|
2013-06-22 19:34:12 -07:00
|
|
|
|
NSString *createImagesTable = [NSString stringWithFormat:@"create table if not exists cached_images "
|
|
|
|
|
"("
|
2013-10-03 18:07:39 -07:00
|
|
|
|
" story_feed_id varchar(20),"
|
2013-06-22 19:34:12 -07:00
|
|
|
|
" story_hash varchar(24),"
|
|
|
|
|
" image_url varchar(1024),"
|
2013-07-18 18:24:38 -07:00
|
|
|
|
" image_cached boolean,"
|
2013-07-11 18:36:09 -07:00
|
|
|
|
" failed boolean"
|
2013-06-22 19:34:12 -07:00
|
|
|
|
")"];
|
|
|
|
|
[db executeUpdate:createImagesTable];
|
2013-06-26 16:22:44 -07:00
|
|
|
|
NSString *indexImagesFeedId = @"CREATE INDEX IF NOT EXISTS cached_images_story_feed_id ON cached_images (story_feed_id)";
|
|
|
|
|
[db executeUpdate:indexImagesFeedId];
|
|
|
|
|
NSString *indexImagesStoryHash = @"CREATE INDEX IF NOT EXISTS cached_images_story_hash ON cached_images (story_hash)";
|
|
|
|
|
[db executeUpdate:indexImagesStoryHash];
|
2013-06-22 19:34:12 -07:00
|
|
|
|
|
2013-09-05 16:34:39 -07:00
|
|
|
|
|
|
|
|
|
NSString *createUsersTable = [NSString stringWithFormat:@"create table if not exists users "
|
|
|
|
|
"("
|
|
|
|
|
" user_id number,"
|
|
|
|
|
" username varchar(64),"
|
|
|
|
|
" location varchar(128),"
|
|
|
|
|
" image_url varchar(1024),"
|
|
|
|
|
" image_cached boolean,"
|
|
|
|
|
" user_json text,"
|
|
|
|
|
" UNIQUE(user_id) ON CONFLICT REPLACE"
|
|
|
|
|
")"];
|
|
|
|
|
[db executeUpdate:createUsersTable];
|
|
|
|
|
NSString *indexUsersUserId = @"CREATE INDEX IF NOT EXISTS users_user_id ON users (user_id)";
|
|
|
|
|
[db executeUpdate:indexUsersUserId];
|
|
|
|
|
|
2014-02-25 12:37:22 -08:00
|
|
|
|
NSError *error;
|
|
|
|
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
|
|
|
|
NSString *storyImagesDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"story_images"];
|
|
|
|
|
if (![[NSFileManager defaultManager] fileExistsAtPath:storyImagesDirectory]) {
|
|
|
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:storyImagesDirectory
|
|
|
|
|
withIntermediateDirectories:NO
|
|
|
|
|
attributes:nil
|
|
|
|
|
error:&error];
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-11 11:52:32 -08:00
|
|
|
|
// NSLog(@"Create db %d: %@", [db lastErrorCode], [db lastErrorMessage]);
|
2013-05-30 18:45:51 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-07-18 18:24:38 -07:00
|
|
|
|
- (void)cancelOfflineQueue {
|
|
|
|
|
if (offlineQueue) {
|
|
|
|
|
[offlineQueue cancelAllOperations];
|
|
|
|
|
}
|
2013-08-05 18:32:43 -07:00
|
|
|
|
if (offlineCleaningQueue) {
|
|
|
|
|
[offlineCleaningQueue cancelAllOperations];
|
|
|
|
|
}
|
2013-07-18 18:24:38 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-07-15 18:25:09 -07:00
|
|
|
|
- (void)startOfflineQueue {
|
|
|
|
|
if (!offlineQueue) {
|
|
|
|
|
offlineQueue = [NSOperationQueue new];
|
|
|
|
|
}
|
|
|
|
|
offlineQueue.name = @"Offline Queue";
|
2014-02-11 11:52:32 -08:00
|
|
|
|
// NSLog(@"Operation queue: %lu", (unsigned long)offlineQueue.operationCount);
|
2013-07-15 18:25:09 -07:00
|
|
|
|
[offlineQueue cancelAllOperations];
|
|
|
|
|
[offlineQueue setMaxConcurrentOperationCount:1];
|
|
|
|
|
OfflineSyncUnreads *operationSyncUnreads = [[OfflineSyncUnreads alloc] init];
|
|
|
|
|
|
|
|
|
|
[offlineQueue addOperation:operationSyncUnreads];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)startOfflineFetchStories {
|
|
|
|
|
OfflineFetchStories *operationFetchStories = [[OfflineFetchStories alloc] init];
|
|
|
|
|
|
|
|
|
|
[offlineQueue addOperation:operationFetchStories];
|
2013-07-16 18:06:36 -07:00
|
|
|
|
|
2014-02-11 11:52:32 -08:00
|
|
|
|
// NSLog(@"Done start offline fetch stories");
|
2013-07-15 18:25:09 -07:00
|
|
|
|
}
|
|
|
|
|
|
2019-10-25 20:52:51 -07:00
|
|
|
|
- (void)startOfflineFetchText {
|
|
|
|
|
OfflineFetchText *operationFetchText = [[OfflineFetchText alloc] init];
|
|
|
|
|
|
|
|
|
|
[offlineQueue addOperation:operationFetchText];
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-15 18:25:09 -07:00
|
|
|
|
- (void)startOfflineFetchImages {
|
|
|
|
|
OfflineFetchImages *operationFetchImages = [[OfflineFetchImages alloc] init];
|
|
|
|
|
|
|
|
|
|
[offlineQueue addOperation:operationFetchImages];
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-16 16:53:07 -07:00
|
|
|
|
- (BOOL)isReachableForOffline {
|
2013-09-04 15:56:37 -07:00
|
|
|
|
Reachability *reachability = [Reachability reachabilityForInternetConnection];
|
|
|
|
|
NetworkStatus remoteHostStatus = [reachability currentReachabilityStatus];
|
|
|
|
|
|
|
|
|
|
NSString *connection = [[NSUserDefaults standardUserDefaults]
|
|
|
|
|
stringForKey:@"offline_download_connection"];
|
|
|
|
|
|
2013-10-08 12:36:43 -07:00
|
|
|
|
// NSLog(@"Reachable via: %d / %d", remoteHostStatus == ReachableViaWWAN, remoteHostStatus == ReachableViaWiFi);
|
2013-09-04 15:56:37 -07:00
|
|
|
|
if ([connection isEqualToString:@"wifi"] && remoteHostStatus != ReachableViaWiFi) {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
2013-07-15 18:25:09 -07:00
|
|
|
|
|
2013-09-05 16:34:39 -07:00
|
|
|
|
- (void)storeUserProfiles:(NSArray *)userProfiles {
|
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
|
|
|
|
|
(unsigned long)NULL), ^(void) {
|
|
|
|
|
[self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
|
|
|
|
for (NSDictionary *user in userProfiles) {
|
|
|
|
|
[db executeUpdate:@"INSERT INTO users "
|
|
|
|
|
"(user_id, username, location, image_url, user_json) VALUES "
|
|
|
|
|
"(?, ?, ?, ?, ?)",
|
|
|
|
|
[user objectForKey:@"user_id"],
|
|
|
|
|
[user objectForKey:@"username"],
|
|
|
|
|
[user objectForKey:@"location"],
|
|
|
|
|
[user objectForKey:@"photo_url"],
|
|
|
|
|
[user JSONRepresentation]
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-17 13:15:10 -07:00
|
|
|
|
- (void)markScrollPosition:(NSInteger)position inStory:(NSDictionary *)story {
|
2015-09-22 13:10:35 -07:00
|
|
|
|
if (position < 0) return;
|
2017-10-26 12:50:04 -07:00
|
|
|
|
__block NSNumber *positionNum = @(position);
|
|
|
|
|
__block NSDictionary *storyDict = story;
|
2015-09-17 13:15:10 -07:00
|
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,
|
2015-09-16 21:34:41 -07:00
|
|
|
|
(unsigned long)NULL), ^(void) {
|
2015-09-17 13:15:10 -07:00
|
|
|
|
[self.database inDatabase:^(FMDatabase *db) {
|
2017-11-07 19:37:28 -08:00
|
|
|
|
NSLog(@"Saving scroll %ld in %@-%@", (long)[positionNum integerValue], [storyDict objectForKey:@"story_hash"], [storyDict objectForKey:@"story_title"]);
|
2015-09-17 13:15:10 -07:00
|
|
|
|
[db executeUpdate:@"INSERT INTO story_scrolls (story_feed_id, story_hash, story_timestamp, scroll) VALUES (?, ?, ?, ?)",
|
2017-10-26 12:50:04 -07:00
|
|
|
|
[storyDict objectForKey:@"story_feed_id"],
|
|
|
|
|
[storyDict objectForKey:@"story_hash"],
|
|
|
|
|
[storyDict objectForKey:@"story_timestamp"],
|
|
|
|
|
positionNum];
|
2015-09-16 21:34:41 -07:00
|
|
|
|
}];
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-06 18:08:55 -07:00
|
|
|
|
- (void)queueReadStories:(NSDictionary *)feedsStories {
|
2013-09-05 16:34:39 -07:00
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
|
|
|
|
|
(unsigned long)NULL), ^(void) {
|
|
|
|
|
[self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
|
|
|
|
for (NSString *feedIdStr in [feedsStories allKeys]) {
|
|
|
|
|
for (NSString *storyHash in [feedsStories objectForKey:feedIdStr]) {
|
|
|
|
|
[db executeUpdate:@"INSERT INTO queued_read_hashes "
|
|
|
|
|
"(story_feed_id, story_hash) VALUES "
|
|
|
|
|
"(?, ?)", feedIdStr, storyHash];
|
|
|
|
|
}
|
2013-08-06 18:08:55 -07:00
|
|
|
|
}
|
2013-09-05 16:34:39 -07:00
|
|
|
|
}];
|
|
|
|
|
});
|
2013-08-06 18:08:55 -07:00
|
|
|
|
self.hasQueuedReadStories = YES;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-03 18:07:39 -07:00
|
|
|
|
- (BOOL)dequeueReadStoryHash:(NSString *)storyHash inFeed:(NSString *)storyFeedId {
|
|
|
|
|
__block BOOL storyQueued = NO;
|
|
|
|
|
|
|
|
|
|
[self.database inDatabase:^(FMDatabase *db) {
|
|
|
|
|
FMResultSet *stories = [db executeQuery:@"SELECT * FROM queued_read_hashes "
|
|
|
|
|
"WHERE story_hash = ? AND story_feed_id = ? LIMIT 1",
|
|
|
|
|
storyHash, storyFeedId];
|
|
|
|
|
while ([stories next]) {
|
|
|
|
|
storyQueued = YES;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
[stories close];
|
|
|
|
|
if (storyQueued) {
|
|
|
|
|
[db executeUpdate:@"DELETE FROM queued_read_hashes "
|
|
|
|
|
"WHERE story_hash = ? AND story_feed_id = ?",
|
|
|
|
|
storyHash, storyFeedId];
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
return storyQueued;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-26 10:48:02 -07:00
|
|
|
|
- (void)flushQueuedReadStories:(BOOL)forceCheck withCallback:(void(^)(void))callback {
|
2016-06-14 22:19:00 -07:00
|
|
|
|
if (self.feedsViewController.isOffline) {
|
|
|
|
|
if (callback) callback();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-15 18:25:09 -07:00
|
|
|
|
if (self.hasQueuedReadStories || forceCheck) {
|
2014-02-03 18:54:50 -08:00
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,
|
2013-07-31 15:23:00 -07:00
|
|
|
|
(unsigned long)NULL), ^(void) {
|
2013-06-21 22:18:54 -07:00
|
|
|
|
[self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
|
|
|
|
NSMutableDictionary *hashes = [NSMutableDictionary dictionary];
|
|
|
|
|
FMResultSet *stories = [db executeQuery:@"SELECT * FROM queued_read_hashes"];
|
|
|
|
|
while ([stories next]) {
|
|
|
|
|
NSString *storyFeedId = [NSString stringWithFormat:@"%@", [stories objectForColumnName:@"story_feed_id"]];
|
|
|
|
|
NSString *storyHash = [stories objectForColumnName:@"story_hash"];
|
|
|
|
|
if (![hashes objectForKey:storyFeedId]) {
|
|
|
|
|
[hashes setObject:[NSMutableArray array] forKey:storyFeedId];
|
|
|
|
|
}
|
|
|
|
|
[[hashes objectForKey:storyFeedId] addObject:storyHash];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([[hashes allKeys] count]) {
|
2013-07-15 18:25:09 -07:00
|
|
|
|
self.hasQueuedReadStories = NO;
|
2013-06-21 22:18:54 -07:00
|
|
|
|
[self syncQueuedReadStories:db withStories:hashes withCallback:callback];
|
|
|
|
|
} else {
|
|
|
|
|
if (callback) callback();
|
|
|
|
|
}
|
2013-10-03 18:07:39 -07:00
|
|
|
|
[stories close];
|
2013-06-21 22:18:54 -07:00
|
|
|
|
}];
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
if (callback) callback();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-26 10:48:02 -07:00
|
|
|
|
- (void)syncQueuedReadStories:(FMDatabase *)db withStories:(NSDictionary *)hashes withCallback:(void(^)(void))callback {
|
2013-06-21 22:18:54 -07:00
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_feed_stories_as_read",
|
2016-01-21 22:11:37 -08:00
|
|
|
|
self.url];
|
2013-06-21 22:18:54 -07:00
|
|
|
|
NSMutableArray *completedHashes = [NSMutableArray array];
|
|
|
|
|
for (NSArray *storyHashes in [hashes allValues]) {
|
|
|
|
|
[completedHashes addObjectsFromArray:storyHashes];
|
|
|
|
|
}
|
2015-11-17 21:10:57 -08:00
|
|
|
|
NSLog(@"Marking %lu queued read stories as read...", (unsigned long)[completedHashes count]);
|
2013-06-21 22:18:54 -07:00
|
|
|
|
NSString *completedHashesStr = [completedHashes componentsJoinedByString:@"\",\""];
|
2017-03-09 20:10:44 -08:00
|
|
|
|
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
|
|
|
|
[params setObject:[hashes JSONRepresentation] forKey:@"feeds_stories"];
|
|
|
|
|
|
2019-08-22 12:03:39 -07:00
|
|
|
|
[self POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
2017-03-09 20:10:44 -08:00
|
|
|
|
NSLog(@"Completed clearing %@ hashes", completedHashesStr);
|
|
|
|
|
[db executeUpdate:[NSString stringWithFormat:@"DELETE FROM queued_read_hashes "
|
|
|
|
|
"WHERE story_hash in (\"%@\")", completedHashesStr]];
|
|
|
|
|
[self pruneQueuedReadHashes];
|
2013-06-21 22:18:54 -07:00
|
|
|
|
if (callback) callback();
|
2017-03-09 20:10:44 -08:00
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
2013-06-21 22:18:54 -07:00
|
|
|
|
NSLog(@"Failed mark read queued.");
|
2013-07-15 18:25:09 -07:00
|
|
|
|
self.hasQueuedReadStories = YES;
|
2015-11-17 21:10:57 -08:00
|
|
|
|
[self pruneQueuedReadHashes];
|
2013-06-21 22:18:54 -07:00
|
|
|
|
if (callback) callback();
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-17 21:10:57 -08:00
|
|
|
|
- (void)pruneQueuedReadHashes {
|
|
|
|
|
[self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
|
|
|
|
NSString *unreadSql = [NSString stringWithFormat:@"SELECT qrh.story_hash FROM queued_read_hashes qrh "
|
|
|
|
|
"INNER JOIN unread_hashes uh ON qrh.story_hash = uh.story_hash"];
|
|
|
|
|
FMResultSet *cursor = [db executeQuery:unreadSql];
|
|
|
|
|
while ([cursor next]) {
|
|
|
|
|
NSLog(@"Story: %@", [cursor objectForColumnName:@"story_hash"]);
|
|
|
|
|
}
|
|
|
|
|
// NSLog(@"Found %lu stories queued to be read but already read", (unsigned long)[[cursor.resultDictionary allKeys] count]);
|
|
|
|
|
NSString *deleteSql = [NSString stringWithFormat:@"DELETE FROM queued_read_hashes "
|
|
|
|
|
"WHERE story_hash not in (%@)", unreadSql];
|
|
|
|
|
[db executeUpdate:deleteSql];
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-13 10:44:00 -07:00
|
|
|
|
|
|
|
|
|
- (void)queueSavedStory:(NSDictionary *)story {
|
|
|
|
|
NSString *storyHash = [story objectForKey:@"story_hash"];
|
|
|
|
|
NSString *storyFeedId = [story objectForKey:@"story_feed_id"];
|
|
|
|
|
|
|
|
|
|
if ([self dequeueSavedStoryHash:storyHash inFeed:storyFeedId]) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
|
|
|
|
|
(unsigned long)NULL), ^(void) {
|
|
|
|
|
[self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
|
|
|
|
BOOL isSaved = [[story objectForKey:@"starred"] boolValue];
|
|
|
|
|
NSArray *userTags = [[story objectForKey:@"user_tags"] copy];
|
|
|
|
|
NSDictionary *info = @{@"user_tags" : userTags}; // A dictionary to enable easily adding future properties (highlights?)
|
|
|
|
|
|
|
|
|
|
[db executeUpdate:@"INSERT INTO queued_saved_hashes "
|
|
|
|
|
"(story_feed_id, story_hash, saved, info_json) VALUES "
|
|
|
|
|
"(?, ?, ?, ?)", storyFeedId, storyHash, @(isSaved), info.JSONRepresentation];
|
|
|
|
|
}];
|
|
|
|
|
});
|
|
|
|
|
self.hasQueuedSavedStories = YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)dequeueSavedStoryHash:(NSString *)storyHash inFeed:(NSString *)storyFeedId {
|
|
|
|
|
__block BOOL storyQueued = NO;
|
|
|
|
|
|
|
|
|
|
[self.database inDatabase:^(FMDatabase *db) {
|
|
|
|
|
FMResultSet *stories = [db executeQuery:@"SELECT * FROM queued_saved_hashes "
|
|
|
|
|
"WHERE story_hash = ? AND story_feed_id = ? LIMIT 1",
|
|
|
|
|
storyHash, storyFeedId];
|
|
|
|
|
while ([stories next]) {
|
|
|
|
|
storyQueued = YES;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
[stories close];
|
|
|
|
|
if (storyQueued) {
|
|
|
|
|
[db executeUpdate:@"DELETE FROM queued_saved_hashes "
|
|
|
|
|
"WHERE story_hash = ? AND story_feed_id = ?",
|
|
|
|
|
storyHash, storyFeedId];
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
return storyQueued;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)flushQueuedSavedStories:(BOOL)forceCheck withCallback:(void(^)(void))callback {
|
|
|
|
|
if (self.feedsViewController.isOffline) {
|
|
|
|
|
if (callback) callback();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self.hasQueuedSavedStories || forceCheck) {
|
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,
|
|
|
|
|
(unsigned long)NULL), ^(void) {
|
|
|
|
|
[self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
|
|
|
|
FMResultSet *stories = [db executeQuery:@"SELECT * FROM queued_saved_hashes"];
|
|
|
|
|
__block NSMutableArray *requests = [NSMutableArray array];
|
|
|
|
|
|
|
|
|
|
while ([stories next]) {
|
|
|
|
|
NSString *storyFeedId = [NSString stringWithFormat:@"%@", [stories objectForColumnName:@"story_feed_id"]];
|
|
|
|
|
NSString *storyHash = [stories objectForColumnName:@"story_hash"];
|
|
|
|
|
BOOL saved = [stories boolForColumn:@"saved"];
|
|
|
|
|
NSDictionary *info = [NSJSONSerialization
|
|
|
|
|
JSONObjectWithData:[[stories stringForColumn:@"info_json"]
|
|
|
|
|
dataUsingEncoding:NSUTF8StringEncoding]
|
|
|
|
|
options:0 error:nil];
|
|
|
|
|
|
|
|
|
|
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
|
|
|
|
NSArray *userTags = info[@"user_tags"];
|
|
|
|
|
|
|
|
|
|
[params setObject:storyHash forKey:@"story_id"];
|
|
|
|
|
[params setObject:storyFeedId forKey:@"feed_id"];
|
|
|
|
|
|
|
|
|
|
if (saved) {
|
|
|
|
|
[params setObject:userTags forKey:@"user_tags"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[requests addObject:params];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[stories close];
|
|
|
|
|
|
|
|
|
|
self.hasQueuedSavedStories = NO;
|
|
|
|
|
[self syncQueuedSavedStoriesRequests:requests withCallback:callback];
|
|
|
|
|
}];
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
if (callback) callback();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)syncQueuedSavedStoriesRequests:(NSMutableArray *)requests withCallback:(void(^)(void))callback {
|
|
|
|
|
NSDictionary *params = requests.firstObject;
|
|
|
|
|
[requests removeObject:params];
|
|
|
|
|
|
|
|
|
|
if (!params) {
|
|
|
|
|
if (callback) callback();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self syncQueuedSavedStoryParams:params withCallback:^{
|
|
|
|
|
[self syncQueuedSavedStoriesRequests:requests withCallback:callback];
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)syncQueuedSavedStoryParams:(NSDictionary *)params withCallback:(void(^)(void))callback {
|
|
|
|
|
BOOL saved = [params objectForKey:@"user_tags"] != nil;
|
|
|
|
|
NSString *endpoint = saved ? @"mark_story_as_starred" : @"mark_story_as_unstarred";
|
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@/reader/%@", self.url, endpoint];
|
|
|
|
|
|
2019-08-22 12:03:39 -07:00
|
|
|
|
[self POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
2018-09-13 10:44:00 -07:00
|
|
|
|
NSString *storyHash = [params objectForKey:@"story_id"];
|
|
|
|
|
NSString *storyFeedId = [params objectForKey:@"feed_id"];
|
|
|
|
|
[self dequeueSavedStoryHash:storyHash inFeed:storyFeedId];
|
|
|
|
|
if (callback) callback();
|
|
|
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
|
|
|
self.hasQueuedSavedStories = YES;
|
|
|
|
|
if (callback) callback();
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-25 20:52:51 -07:00
|
|
|
|
- (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);
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
}
|
2018-09-13 10:44:00 -07:00
|
|
|
|
|
2013-07-15 18:25:09 -07:00
|
|
|
|
- (void)prepareActiveCachedImages:(FMDatabase *)db {
|
|
|
|
|
activeCachedImages = [NSMutableDictionary dictionary];
|
|
|
|
|
NSArray *feedIds;
|
2013-07-18 18:24:38 -07:00
|
|
|
|
int cached = 0;
|
2013-06-16 21:39:38 -07:00
|
|
|
|
|
2014-02-12 20:09:37 -08:00
|
|
|
|
if (storiesCollection.isRiverView) {
|
|
|
|
|
feedIds = storiesCollection.activeFolderFeeds;
|
|
|
|
|
} else if (storiesCollection.activeFeed) {
|
|
|
|
|
feedIds = @[[storiesCollection.activeFeed objectForKey:@"id"]];
|
2013-06-16 14:09:28 -07:00
|
|
|
|
}
|
2013-07-15 18:25:09 -07:00
|
|
|
|
NSString *sql = [NSString stringWithFormat:@"SELECT c.image_url, c.story_hash FROM cached_images c "
|
2013-09-30 16:18:11 -07:00
|
|
|
|
"WHERE c.image_cached = 1 AND c.failed is null AND c.story_feed_id in (\"%@\")",
|
|
|
|
|
[feedIds componentsJoinedByString:@"\",\""]];
|
2013-07-15 18:25:09 -07:00
|
|
|
|
FMResultSet *cursor = [db executeQuery:sql];
|
2013-06-16 14:09:28 -07:00
|
|
|
|
|
2013-07-15 18:25:09 -07:00
|
|
|
|
while ([cursor next]) {
|
|
|
|
|
NSString *storyHash = [cursor objectForColumnName:@"story_hash"];
|
|
|
|
|
NSMutableArray *imageUrls;
|
|
|
|
|
if (![activeCachedImages objectForKey:storyHash]) {
|
|
|
|
|
imageUrls = [NSMutableArray array];
|
|
|
|
|
[activeCachedImages setObject:imageUrls forKey:storyHash];
|
2013-06-21 22:18:54 -07:00
|
|
|
|
} else {
|
2013-07-15 18:25:09 -07:00
|
|
|
|
imageUrls = [activeCachedImages objectForKey:storyHash];
|
2013-07-11 18:36:09 -07:00
|
|
|
|
}
|
2013-07-15 18:25:09 -07:00
|
|
|
|
[imageUrls addObject:[cursor objectForColumnName:@"image_url"]];
|
|
|
|
|
[activeCachedImages setObject:imageUrls forKey:storyHash];
|
2013-07-18 18:24:38 -07:00
|
|
|
|
cached++;
|
2013-06-16 08:15:40 -07:00
|
|
|
|
}
|
2013-06-22 19:34:12 -07:00
|
|
|
|
|
2014-02-11 11:52:32 -08:00
|
|
|
|
// NSLog(@"Pre-cached %d images", cached);
|
2013-06-16 08:15:40 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-10-08 16:00:55 -07:00
|
|
|
|
- (void)cleanImageCache {
|
|
|
|
|
OfflineCleanImages *operationCleanImages = [[OfflineCleanImages alloc] init];
|
|
|
|
|
if (!offlineCleaningQueue) {
|
|
|
|
|
offlineCleaningQueue = [NSOperationQueue new];
|
|
|
|
|
}
|
|
|
|
|
[offlineCleaningQueue addOperation:operationCleanImages];
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-15 18:25:09 -07:00
|
|
|
|
- (void)deleteAllCachedImages {
|
2017-04-04 15:33:08 -07:00
|
|
|
|
NSUInteger memorySize = 1024 * 1024 * 64;
|
|
|
|
|
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:memorySize diskCapacity:memorySize diskPath:nil];
|
|
|
|
|
[NSURLCache setSharedURLCache:sharedCache];
|
2017-04-12 11:19:24 -07:00
|
|
|
|
NSLog(@"cap: %ld", (unsigned long)[[NSURLCache sharedURLCache] diskCapacity]);
|
2017-04-04 15:33:08 -07:00
|
|
|
|
|
|
|
|
|
NSInteger sizeInteger = [[NSURLCache sharedURLCache] currentDiskUsage];
|
|
|
|
|
float sizeInMB = sizeInteger / (1024.0f * 1024.0f);
|
|
|
|
|
NSLog(@"size: %ld, %f", (long)sizeInteger, sizeInMB);
|
|
|
|
|
|
|
|
|
|
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
|
|
|
|
|
|
|
|
|
sizeInteger = [[NSURLCache sharedURLCache] currentDiskUsage];
|
|
|
|
|
sizeInMB = sizeInteger / (1024.0f * 1024.0f);
|
|
|
|
|
NSLog(@"size: %ld, %f", (long)sizeInteger, sizeInMB);
|
|
|
|
|
|
|
|
|
|
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
|
|
|
|
|
|
|
|
|
sizeInteger = [[NSURLCache sharedURLCache] currentDiskUsage];
|
|
|
|
|
sizeInMB = sizeInteger / (1024.0f * 1024.0f);
|
|
|
|
|
NSLog(@"size: %ld, %f", (long)sizeInteger, sizeInMB);
|
|
|
|
|
|
|
|
|
|
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
|
|
|
|
|
|
|
|
|
[[PINCache sharedCache] removeAllObjects];
|
|
|
|
|
[self.cachedStoryImages removeAllObjects];
|
|
|
|
|
|
2013-07-15 18:25:09 -07:00
|
|
|
|
NSFileManager *fileManager = [[NSFileManager alloc] init];
|
|
|
|
|
NSError *error = nil;
|
|
|
|
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
|
|
|
|
NSString *cacheDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"story_images"];
|
|
|
|
|
NSArray *directoryContents = [fileManager contentsOfDirectoryAtPath:cacheDirectory error:&error];
|
|
|
|
|
int removed = 0;
|
2013-06-23 22:19:08 -07:00
|
|
|
|
|
2013-07-15 18:25:09 -07:00
|
|
|
|
if (error == nil) {
|
|
|
|
|
for (NSString *path in directoryContents) {
|
|
|
|
|
NSString *fullPath = [cacheDirectory stringByAppendingPathComponent:path];
|
|
|
|
|
BOOL removeSuccess = [fileManager removeItemAtPath:fullPath error:&error];
|
|
|
|
|
removed++;
|
|
|
|
|
if (!removeSuccess) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2013-06-26 16:22:44 -07:00
|
|
|
|
}
|
2013-06-23 22:19:08 -07:00
|
|
|
|
}
|
2013-06-26 11:38:49 -07:00
|
|
|
|
|
2013-07-15 18:25:09 -07:00
|
|
|
|
NSLog(@"Deleted %d images.", removed);
|
2017-04-04 15:33:08 -07:00
|
|
|
|
|
|
|
|
|
|
2013-06-23 22:19:08 -07:00
|
|
|
|
}
|
2012-10-07 15:47:21 -04:00
|
|
|
|
@end
|
|
|
|
|
|
2013-05-30 18:45:51 -07:00
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Unread Counts
|
|
|
|
|
|
2012-10-07 15:47:21 -04:00
|
|
|
|
|
2012-10-12 15:33:40 -04:00
|
|
|
|
@implementation UnreadCounts
|
2012-10-07 15:47:21 -04:00
|
|
|
|
|
|
|
|
|
@synthesize ps, nt, ng;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (id)init {
|
|
|
|
|
if (self = [super init]) {
|
|
|
|
|
ps = 0;
|
|
|
|
|
nt = 0;
|
|
|
|
|
ng = 0;
|
|
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-12 15:33:40 -04:00
|
|
|
|
- (void)addCounts:(UnreadCounts *)counts {
|
2012-10-07 15:47:21 -04:00
|
|
|
|
ps += counts.ps;
|
|
|
|
|
nt += counts.nt;
|
|
|
|
|
ng += counts.ng;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-09 14:54:49 -07:00
|
|
|
|
- (NSString *)description {
|
|
|
|
|
return [NSString stringWithFormat:@"PS: %d, NT: %d, NG: %d", ps, nt, ng];
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-13 21:54:32 -08:00
|
|
|
|
@end
|