From 383384b6df50e9959303f9c6e70273c00f288f5a Mon Sep 17 00:00:00 2001 From: David Sinclair Date: Sun, 23 Feb 2020 15:21:32 -0800 Subject: [PATCH] #1270 (replacing UIWebView with WKWebView) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - All app-specific uses of UIWebView have been replaced with WKWebView. - Some third-party uses remain. - More testing required. - Note that I’ve bumped the minimum iOS version to iOS 11, since that is needed for WKWebView support. - Committing to a new branch until ready. --- .../Classes/AuthorizeServicesViewController.h | 5 +- .../Classes/AuthorizeServicesViewController.m | 84 ++-- .../AuthorizeServicesViewController.xib | 208 ++-------- clients/ios/Classes/DashboardViewController.h | 4 +- clients/ios/Classes/DashboardViewController.m | 31 -- .../FirstTimeUserAddFriendsViewController.h | 2 +- clients/ios/Classes/NewsBlurAppDelegate.h | 4 + clients/ios/Classes/NewsBlurAppDelegate.m | 29 +- .../ios/Classes/StoryDetailViewController.h | 7 +- .../ios/Classes/StoryDetailViewController.m | 367 +++++++++--------- clients/ios/Classes/StoryPageControl.m | 3 +- clients/ios/Classes/TrainerViewController.h | 7 +- clients/ios/Classes/TrainerViewController.m | 27 +- .../ios/NewsBlur.xcodeproj/project.pbxproj | 12 +- clients/ios/Other Sources/UIWebView+Offsets.h | 16 - clients/ios/Other Sources/UIWebView+Offsets.m | 29 -- .../DashboardViewController.xib | 36 +- .../StoryDetailViewController.xib | 37 +- .../TrainerViewController.xib | 36 +- clients/ios/static/storyDetailView.css | 18 +- 20 files changed, 399 insertions(+), 563 deletions(-) delete mode 100644 clients/ios/Other Sources/UIWebView+Offsets.h delete mode 100644 clients/ios/Other Sources/UIWebView+Offsets.m diff --git a/clients/ios/Classes/AuthorizeServicesViewController.h b/clients/ios/Classes/AuthorizeServicesViewController.h index 8d2b0cdecf..eb379c64a5 100644 --- a/clients/ios/Classes/AuthorizeServicesViewController.h +++ b/clients/ios/Classes/AuthorizeServicesViewController.h @@ -7,10 +7,11 @@ // #import +@import WebKit; @class NewsBlurAppDelegate; -@interface AuthorizeServicesViewController : UIViewController { +@interface AuthorizeServicesViewController : UIViewController { NewsBlurAppDelegate *appDelegate; NSString *url; NSString *type; @@ -22,7 +23,7 @@ @property (nonatomic) NSString *type; @property (nonatomic, readwrite) BOOL fromStory; -@property (weak, nonatomic) IBOutlet UIWebView *webView; +@property (weak, nonatomic) IBOutlet WKWebView *webView; - (void)doCancelButton; - (void)showError:(NSString *)error; diff --git a/clients/ios/Classes/AuthorizeServicesViewController.m b/clients/ios/Classes/AuthorizeServicesViewController.m index 03a5c20089..ab29d19f10 100644 --- a/clients/ios/Classes/AuthorizeServicesViewController.m +++ b/clients/ios/Classes/AuthorizeServicesViewController.m @@ -33,7 +33,6 @@ - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view from its nib. self.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate]; - self.webView.delegate = self; } - (void)viewDidUnload { @@ -53,11 +52,14 @@ - (void)viewWillAppear:(BOOL)animated { } else if ([type isEqualToString:@"twitter"]) { self.navigationItem.title = @"Twitter"; } - NSString *urlAddress = [NSString stringWithFormat:@"%@%@", self.appDelegate.url, url]; - NSURL *fullUrl = [NSURL URLWithString:urlAddress]; - NSURLRequest *requestObj = [NSURLRequest requestWithURL:fullUrl]; - [self.webView loadRequest:requestObj]; - + + [self.appDelegate prepareWebView:self.webView completionHandler:^{ + NSString *urlAddress = [NSString stringWithFormat:@"%@%@", self.appDelegate.url, url]; + NSURL *fullUrl = [NSURL URLWithString:urlAddress]; + NSURLRequest *requestObj = [NSURLRequest requestWithURL:fullUrl]; + [self.webView loadRequest:requestObj]; + }]; + if (self.fromStory && UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle: @"Cancel" @@ -79,51 +81,49 @@ - (void)doCancelButton { [appDelegate.modalNavigationController dismissViewControllerAnimated:YES completion:nil]; } -- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + NSURLRequest *request = navigationAction.request; NSString *URLString = [[request URL] absoluteString]; NSLog(@"URL STRING IS %@", URLString); // Look at the host & path to cope with http:// or https:// schemes if ([request.URL.host isEqualToString:self.appDelegate.host] && [request.URL.path isEqualToString:@"/"]) { - NSString *error = [self.webView stringByEvaluatingJavaScriptFromString:@"NEWSBLUR.error"]; - - if (self.fromStory) { - [appDelegate refreshUserProfile:^{ - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { - [appDelegate.shareNavigationController viewWillAppear:YES]; - [appDelegate.modalNavigationController dismissViewControllerAnimated:YES completion:nil]; - } else { - [self.navigationController popViewControllerAnimated:YES]; - } - }]; - } else { - [self.navigationController popViewControllerAnimated:YES]; - if ([type isEqualToString:@"facebook"]) { - if (error.length) { - [self showError:error]; - } else { - [appDelegate.firstTimeUserAddFriendsViewController selectFacebookButton]; - } - } else if ([type isEqualToString:@"twitter"]) { - if (error.length) { - [self showError:error]; - } else { - [appDelegate.firstTimeUserAddFriendsViewController selectTwitterButton]; + [self.webView evaluateJavaScript:@"NEWSBLUR.error" completionHandler:^(id result, NSError *error) { + NSString *errorString = result; + + if (self.fromStory) { + [appDelegate refreshUserProfile:^{ + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + [appDelegate.shareNavigationController viewWillAppear:YES]; + [appDelegate.modalNavigationController dismissViewControllerAnimated:YES completion:nil]; + } else { + [self.navigationController popViewControllerAnimated:YES]; + } + }]; + } else { + [self.navigationController popViewControllerAnimated:YES]; + if ([type isEqualToString:@"facebook"]) { + if (errorString.length) { + [self showError:errorString]; + } else { + [appDelegate.firstTimeUserAddFriendsViewController selectFacebookButton]; + } + } else if ([type isEqualToString:@"twitter"]) { + if (errorString.length) { + [self showError:errorString]; + } else { + [appDelegate.firstTimeUserAddFriendsViewController selectTwitterButton]; + } } } - } - return NO; + + decisionHandler(WKNavigationActionPolicyCancel); + }]; + + return; } -// // for failed google reader authorization -// if ([URLString hasPrefix:[NSString stringWithFormat:@"%@/import/callback", self.appDelegate.url]]) { -// [self.navigationController popViewControllerAnimated:YES]; -// [appDelegate.firstTimeUserAddSitesViewController importFromGoogleReaderFailed]; -// return NO; -// } - - - return YES; + decisionHandler(WKNavigationActionPolicyAllow); } - (void)showError:(NSString *)error { diff --git a/clients/ios/Classes/AuthorizeServicesViewController.xib b/clients/ios/Classes/AuthorizeServicesViewController.xib index 6daee93501..a11c5434dd 100644 --- a/clients/ios/Classes/AuthorizeServicesViewController.xib +++ b/clients/ios/Classes/AuthorizeServicesViewController.xib @@ -1,169 +1,41 @@ - - - 1296 - 11E53 - 2182 - 1138.47 - 569.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1181 - - - IBProxyObject - IBUIView - IBUIWebView - - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - PluginDependencyRecalculationVersion - - - - - IBFilesOwner - IBCocoaTouchFramework - - - IBFirstResponder - IBCocoaTouchFramework - - - - 274 - - - - 274 - {320, 460} - - - _NS:9 - - 1 - MSAxIDEAA - - IBCocoaTouchFramework - 1 - YES - - - {{0, 20}, {320, 460}} - - - - - 3 - MQA - - 2 - - - - IBCocoaTouchFramework - - - - - - - view - - - - 3 - - - - webView - - - - 5 - - - - - - 0 - - - - - - 1 - - - - - - - - -1 - - - File's Owner - - - -2 - - - - - 4 - - - - - - - AuthorizeServicesViewController - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - UIResponder - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - - - 5 - - - - - AuthorizeServicesViewController - UIViewController - - webView - UIWebView - - - webView - - webView - UIWebView - - - - IBProjectSource - ./Classes/AuthorizeServicesViewController.h - - - - - 0 - IBCocoaTouchFramework - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - YES - 3 - 1181 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/clients/ios/Classes/DashboardViewController.h b/clients/ios/Classes/DashboardViewController.h index 65b3e52b22..de461f9b6b 100644 --- a/clients/ios/Classes/DashboardViewController.h +++ b/clients/ios/Classes/DashboardViewController.h @@ -15,12 +15,11 @@ @class FeedbackModule; @class FeedDetailViewController; -@interface DashboardViewController : UIViewController { +@interface DashboardViewController : UIViewController { NewsBlurAppDelegate *appDelegate; InteractionsModule *interactionsModule; ActivityModule *activitiesModule; FeedDetailViewController *storiesModule; - UIWebView *feedbackWebView; UIToolbar *toolbar; NBDashboardNavigationBar *topToolbar; UISegmentedControl *segmentedButton; @@ -30,7 +29,6 @@ @property (nonatomic) IBOutlet InteractionsModule *interactionsModule; @property (nonatomic) IBOutlet ActivityModule *activitiesModule; @property (nonatomic) IBOutlet FeedDetailViewController *storiesModule; -@property (nonatomic) IBOutlet UIWebView *feedbackWebView; @property (nonatomic) IBOutlet NBDashboardNavigationBar *topToolbar; @property (nonatomic) IBOutlet UIToolbar *toolbar; diff --git a/clients/ios/Classes/DashboardViewController.m b/clients/ios/Classes/DashboardViewController.m index 756866d772..aadd3cfc8b 100644 --- a/clients/ios/Classes/DashboardViewController.m +++ b/clients/ios/Classes/DashboardViewController.m @@ -16,15 +16,12 @@ #import "StoriesCollection.h" #import "UISearchBar+Field.h" -#define FEEDBACK_URL @"http://www.newsblur.com/about" - @implementation DashboardViewController @synthesize appDelegate; @synthesize interactionsModule; @synthesize activitiesModule; @synthesize storiesModule; -@synthesize feedbackWebView; @synthesize topToolbar; @synthesize toolbar; @synthesize segmentedButton; @@ -47,26 +44,13 @@ - (void)viewDidLoad { self.interactionsModule.hidden = NO; } self.activitiesModule.hidden = YES; - self.feedbackWebView.hidden = YES; - self.feedbackWebView.delegate = self; self.segmentedButton.selectedSegmentIndex = 0; - // preload feedback - self.feedbackWebView.scalesPageToFit = YES; - [self.segmentedButton setTitleTextAttributes:@{NSFontAttributeName: [UIFont fontWithName:@"Helvetica-Bold" size:11.0f]} forState:UIControlStateNormal]; - NSString *urlAddress = FEEDBACK_URL; - //Create a URL object. - NSURL *url = [NSURL URLWithString:urlAddress]; - //URL Requst Object - NSURLRequest *requestObj = [NSURLRequest requestWithURL:url]; - //Load the request in the UIWebView. - [self.feedbackWebView loadRequest:requestObj]; - CGRect topToolbarFrame = self.topToolbar.frame; topToolbarFrame.size.height += 20; self.topToolbar.frame = topToolbarFrame; @@ -215,19 +199,4 @@ - (void)refreshActivity { [self.activitiesModule fetchActivitiesDetail:1]; } -# pragma mark -# pragma mark Feedback - -- (BOOL)webView:(UIWebView *)webView -shouldStartLoadWithRequest:(NSURLRequest *)request - navigationType:(UIWebViewNavigationType)navigationType { - NSURL *url = [request URL]; - NSString *urlString = [NSString stringWithFormat:@"%@", url]; - - if ([urlString isEqualToString: FEEDBACK_URL]){ - return YES; - } else { - return NO; - } -} @end diff --git a/clients/ios/Classes/FirstTimeUserAddFriendsViewController.h b/clients/ios/Classes/FirstTimeUserAddFriendsViewController.h index 44dbc5f5e4..b85ba796fa 100644 --- a/clients/ios/Classes/FirstTimeUserAddFriendsViewController.h +++ b/clients/ios/Classes/FirstTimeUserAddFriendsViewController.h @@ -10,7 +10,7 @@ #import "NewsBlurAppDelegate.h" -@interface FirstTimeUserAddFriendsViewController : BaseViewController { +@interface FirstTimeUserAddFriendsViewController : BaseViewController { NewsBlurAppDelegate *appDelegate; } diff --git a/clients/ios/Classes/NewsBlurAppDelegate.h b/clients/ios/Classes/NewsBlurAppDelegate.h index 456f37e9a3..d8023944da 100644 --- a/clients/ios/Classes/NewsBlurAppDelegate.h +++ b/clients/ios/Classes/NewsBlurAppDelegate.h @@ -52,6 +52,7 @@ @class PINCache; @class PremiumManager; @class PremiumViewController; +@class WKWebView; @interface NewsBlurAppDelegate : BaseViewController { @property (nonatomic, readonly) NSString *url; @property (nonatomic, readonly) NSString *host; +@property (nonatomic, readonly) NSHTTPCookie *sessionIdCookie; @property (readwrite) NSString * activeUsername; @property (readwrite) NSString * activeUserProfileId; @property (readwrite) NSString * activeUserProfileName; @@ -354,6 +356,8 @@ SFSafariViewControllerDelegate> { success:(SEL)success failure:(SEL)failure; +- (void)prepareWebView:(WKWebView *)webView completionHandler:(void (^)(void))completion; + - (void)loadFolder:(NSString *)folder feedID:(NSString *)feedIdStr; - (void)reloadFeedsView:(BOOL)showLoader; - (void)setTitle:(NSString *)title; diff --git a/clients/ios/Classes/NewsBlurAppDelegate.m b/clients/ios/Classes/NewsBlurAppDelegate.m index 591f7b7043..3078280c01 100644 --- a/clients/ios/Classes/NewsBlurAppDelegate.m +++ b/clients/ios/Classes/NewsBlurAppDelegate.m @@ -1430,6 +1430,29 @@ - (void)POST:(NSString *)urlString }]; } +- (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(); + } +} + #pragma mark - - (void)loadFolder:(NSString *)folder feedID:(NSString *)feedIdStr { @@ -1887,7 +1910,7 @@ - (void)openDashboardRiverForStory:(NSString *)contentId } - (void)adjustStoryDetailWebView { - // change UIWebView + // change the web view [storyPageControl.currentPage changeWebViewWidth]; [storyPageControl.nextPage changeWebViewWidth]; [storyPageControl.previousPage changeWebViewWidth]; @@ -2114,7 +2137,9 @@ - (void)showInAppBrowser:(NSURL *)url withCustomTitle:(NSString *)customTitle fr } - (void)showSafariViewControllerWithURL:(NSURL *)url useReader:(BOOL)useReader { - self.safariViewController = [[SFSafariViewController alloc] initWithURL:url entersReaderIfAvailable:useReader]; + SFSafariViewControllerConfiguration *config = [SFSafariViewControllerConfiguration new]; + config.entersReaderIfAvailable = useReader; + self.safariViewController = [[SFSafariViewController alloc] initWithURL:url configuration:config]; self.safariViewController.delegate = self; [self.storyPageControl setNavigationBarHidden:NO]; [navigationController presentViewController:self.safariViewController animated:YES completion:nil]; diff --git a/clients/ios/Classes/StoryDetailViewController.h b/clients/ios/Classes/StoryDetailViewController.h index aafe4ee850..72ac8217fe 100644 --- a/clients/ios/Classes/StoryDetailViewController.h +++ b/clients/ios/Classes/StoryDetailViewController.h @@ -9,18 +9,19 @@ #import #import #import "BaseViewController.h" +@import WebKit; @class NewsBlurAppDelegate; @interface StoryDetailViewController : BaseViewController { +UIActionSheetDelegate, WKNavigationDelegate> { NewsBlurAppDelegate *appDelegate; NSString *activeStoryId; NSMutableDictionary *activeStory; UIView *innerView; - UIWebView *webView; + WKWebView *webView; NSInteger pageIndex; BOOL pullingScrollview; BOOL inTextView; @@ -37,7 +38,7 @@ UIActionSheetDelegate> { @property (nonatomic) NSString *activeStoryId; @property (nonatomic, readwrite) NSMutableDictionary *activeStory; @property (nonatomic) IBOutlet UIView *innerView; -@property (nonatomic) IBOutlet UIWebView *webView; +@property (nonatomic) IBOutlet WKWebView *webView; @property (nonatomic) IBOutlet UIView *feedTitleGradient; @property (nonatomic) IBOutlet UIView *noStoryMessage; @property (nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator; diff --git a/clients/ios/Classes/StoryDetailViewController.m b/clients/ios/Classes/StoryDetailViewController.m index 34aaae98f2..e73fe4e8c5 100644 --- a/clients/ios/Classes/StoryDetailViewController.m +++ b/clients/ios/Classes/StoryDetailViewController.m @@ -24,7 +24,6 @@ #import "SBJson4.h" #import "StringHelper.h" #import "StoriesCollection.h" -#import "UIWebView+Offsets.h" #import "UIView+ViewController.h" #import "JNWThrottledBlock.h" @@ -77,7 +76,8 @@ - (void)viewDidLoad { [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil]; - self.webView.scalesPageToFit = YES; + self.webView.navigationDelegate = self; +// self.webView.scalesPageToFit = YES; self.webView.allowsLinkPreview = YES; // self.webView.multipleTouchEnabled = NO; @@ -97,6 +97,7 @@ - (void)viewDidLoad { options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; + [self.appDelegate prepareWebView:self.webView completionHandler:nil]; [self clearWebView]; // UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] @@ -148,19 +149,12 @@ - (void)viewDidLoad { } - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { -// NSLog(@"Gesture: %d - %d", (unsigned long)touch.tapCount, gestureRecognizer.state); +// NSLog(@"%@: taps: %@, state: %@", gestureRecognizer.class, @(touch.tapCount), @(gestureRecognizer.state)); inDoubleTap = (touch.tapCount == 2); CGPoint pt = [self pointForGesture:gestureRecognizer]; if (pt.x == CGPointZero.x && pt.y == CGPointZero.y) return YES; // NSLog(@"Tapped point: %@", NSStringFromCGPoint(pt)); - NSString *tagName = [webView stringByEvaluatingJavaScriptFromString: - [NSString stringWithFormat:@"linkAt(%li, %li, 'tagName');", - (long)pt.x,(long)pt.y]]; - - if ([tagName isEqualToString:@"IMG"] && !inDoubleTap) { - return NO; - } if (inDoubleTap) { self.webView.scrollView.scrollEnabled = NO; @@ -182,31 +176,27 @@ - (void)tap:(UITapGestureRecognizer *)gestureRecognizer { if (gestureRecognizer.state == UIGestureRecognizerStateEnded && gestureRecognizer.numberOfTouches == 1 && appDelegate.storyPageControl.autoscrollAvailable && self.presentedViewController == nil) { CGPoint pt = [self pointForGesture:gestureRecognizer]; if (pt.x == CGPointZero.x && pt.y == CGPointZero.y) return; + if (inDoubleTap) return; // NSLog(@"Tapped point: %@", NSStringFromCGPoint(pt)); - NSString *tagName = [webView stringByEvaluatingJavaScriptFromString: - [NSString stringWithFormat:@"linkAt(%li, %li, 'tagName');", - (long)pt.x,(long)pt.y]]; - - // Special case to handle the story title, Train, Save, and Share buttons. - if ([tagName isEqualToString:@"DIV"]) { - NSString *identifier = [webView stringByEvaluatingJavaScriptFromString: - [NSString stringWithFormat:@"linkAt(%li, %li, 'id');", - (long)pt.x,(long)pt.y]]; - NSString *outerHTML = [webView stringByEvaluatingJavaScriptFromString: - [NSString stringWithFormat:@"linkAt(%li, %li, 'outerHTML');", - (long)pt.x,(long)pt.y]]; + [webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'tagName');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *tagName, NSError *error) { + // Special case to handle the story title, Train, Save, and Share buttons. + if ([tagName isEqualToString:@"DIV"]) { + [webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'id');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *identifier, NSError *error) { + [webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'outerHTML');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *outerHTML, NSError *error) { + if ([identifier isEqualToString:@"NB-story"] || ![outerHTML containsString:@"NB-"]) { + [appDelegate.storyPageControl showAutoscrollBriefly:YES]; + } + }]; + }]; + + return; + } - if (![identifier isEqualToString:@"NB-story"] && [outerHTML containsString:@"NB-"]) { - tagName = @"A"; + // Ignore links, videos, and iframes (e.g. embedded YouTube videos). + if (![@[@"A", @"VIDEO", @"IFRAME"] containsObject:tagName]) { + [appDelegate.storyPageControl showAutoscrollBriefly:YES]; } - } - - // Ignore links, videos, and iframes (e.g. embedded YouTube videos). - if (!inDoubleTap && ![@[@"A", @"VIDEO", @"IFRAME"] containsObject:tagName]) { - [appDelegate.storyPageControl showAutoscrollBriefly:YES]; - } - -// [self tapImage:gestureRecognizer]; + }]; } } @@ -598,8 +588,8 @@ - (void)drawStory:(BOOL)force withOrientation:(UIInterfaceOrientation)orientatio dispatch_async(dispatch_get_main_queue(), ^{ // NSLog(@"Drawing Story: %@", [self.activeStory objectForKey:@"story_title"]); - [self.webView setMediaPlaybackRequiresUserAction:NO]; - self.webView.allowsInlineMediaPlayback = YES; +// [self.webView setMediaPlaybackRequiresUserAction:NO]; +// self.webView.allowsInlineMediaPlayback = YES; [self loadHTMLString:htmlTopAndBottom]; [self.appDelegate.storyPageControl setTextButton:self]; }); @@ -1549,9 +1539,8 @@ - (void)setActiveStoryAtIndex:(NSInteger)activeStoryIndex { } } -- (BOOL)webView:(UIWebView *)webView -shouldStartLoadWithRequest:(NSURLRequest *)request - navigationType:(UIWebViewNavigationType)navigationType { +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + NSURLRequest *request = navigationAction.request; NSURL *url = [request URL]; NSArray *urlComponents = [url pathComponents]; NSString *action = @""; @@ -1612,7 +1601,8 @@ - (BOOL)webView:(UIWebView *)webView if (appDelegate.activeComment == nil) { NSLog(@"PROBLEM! the active comment was not found in friend or public comments"); - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } if ([action isEqualToString:@"reply"]) { @@ -1635,7 +1625,8 @@ - (BOOL)webView:(UIWebView *)webView } else if ([action isEqualToString:@"unlike-comment"]) { [self toggleLikeComment:NO]; } - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } else if ([action isEqualToString:@"togglechanges"]) { if (self.activeStory[@"story_changes"] != nil) { [self.activeStory removeObjectForKey:@"story_changes"]; @@ -1643,16 +1634,19 @@ - (BOOL)webView:(UIWebView *)webView } else { [self fetchStoryChanges]; } - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } else if ([action isEqualToString:@"share"]) { [self openShareDialog]; - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } else if ([action isEqualToString:@"train"] && [urlComponents count] > 5) { [self openTrainingDialog:[[urlComponents objectAtIndex:2] intValue] yCoordinate:[[urlComponents objectAtIndex:3] intValue] width:[[urlComponents objectAtIndex:4] intValue] height:[[urlComponents objectAtIndex:5] intValue]]; - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } else if ([action isEqualToString:@"save"]) { BOOL isSaved = [appDelegate.storiesCollection toggleStorySaved:self.activeStory]; if (isSaved) { @@ -1661,24 +1655,29 @@ - (BOOL)webView:(UIWebView *)webView width:[[urlComponents objectAtIndex:5] intValue] height:[[urlComponents objectAtIndex:6] intValue]]; } - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } else if ([action isEqualToString:@"remove-user-tag"] || [action isEqualToString:@"add-user-tag"]) { [self openUserTagsDialog:[[urlComponents objectAtIndex:3] intValue] yCoordinate:[[urlComponents objectAtIndex:4] intValue] width:[[urlComponents objectAtIndex:5] intValue] height:[[urlComponents objectAtIndex:6] intValue]]; - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } else if ([action isEqualToString:@"classify-author"]) { NSString *author = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]]; [self.appDelegate toggleAuthorClassifier:author feedId:feedId]; - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } else if ([action isEqualToString:@"classify-tag"]) { NSString *tag = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]]; [self.appDelegate toggleTagClassifier:tag feedId:feedId]; - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } else if ([action isEqualToString:@"premium"]) { [self.appDelegate showPremiumDialog]; - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } else if ([action isEqualToString:@"show-profile"] && [urlComponents count] > 6) { appDelegate.activeUserProfileId = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]]; @@ -1696,24 +1695,31 @@ - (BOOL)webView:(UIWebView *)webView yCoordinate:[[urlComponents objectAtIndex:4] intValue] width:[[urlComponents objectAtIndex:5] intValue] height:[[urlComponents objectAtIndex:6] intValue]]; - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } else if ([action isEqualToString:@"notify-loaded"]) { [self webViewNotifyLoaded]; - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } } else if ([url.host hasSuffix:@"itunes.apple.com"]) { [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } - if (navigationType == UIWebViewNavigationTypeLinkClicked) { + if (navigationAction.navigationType == WKNavigationTypeLinkActivated) { // NSLog(@"Link clicked, views: %@ = %@", appDelegate.navigationController.topViewController, appDelegate.masterContainerViewController.childViewControllers); - if (appDelegate.isPresentingActivities) return NO; + if (appDelegate.isPresentingActivities) { + decisionHandler(WKNavigationActionPolicyCancel); + return; + } [appDelegate showOriginalStory:url]; - return NO; + decisionHandler(WKNavigationActionPolicyCancel); + return; } - return YES; + decisionHandler(WKNavigationActionPolicyAllow); } - (void)showOriginalStory:(UIGestureRecognizer *)gesture { @@ -1769,7 +1775,7 @@ - (void)showUserProfile:(NSString *)userId xCoordinate:(int)x yCoordinate:(int)y [appDelegate showUserProfileModal:[NSValue valueWithCGRect:frame]]; } -- (void)webViewDidStartLoad:(UIWebView *)webView { +- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { if (!self.hasStory) // other Web page loads aren't visible return; @@ -1777,10 +1783,10 @@ - (void)webViewDidStartLoad:(UIWebView *)webView { NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults]; [self changeFontSize:[userPreferences stringForKey:@"story_font_size"]]; [self changeLineSpacing:[userPreferences stringForKey:@"story_line_spacing"]]; - [self.webView stringByEvaluatingJavaScriptFromString:@"document.body.style.webkitTouchCallout='none';"]; + [self.webView evaluateJavaScript:@"document.body.style.webkitTouchCallout='none';" completionHandler:nil]; } -- (void)webViewDidFinishLoad:(UIWebView *)webView { +- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { [self loadStory]; } @@ -1824,12 +1830,12 @@ - (void)checkTryFeedStory { [appDelegate.tryFeedCategory isEqualToString:@"comment_reply"]) { NSString *currentUserId = [NSString stringWithFormat:@"%@", [appDelegate.dictSocialProfile objectForKey:@"user_id"]]; NSString *jsFlashString = [[NSString alloc] initWithFormat:@"slideToComment('%@', true, true);", currentUserId]; - [self.webView stringByEvaluatingJavaScriptFromString:jsFlashString]; + [self.webView evaluateJavaScript:jsFlashString completionHandler:nil]; } else if ([appDelegate.tryFeedCategory isEqualToString:@"story_reshare"] || [appDelegate.tryFeedCategory isEqualToString:@"reply_reply"]) { NSString *blurblogUserId = [NSString stringWithFormat:@"%@", [self.activeStory objectForKey:@"social_user_id"]]; NSString *jsFlashString = [[NSString alloc] initWithFormat:@"slideToComment('%@', true, true);", blurblogUserId]; - [self.webView stringByEvaluatingJavaScriptFromString:jsFlashString]; + [self.webView evaluateJavaScript:jsFlashString completionHandler:nil]; } appDelegate.tryFeedCategory = nil; } @@ -1846,7 +1852,7 @@ - (void)setFontStyle:(NSString *)fontStyle { "document.getElementById('NB-font-style').setAttribute('class', '%@')", fontStyle]; - [self.webView stringByEvaluatingJavaScriptFromString:jsString]; + [self.webView evaluateJavaScript:jsString completionHandler:nil]; if (![fontStyle hasPrefix:@"NB-"]) { jsString = [NSString stringWithFormat:@ @@ -1856,28 +1862,28 @@ - (void)setFontStyle:(NSString *)fontStyle { jsString = @"document.getElementById('NB-font-style').setAttribute('style', '')"; } - [self.webView stringByEvaluatingJavaScriptFromString:jsString]; + [self.webView evaluateJavaScript:jsString completionHandler:nil]; } - (void)changeFontSize:(NSString *)fontSize { NSString *jsString = [[NSString alloc] initWithFormat:@"document.getElementById('NB-font-size').setAttribute('class', 'NB-%@')", fontSize]; - [self.webView stringByEvaluatingJavaScriptFromString:jsString]; + [self.webView evaluateJavaScript:jsString completionHandler:nil]; } - (void)changeLineSpacing:(NSString *)lineSpacing { NSString *jsString = [[NSString alloc] initWithFormat:@"document.getElementById('NB-line-spacing').setAttribute('class', 'NB-line-spacing-%@')", lineSpacing]; - [self.webView stringByEvaluatingJavaScriptFromString:jsString]; + [self.webView evaluateJavaScript:jsString completionHandler:nil]; } - (void)updateStoryTheme { NSString *jsString = [NSString stringWithFormat:@"document.getElementById('NB-theme-style').href='storyDetailView%@.css';", [ThemeManager themeManager].themeCSSSuffix]; - [self.webView stringByEvaluatingJavaScriptFromString:jsString]; + [self.webView evaluateJavaScript:jsString completionHandler:nil]; self.webView.backgroundColor = UIColorFromLightDarkRGB(0x707070, 0x404040); } @@ -2049,91 +2055,82 @@ - (void)tapImage:(UIGestureRecognizer *)gestureRecognizer { CGPoint pt = [self pointForGesture:gestureRecognizer]; if (pt.x == CGPointZero.x && pt.y == CGPointZero.y) return; // NSLog(@"Tapped point: %@", NSStringFromCGPoint(pt)); - NSString *tagName = [webView stringByEvaluatingJavaScriptFromString: - [NSString stringWithFormat:@"linkAt(%li, %li, 'tagName');", - (long)pt.x,(long)pt.y]]; - - if ([tagName isEqualToString:@"IMG"]) { - [self showImageMenu:pt]; - [gestureRecognizer setEnabled:NO]; - [gestureRecognizer setEnabled:YES]; - } + [webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'tagName');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *tagName, NSError *error) { + if ([tagName isEqualToString:@"IMG"]) { + [self showImageMenu:pt]; + [gestureRecognizer setEnabled:NO]; + [gestureRecognizer setEnabled:YES]; + } + }]; } - (void)tapAndHold:(NSNotification*)notification { CGPoint pt = [self pointForEvent:notification]; if (pt.x == CGPointZero.x && pt.y == CGPointZero.y) return; - NSString *tagName = [webView stringByEvaluatingJavaScriptFromString: - [NSString stringWithFormat:@"linkAt(%li, %li, 'tagName');", - (long)pt.x,(long)pt.y]]; - - if ([tagName isEqualToString:@"IMG"]) { - [self showImageMenu:pt]; - } - - if ([tagName isEqualToString:@"A"]) { -// [self showLinkContextMenu:pt]; - } + [webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'tagName');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *tagName, NSError *error) { + if ([tagName isEqualToString:@"IMG"]) { + [self showImageMenu:pt]; + } + + if ([tagName isEqualToString:@"A"]) { + // [self showLinkContextMenu:pt]; + } + }]; } - (void)showImageMenu:(CGPoint)pt { - NSString *title = [webView stringByEvaluatingJavaScriptFromString: - [NSString stringWithFormat:@"linkAt(%li, %li, 'title');", - (long)pt.x,(long)pt.y]]; - NSString *alt = [webView stringByEvaluatingJavaScriptFromString: - [NSString stringWithFormat:@"linkAt(%li, %li, 'alt');", - (long)pt.x,(long)pt.y]]; - NSString *src = [webView stringByEvaluatingJavaScriptFromString: - [NSString stringWithFormat:@"linkAt(%li, %li, 'src');", - (long)pt.x,(long)pt.y]]; - title = title.length ? title : alt; - activeLongPressUrl = [NSURL URLWithString:src]; - - UIAlertController *alert = [UIAlertController alertControllerWithTitle:title.length ? title : nil - message:nil - preferredStyle:UIAlertControllerStyleActionSheet]; - [alert addAction:[UIAlertAction actionWithTitle:@"View and zoom" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - [appDelegate showOriginalStory:activeLongPressUrl]; - }]]; - [alert addAction:[UIAlertAction actionWithTitle:@"Copy image" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - [self fetchImage:activeLongPressUrl copy:YES save:NO]; - }]]; - [alert addAction:[UIAlertAction actionWithTitle:@"Save to camera roll" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - [self fetchImage:activeLongPressUrl copy:NO save:YES]; - }]]; - [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { - - }]]; - - [alert setModalPresentationStyle:UIModalPresentationPopover]; - - UIPopoverPresentationController *popover = [alert popoverPresentationController]; - popover.sourceRect = CGRectMake(pt.x, pt.y, 1, 1); - popover.sourceView = appDelegate.storyPageControl.view; - [self presentViewController:alert animated:YES completion:nil]; + [webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'title');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *title, NSError *error) { + [webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'alt');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *alt, NSError *error) { + [webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'src');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *src, NSError * error) { + NSString *alertTitle = title.length ? title : alt; + activeLongPressUrl = [NSURL URLWithString:src]; + + UIAlertController *alert = [UIAlertController alertControllerWithTitle:alertTitle.length ? alertTitle : nil + message:nil + preferredStyle:UIAlertControllerStyleActionSheet]; + [alert addAction:[UIAlertAction actionWithTitle:@"View and zoom" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [appDelegate showOriginalStory:activeLongPressUrl]; + }]]; + [alert addAction:[UIAlertAction actionWithTitle:@"Copy image" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [self fetchImage:activeLongPressUrl copy:YES save:NO]; + }]]; + [alert addAction:[UIAlertAction actionWithTitle:@"Save to camera roll" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [self fetchImage:activeLongPressUrl copy:NO save:YES]; + }]]; + [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { + + }]]; + + [alert setModalPresentationStyle:UIModalPresentationPopover]; + + UIPopoverPresentationController *popover = [alert popoverPresentationController]; + popover.sourceRect = CGRectMake(pt.x, pt.y, 1, 1); + popover.sourceView = appDelegate.storyPageControl.view; + [self presentViewController:alert animated:YES completion:nil]; + }]; + }]; + }]; } - (void)showLinkContextMenu:(CGPoint)pt { - NSString *href = [webView stringByEvaluatingJavaScriptFromString: - [NSString stringWithFormat:@"linkAt(%li, %li, 'href');", - (long)pt.x,(long)pt.y]]; - NSString *title = [webView stringByEvaluatingJavaScriptFromString: - [NSString stringWithFormat:@"linkAt(%li, %li, 'innerText');", - (long)pt.x,(long)pt.y]]; - NSURL *url = [NSURL URLWithString:href]; - - if (!href || ![href length]) return; - - NSValue *ptValue = [NSValue valueWithCGPoint:pt]; - [appDelegate showSendTo:appDelegate.storyPageControl - sender:ptValue - withUrl:url - authorName:nil - text:nil - title:title - feedTitle:nil - images:nil]; + [webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'href');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *href, NSError *error) { + [webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'innerText');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *title, NSError *error) { + NSURL *url = [NSURL URLWithString:href]; + + if (!href || ![href length]) return; + + NSValue *ptValue = [NSValue valueWithCGPoint:pt]; + [appDelegate showSendTo:appDelegate.storyPageControl + sender:ptValue + withUrl:url + authorName:nil + text:nil + title:title + feedTitle:nil + images:nil]; + }]; + }]; } - (CGPoint)pointForEvent:(NSNotification*)notification { @@ -2150,12 +2147,14 @@ - (CGPoint)pointForEvent:(NSNotification*)notification { // convert point from view to HTML coordinate system // CGPoint offset = [self.webView scrollOffset]; - CGSize viewSize = [self.webView frame].size; - CGSize windowSize = [self.webView windowSize]; - CGFloat f = windowSize.width / viewSize.width; - pt.x = pt.x * f;// + offset.x; - pt.y = pt.y * f;// + offset.y; + // The viewSize seems to always match the windowSize, so don't need this. If there is some case where it is needed, will need to cache it, as the old windowSize method would be async with WKWebView +// CGSize viewSize = [self.webView frame].size; +// CGSize windowSize = [self.webView windowSize]; +// +// CGFloat f = windowSize.width / viewSize.width; +// pt.x = pt.x * f;// + offset.x; +// pt.y = pt.y * f;// + offset.y; return pt; } @@ -2168,12 +2167,13 @@ - (CGPoint)pointForGesture:(UIGestureRecognizer *)gestureRecognizer { // convert point from view to HTML coordinate system // CGPoint offset = [self.webView scrollOffset]; - CGSize viewSize = [self.webView frame].size; - CGSize windowSize = [self.webView windowSize]; - - CGFloat f = windowSize.width / viewSize.width; - pt.x = pt.x * f;// + offset.x; - pt.y = pt.y * f;// + offset.y; + // The viewSize seems to always match the windowSize, so don't need this. If there is some case where it is needed, will need to cache it, as the old windowSize method would be async with WKWebView +// CGSize viewSize = [self.webView frame].size; +// CGSize windowSize = [self.webView windowSize]; +// +// CGFloat f = windowSize.width / viewSize.width; +// pt.x = pt.x * f;// + offset.x; +// pt.y = pt.y * f;// + offset.y; return pt; } @@ -2259,34 +2259,33 @@ - (void)refreshComments:(NSString *)replyId { commentString, shareBarString]; NSString *shareType = appDelegate.activeShareType; - [self.webView stringByEvaluatingJavaScriptFromString:jsString]; - - [self.webView stringByEvaluatingJavaScriptFromString:@"attachFastClick();"]; - - // HACK to make the scroll event happen after the replace innerHTML event above happens. - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, .15 * NSEC_PER_SEC), - dispatch_get_main_queue(), ^{ - if (!replyId) { - NSString *currentUserId = [NSString stringWithFormat:@"%@", - [appDelegate.dictSocialProfile objectForKey:@"user_id"]]; - NSString *jsFlashString = [[NSString alloc] - initWithFormat:@"slideToComment('%@', true);", currentUserId]; - [self.webView stringByEvaluatingJavaScriptFromString:jsFlashString]; - } else if ([replyId isEqualToString:@"like"]) { - - } else { - NSString *jsFlashString = [[NSString alloc] - initWithFormat:@"slideToComment('%@', true);", replyId]; - [self.webView stringByEvaluatingJavaScriptFromString:jsFlashString]; - } - }); - - -// // adding in a simulated delay -// sleep(1); - - [self flashCheckmarkHud:shareType]; - [self refreshSideoptions]; + [self.webView evaluateJavaScript:jsString completionHandler:^(id result, NSError * _Nullable error) { + [self.webView evaluateJavaScript:@"attachFastClick();" completionHandler:^(id result, NSError * _Nullable error) { + // HACK to make the scroll event happen after the replace innerHTML event above happens. + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, .15 * NSEC_PER_SEC), + dispatch_get_main_queue(), ^{ + if (!replyId) { + NSString *currentUserId = [NSString stringWithFormat:@"%@", + [appDelegate.dictSocialProfile objectForKey:@"user_id"]]; + NSString *jsFlashString = [[NSString alloc] + initWithFormat:@"slideToComment('%@', true);", currentUserId]; + [self.webView evaluateJavaScript:jsFlashString completionHandler:^(id result, NSError * _Nullable error) { + [self flashCheckmarkHud:shareType]; + [self refreshSideoptions]; + }]; + } else if ([replyId isEqualToString:@"like"]) { + + } else { + NSString *jsFlashString = [[NSString alloc] + initWithFormat:@"slideToComment('%@', true);", replyId]; + [self.webView evaluateJavaScript:jsFlashString completionHandler:^(id result, NSError * _Nullable error) { + [self flashCheckmarkHud:shareType]; + [self refreshSideoptions]; + }]; + } + }); + }]; + }]; } - (void)flashCheckmarkHud:(NSString *)messageType { @@ -2331,7 +2330,7 @@ - (void)flashCheckmarkHud:(NSString *)messageType { - (void)scrolltoComment { NSString *currentUserId = [NSString stringWithFormat:@"%@", [appDelegate.dictSocialProfile objectForKey:@"user_id"]]; NSString *jsFlashString = [[NSString alloc] initWithFormat:@"slideToComment('%@', true);", currentUserId]; - [self.webView stringByEvaluatingJavaScriptFromString:jsFlashString]; + [self.webView evaluateJavaScript:jsFlashString completionHandler:nil]; } - (void)tryScrollingDown:(BOOL)down { @@ -2428,7 +2427,7 @@ - (void)changeWebViewWidth { alternateViewClass, riverClass, (long)contentWidth]; - [self.webView stringByEvaluatingJavaScriptFromString:jsString]; + [self.webView evaluateJavaScript:jsString completionHandler:nil]; // self.webView.hidden = NO; } @@ -2439,9 +2438,9 @@ - (void)refreshHeader { NSString *jsString = [NSString stringWithFormat:@"document.getElementById('NB-header-container').innerHTML = '%@';", headerString]; - [self.webView stringByEvaluatingJavaScriptFromString:jsString]; - - [self.webView stringByEvaluatingJavaScriptFromString:@"attachFastClick();"]; + [self.webView evaluateJavaScript:jsString completionHandler:^(id result, NSError *error) { + [self.webView evaluateJavaScript:@"attachFastClick();" completionHandler:nil]; + }]; } - (void)refreshSideoptions { @@ -2450,9 +2449,9 @@ - (void)refreshSideoptions { NSString *jsString = [NSString stringWithFormat:@"document.getElementById('NB-sideoptions-container').innerHTML = '%@';", sideoptionsString]; - [self.webView stringByEvaluatingJavaScriptFromString:jsString]; - - [self.webView stringByEvaluatingJavaScriptFromString:@"attachFastClick();"]; + [self.webView evaluateJavaScript:jsString completionHandler:^(id result, NSError *error) { + [self.webView evaluateJavaScript:@"attachFastClick();" completionHandler:nil]; + }]; } #pragma mark - diff --git a/clients/ios/Classes/StoryPageControl.m b/clients/ios/Classes/StoryPageControl.m index 5fa0721ecc..1a7a902f0c 100644 --- a/clients/ios/Classes/StoryPageControl.m +++ b/clients/ios/Classes/StoryPageControl.m @@ -23,6 +23,7 @@ #import "THCircularProgressView.h" #import "FMDatabase.h" #import "StoriesCollection.h" +@import WebKit; @interface StoryPageControl () @@ -1631,7 +1632,7 @@ - (void)setAutoscrollActive:(BOOL)active { } - (void)autoscroll:(NSTimer *)timer { - UIWebView *webView = self.currentPage.webView; + WKWebView *webView = self.currentPage.webView; CGFloat position = webView.scrollView.contentOffset.y + 0.5; CGFloat maximum = webView.scrollView.contentSize.height - webView.frame.size.height; diff --git a/clients/ios/Classes/TrainerViewController.h b/clients/ios/Classes/TrainerViewController.h index 9e420d025c..93a85fb2ac 100644 --- a/clients/ios/Classes/TrainerViewController.h +++ b/clients/ios/Classes/TrainerViewController.h @@ -9,9 +9,9 @@ #import #import "BaseViewController.h" #import "NewsBlurAppDelegate.h" +@import WebKit; - -@interface TrainerWebView : UIWebView {} +@interface TrainerWebView : WKWebView {} - (void)focusTitle:(id)sender; - (void)hideTitle:(id)sender; @@ -19,8 +19,7 @@ @end -@interface TrainerViewController : BaseViewController - { +@interface TrainerViewController : BaseViewController { NewsBlurAppDelegate *appDelegate; IBOutlet UIBarButtonItem * closeButton; diff --git a/clients/ios/Classes/TrainerViewController.m b/clients/ios/Classes/TrainerViewController.m index 62cf1fe1ac..1bdeadbe1f 100644 --- a/clients/ios/Classes/TrainerViewController.m +++ b/clients/ios/Classes/TrainerViewController.m @@ -44,6 +44,7 @@ - (void)viewDidLoad action:@selector(doCloseDialog:)]; self.navigationItem.rightBarButtonItem = done; + self.webView.navigationDelegate = self; [self hideGradientBackground:webView]; [self.webView.scrollView setDelaysContentTouches:YES]; [self.webView.scrollView setDecelerationRate:UIScrollViewDecelerationRateNormal]; @@ -140,9 +141,9 @@ - (void)refresh { NSString *jsString = [NSString stringWithFormat:@"document.getElementById('NB-trainer').innerHTML = '%@';", headerString]; - [self.webView stringByEvaluatingJavaScriptFromString:jsString]; + [self.webView evaluateJavaScript:jsString completionHandler:nil]; - [self.webView stringByEvaluatingJavaScriptFromString:@"attachFastClick({skipEvent: true});"]; + [self.webView evaluateJavaScript:@"attachFastClick({skipEvent: true});" completionHandler:nil]; } - (void)viewDidDisappear:(BOOL)animated { @@ -545,16 +546,13 @@ - (IBAction)doCloseDialog:(id)sender { - (void)changeTitle:(id)sender score:(int)score { NSString *feedId = [NSString stringWithFormat:@"%@", [appDelegate.activeStory objectForKey:@"story_feed_id"]]; - NSString *selectedTitle = [self.webView - stringByEvaluatingJavaScriptFromString:@"window.getSelection().toString()"]; - - [self.appDelegate toggleTitleClassifier:selectedTitle feedId:feedId score:score]; + [self.webView evaluateJavaScript:@"window.getSelection().toString()" completionHandler:^(id result, NSError *error) { + [self.appDelegate toggleTitleClassifier:result feedId:feedId score:score]; + }]; } - -- (BOOL)webView:(UIWebView *)webView -shouldStartLoadWithRequest:(NSURLRequest *)request - navigationType:(UIWebViewNavigationType)navigationType { +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + NSURLRequest *request = navigationAction.request; NSURL *url = [request URL]; NSArray *urlComponents = [url pathComponents]; NSString *action = @""; @@ -570,23 +568,22 @@ - (BOOL)webView:(UIWebView *)webView if ([action isEqualToString:@"classify-author"]) { NSString *author = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]]; [self.appDelegate toggleAuthorClassifier:author feedId:feedId]; - return NO; } else if ([action isEqualToString:@"classify-tag"]) { NSString *tag = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]]; [self.appDelegate toggleTagClassifier:tag feedId:feedId]; - return NO; } else if ([action isEqualToString:@"classify-title"]) { NSString *title = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]]; [self.appDelegate toggleTitleClassifier:title feedId:feedId score:0]; - return NO; } else if ([action isEqualToString:@"classify-feed"]) { NSString *feedId = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]]; [self.appDelegate toggleFeedClassifier:feedId]; - return NO; } + + decisionHandler(WKNavigationActionPolicyCancel); + return; } - return YES; + decisionHandler(WKNavigationActionPolicyAllow); } @end diff --git a/clients/ios/NewsBlur.xcodeproj/project.pbxproj b/clients/ios/NewsBlur.xcodeproj/project.pbxproj index 8f79813c82..0de017e469 100755 --- a/clients/ios/NewsBlur.xcodeproj/project.pbxproj +++ b/clients/ios/NewsBlur.xcodeproj/project.pbxproj @@ -486,7 +486,6 @@ FFA045B519CA49D700618DC4 /* SSWAnimator.m in Sources */ = {isa = PBXBuildFile; fileRef = FFA045B119CA49D700618DC4 /* SSWAnimator.m */; }; FFA045B619CA49D700618DC4 /* SSWDirectionalPanGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = FFA045B319CA49D700618DC4 /* SSWDirectionalPanGestureRecognizer.m */; }; FFA0483E19CA5B8400618DC4 /* EventWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = FFA0483D19CA5B8400618DC4 /* EventWindow.m */; }; - FFA0484119CA5F5B00618DC4 /* UIWebView+Offsets.m in Sources */ = {isa = PBXBuildFile; fileRef = FFA0484019CA5F5B00618DC4 /* UIWebView+Offsets.m */; }; FFA0484419CA73B700618DC4 /* UIView+ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FFA0484319CA73B700618DC4 /* UIView+ViewController.m */; }; FFAD4971144A386100BA6919 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FFAD4970144A386100BA6919 /* libz.dylib */; }; FFAD89C218AC45A100D68567 /* StoriesCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = FFAD89C118AC45A100D68567 /* StoriesCollection.m */; }; @@ -1314,8 +1313,6 @@ FFA045B319CA49D700618DC4 /* SSWDirectionalPanGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSWDirectionalPanGestureRecognizer.m; sourceTree = ""; }; FFA0483C19CA5B8400618DC4 /* EventWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventWindow.h; sourceTree = ""; }; FFA0483D19CA5B8400618DC4 /* EventWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EventWindow.m; sourceTree = ""; }; - FFA0483F19CA5F5B00618DC4 /* UIWebView+Offsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIWebView+Offsets.h"; path = "Other Sources/UIWebView+Offsets.h"; sourceTree = ""; }; - FFA0484019CA5F5B00618DC4 /* UIWebView+Offsets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIWebView+Offsets.m"; path = "Other Sources/UIWebView+Offsets.m"; sourceTree = ""; }; FFA0484219CA73B700618DC4 /* UIView+ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+ViewController.h"; path = "Other Sources/UIView+ViewController.h"; sourceTree = ""; }; FFA0484319CA73B700618DC4 /* UIView+ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+ViewController.m"; path = "Other Sources/UIView+ViewController.m"; sourceTree = ""; }; FFAD4970144A386100BA6919 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; @@ -1722,8 +1719,6 @@ 17C4954A1C129863004805A7 /* UISearchBar+Field.m */, FF1F13D618AAC97900FDA816 /* UIImage+Resize.h */, FF1F13D718AAC97900FDA816 /* UIImage+Resize.m */, - FFA0483F19CA5F5B00618DC4 /* UIWebView+Offsets.h */, - FFA0484019CA5F5B00618DC4 /* UIWebView+Offsets.m */, FFFF683B19D628000081904A /* NBURLCache.h */, FFFF683C19D628000081904A /* NBURLCache.m */, FFD6604A1BACA45D006E4B8D /* THCircularProgressView.h */, @@ -3533,7 +3528,6 @@ FF83FF051FB52565008DAC0F /* PremiumViewController.m in Sources */, FF03B00A19F987E00063002A /* NJKWebViewProgressView.m in Sources */, FF11045F176950F900502C29 /* NBLoadingCell.m in Sources */, - FFA0484119CA5F5B00618DC4 /* UIWebView+Offsets.m in Sources */, FF34FD701E9D93CB0062F8ED /* IASKTextViewCell.m in Sources */, FFAD89C218AC45A100D68567 /* StoriesCollection.m in Sources */, FF34FD6F1E9D93CB0062F8ED /* IASKTextView.m in Sources */, @@ -3837,7 +3831,6 @@ GCC_VERSION = ""; HEADER_SEARCH_PATHS = ""; INFOPLIST_FILE = "NewsBlur-iPhone-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", @@ -3886,7 +3879,6 @@ GCC_VERSION = ""; HEADER_SEARCH_PATHS = ""; INFOPLIST_FILE = "NewsBlur-iPhone-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", @@ -3944,7 +3936,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/**"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 9.1.1; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "-ObjC"; @@ -3992,7 +3984,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/**"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 9.1.1; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; diff --git a/clients/ios/Other Sources/UIWebView+Offsets.h b/clients/ios/Other Sources/UIWebView+Offsets.h deleted file mode 100644 index d74ad1885c..0000000000 --- a/clients/ios/Other Sources/UIWebView+Offsets.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// UIWebView+Offsets.h -// NewsBlur -// -// Created by Samuel Clay on 9/17/14. -// Copyright (c) 2014 NewsBlur. All rights reserved. -// - -#import - -@interface UIWebView (Offsets) - -- (CGSize)windowSize; -- (CGPoint)scrollOffset; - -@end diff --git a/clients/ios/Other Sources/UIWebView+Offsets.m b/clients/ios/Other Sources/UIWebView+Offsets.m deleted file mode 100644 index 695d48c8bd..0000000000 --- a/clients/ios/Other Sources/UIWebView+Offsets.m +++ /dev/null @@ -1,29 +0,0 @@ -// -// UIWebView+Offsets.m -// NewsBlur -// -// Created by Samuel Clay on 9/17/14. -// Copyright (c) 2014 NewsBlur. All rights reserved. -// - -#import "UIWebView+Offsets.h" - -@implementation UIWebView (Offsets) - -- (CGSize)windowSize -{ - CGSize size; - size.width = [[self stringByEvaluatingJavaScriptFromString:@"window.innerWidth"] integerValue]; - size.height = [[self stringByEvaluatingJavaScriptFromString:@"window.innerHeight"] integerValue]; - return size; -} - -- (CGPoint)scrollOffset -{ - CGPoint pt; - pt.x = [[self stringByEvaluatingJavaScriptFromString:@"window.pageXOffset"] integerValue]; - pt.y = [[self stringByEvaluatingJavaScriptFromString:@"window.pageYOffset"] integerValue]; - return pt; -} - -@end diff --git a/clients/ios/Resources-iPhone/DashboardViewController.xib b/clients/ios/Resources-iPhone/DashboardViewController.xib index b73a14f9a3..57df02f224 100644 --- a/clients/ios/Resources-iPhone/DashboardViewController.xib +++ b/clients/ios/Resources-iPhone/DashboardViewController.xib @@ -1,14 +1,15 @@ - - + + + - - + + + - @@ -20,29 +21,24 @@ - + - + - + - + - - - - - - + - - + + @@ -61,8 +57,10 @@ - + + + - \ No newline at end of file + diff --git a/clients/ios/Resources-iPhone/StoryDetailViewController.xib b/clients/ios/Resources-iPhone/StoryDetailViewController.xib index 77459febc1..843923763f 100644 --- a/clients/ios/Resources-iPhone/StoryDetailViewController.xib +++ b/clients/ios/Resources-iPhone/StoryDetailViewController.xib @@ -1,10 +1,9 @@ - - + + + - - - + @@ -12,7 +11,7 @@ - + @@ -21,27 +20,29 @@ - + - + - - - - - + + + + + + - + - + - - - + + + + diff --git a/clients/ios/Resources-iPhone/TrainerViewController.xib b/clients/ios/Resources-iPhone/TrainerViewController.xib index f0ce42a972..2dcced6c96 100644 --- a/clients/ios/Resources-iPhone/TrainerViewController.xib +++ b/clients/ios/Resources-iPhone/TrainerViewController.xib @@ -1,13 +1,16 @@ - - + + + - + + + - + @@ -15,19 +18,26 @@ - + - - - - - - - + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/clients/ios/static/storyDetailView.css b/clients/ios/static/storyDetailView.css index 9569ee6676..fe85a8f528 100644 --- a/clients/ios/static/storyDetailView.css +++ b/clients/ios/static/storyDetailView.css @@ -52,6 +52,21 @@ font-family: "Helvetica"; } +@font-face { + font-family: "ChronicleSSm-Book"; + src: url("ChronicleSSm-Book.otf"); +} + +@font-face { + font-family: "GothamNarrow-Book"; + src: url("GothamNarrow-Book.otf"); +} + +@font-face { + font-family: "WhitneySSm-Book"; + src: url("WhitneySSm-Book-Bas.otf"); +} + /* Line spacing */ .NB-story { line-height: 1.5em; @@ -210,7 +225,6 @@ body { -webkit-text-size-adjust: 100%; /* Prevent font scaling in landscape while allowing user zoom */ font-size: 14px; line-height: 120%; - overflow: hidden; font-family: Helvetica; } @@ -537,7 +551,7 @@ div + p { max-width: 100%; display: none; -webkit-touch-callout: none; - -webkit-user-select: none; /* Disable selection/copy in UIWebView */ + -webkit-user-select: none; /* Disable selection/copy in the web view */ } /* Get iOS 11 Smart Invert to leave images and videos alone. */